移植到 GCC 14:C 语言问题
GCC 14 更严格地遵循现代 C 标准,把长期以来的警告——例如隐式 `int`、未声明函数以及某些指针不匹配——变成错误,迫使大型代码库和诸如 autoconf 之类的构建系统进行更新。评论者在更强的安全性、更清晰的诊断以及长期拖延的 C99/C23 特性(如变长数组类型)带来的收益,和破坏依赖前 ANSI 或编译器特定行为的遗留与“可移植” C 代码的痛苦之间进行权衡。讨论还深入到更底层的语言设计问题,包括整数和指针类型语义、调用约定,以及为什么某些 C 特性虽然几十年前就已标准化,却发展得如此缓慢。
GCC 14 更严格的 C 默认行为
- GCC 14 将若干长期存在的 C 警告变成错误(例如隐式函数声明、隐式
int、某些不兼容的指针用法)。 - 很多评论者认为这早就该这样:这些构造在几十年前就几乎已经过时,在现代代码中几乎从不属于有意为之。
- 也有人指出这会导致破坏:旧代码(包括 IOCCC 条目和历史 GNU 用户态程序)如果不把错误降级就会失败。GCC 仍然允许用标志恢复旧行为。
变长数组(VLA)与 C23
- 讨论涉及 C99(VLA 为必需)、C11(VLA 为可选)以及 C23(VLA 类型 再次成为必需;带自动存储期的 VLA 对象 仍然可选)。
- 澄清了编译器必须支持可变修饰类型的语法和语义(例如对它们使用
sizeof和offsetof),但仍可省略基于栈的 VLA。 - 对实现复杂性的争论:运行时相关的布局和
typedef会引入额外的语义机制,并不只是“alloca的语法糖”。 - MSVC 仍然不支持 VLA,并且跳过了整个 C99,这限制了 VLA 的可移植使用。
发行版中的移植工作
- Fedora 和其他项目已经进行了大规模清理,以适应更严格的默认行为,受影响最严重的是 autoconf 生成的测试:这些测试常使用可疑的 C 代码并掩盖警告。
- 主要风险是“静默功能丢失”:构建成功,但 configure 检查失败,从而禁用某些功能。这促使人们采用比较配置差异的方法。
- Clang/Xcode 更严格的默认行为,以及 Gentoo 将修复上游化,帮助在收紧 GCC 行为上形成共识。
遗留 C 构造与编译模型
- ANSI C 之前允许调用未声明的函数,并将其视为返回
int;在sizeof(int) == sizeof(pointer)的情况下,这“差不多够用”。 - 讨论了一次通过编译器如何处理尚未声明的函数,以及额外检查是否意味着“第二遍扫描”。
- 有人建议语言应正式支持更灵活的前向引用,并引用了新的编译器基础设施,认为那样“就是能工作”。
整数类型、字长与风格
- 关于为什么在 64 位系统上
int仍保持 32 位,出现了长篇讨论:向后兼容、标准整数阶梯有限、以及现有代码的假设。 - 对于是否应把
int设为 64 位,双方都有论点:可移植性 vs. 内存/缓存占用 vs. 清晰性。 - 区分“标准”整数类型(
char/short/int/long/long long)与“扩展”整数类型;指出stdint.h大多只是对前者做typedef,并不是真正引入了新宽度。 - 不同做法并存:有人偏好固定宽度类型(
int32_t、int64_t),也有人偏好int+long long,并因审美和重载问题(在 C++ 中)避免使用编号类型。
函数指针、void* 与指针表示
- 有人提出疑问:为什么一个接受
const char*的函数不能用于期望类型为int (*)(const void*, const void*)的函数指针场景。 - 标准禁止任意函数指针转换;C 也没有通用的函数指针类型。
- C11 保证
void*和字符指针具有相同的表示和对齐;其他指针类型未必如此,函数指针也可能不同于对象指针。 - 历史上以及一些奇特架构中,指针大小和寻址模式不统一,被引用为标准保持严格的原因之一。
向后兼容性 vs. 小众用例
- 有人批评这种“反 C89”的方向,认为移除隐式
int会损害创造性的多语言混写用途(例如 C/JavaScript 双语代码),却没有明显收益。 - 线程中的多数观点支持更严格的默认行为,以防止那些微妙但会出错的代码,同时为遗留程序或竞赛风格程序保留逃生通道。