C23:略微更好的 C

C23 是 C 语言标准的最新修订版,引入了一组渐进式特性,例如 `auto` 类型推导、`typeof`、关键字形式的 `static_assert`、更简洁的初始化语法,以及移除三字符组和 K&R 风格函数定义等旧式构造。评论者对这些变化是否真正改进了 C 看法不一:一些人欢迎更好的泛型宏、受检算术,以及与现有编译器扩展和 C++ 更接近;另一些人则认为这些只是表面修补,没有解决字符串、模块和更安全的资源管理等长期问题。讨论还将 C23 放在更广阔的背景中:许多新的系统项目更偏向 C++ 或 Rust,而 Zig、D、Nim、Ada 等替代语言则在竞争提供更安全或更易用的 C 互操作能力。

C23 中的 Auto、typeof 和“泛型”

  • 重新使用 auto 被一些人视为一个奇怪的选择;许多人认为在严肃的 C 代码中它应当算作一种风格上的“绝对不要”,不同于 C++。
  • 也有人认为它在 C 里确实很有用:
    • 避免冗长的类型名以及 struct/enum 前缀。
    • 有助于通用宏(例如使用 auto tmp = a;SWAP(a,b))以及定宽整数类型。
    • 将现有的 __auto_type 扩展标准化。
  • 还新增了 typeof;有人指出现在可以写 const typeof(var1) tmp = var1;
  • 关于 _Generic 是否真的算“泛型”的争论:有些人认为它只是基于类型的重载/分派,而不是参数化数据结构;也有人说它仍然达到了“generic”的标准。

C23 的其他语言调整

  • static_assert 成为关键字;C11 已经有 _Static_assert 和一个宏,但现在无需 <assert.h> 也能使用。
  • func() 现在明确等价于 func(void),并且允许未命名参数。
  • 使用 {} 进行结构体初始化(零初始化)现在合法(struct foo x = {};),与 C++ 保持一致。
  • 移除三字符组和老式 K&R 风格函数声明被认为是在清理历史遗留怪癖。
  • 有人希望 C23 能内建 defer/RAII 功能;相关提案存在,但目前仍处于“探索性”阶段。

工具链与编译器支持

  • 引用 cppreference 的讨论显示,GCC 13 在 C23 支持方面相对较广;Clang 在若干特性上落后。
  • 给出的解释包括:LLVM 的模块化设计和企业分支减缓了前端演进;主要贡献者更多聚焦于后端或其他语言。
  • 据称 Pelles C 几乎支持所有 C23 特性,包括 #embed
  • MSVC 历来对 C 的支持不够重视,但近来支持了 C17(除部分可选的 C99 特性);未来 C23 的时间表尚不明确。

C 与 C++ 以及语言哲学

  • 有人认为 C++ 本来就是“更好的 C”,而 C23 主要是在把 C++ 的特性回移到 C 里。
  • 也有人回应说,C 和 C++ 现在服务于不同领域;C++ 的复杂性、元编程和隐式代码生成,被认为不适合某些嵌入式/底层场景。
  • 类比说法不一:从“C++ 是 C 的 Cybertruck,自行车之于卡车”这种说法(被批评不准确),到“C 是手工工具,C++ 额外增加了电动工具”。
  • 关于嵌入式中的异常和 RAII 的争论:有人说 RAII 直接而有帮助;也有人担心对象生命周期不够显式。

头文件、模块与 API 表面

  • 有人希望 C 能有真正的模块/import,而不是头文件,以避免重复并加快构建,或许可以让 import 与传统 #include 并存。
  • 也有人喜欢 C 将头文件与实现分离的方式,因为这样能清晰地暴露公共 API;并指出这与语言是否拥有模块系统是两个不同的问题。
  • 还有讨论认为,更强的模块机制可能会破坏部分 C/C++ 互操作性,这在政治上较为敏感。

内存安全替代语言与 C FFI

  • 一段很长的子线程讨论了“体积小、稳定、内存安全、拥有优秀 C FFI 且不会带来显著性能损失的语言”。
  • 提到的候选包括 Zig、D、Rust、Nim、V、Vala、LuaJIT、Ada、各种 Scheme 和 Lisp、Swift、Julia 以及 Go。
  • 强调的权衡包括:
    • 真正的内存安全通常意味着 GC 或 Rust 风格的借用;二者都会使 FFI 和/或简洁性变得复杂。
    • 容易实现、零开销的 C FFI 往往会削弱强安全保证(因为错误可以从 C 中渗入)。
    • 几位评论者认为,这份精确愿望清单可能无法被完全满足;最多只能通过不同的折中方案去近似实现。