C语言标准分析报告

C 语言标准横纵分析报告:从贝尔实验室到 C23 的五十三年

研究时间:2026-06-23 | 所属领域:编程语言 / 系统软件 | 研究对象类型:概念(技术标准)
AIGC


一、一句话定义

C 语言是一种以内存透明性为核心契约的工业级系统编程语言——它给予程序员对硬件的绝对控制权,同时要求程序员承担内存管理的全部责任。自 1972 年诞生以来,它通过 ISO/IEC 9899 系列标准持续演进,至今仍是操作系统内核、嵌入式固件和高性能基础设施的底层语言。


二、纵向分析:从贝尔实验室到 C23 的五十三年

版本(年份) 标准号 / 名称 关键人物 / 组织 备注
K&R C(1978) 《The C Programming Language》第一版 Brian Kernighan, Dennis Ritchie, 贝尔实验室 事实标准,确立 C 语言基本语法和风格
C89(1989) ANSI X3.159-1989 ANSI X3J11 委员会 首个正式工业标准,引入函数原型、const/volatile
C90(1990) ISO/IEC 9899:1990 ISO/IEC JTC1/SC22/WG14 国际标准,与 C89 本质相同
C95(1995) ISO/IEC 9899:1990 AMD 1 WG14 修正案,主要改进国际化支持(宽字符、<wchar.h>
C99(1999) ISO/IEC 9899:1999 WG14(与 Fortran 竞争驱动) 最激进的修订:restrict、VLA、long long、复数、inline
C11(2011) ISO/IEC 9899:2011 WG14(多核时代驱动) 内存模型、原子操作、<threads.h>、废除 gets()
C17(2018) ISO/IEC 9899:2018 WG14 纯缺陷修复,无新特性,当前最稳定的工业基准
C23(2024) ISO/IEC 9899:2024 WG14(Robert Seacord 等) 彻底废除 K&R 声明,引入 nullptrconstexprtypeof#embed
C29(预计 2029) C2y WG14 已采纳 defer、命名循环、_Countofif 声明
timeline title C 语言标准演进时间轴 1978 : K&R C : 事实标准确立 1989 : C89 (ANSI C) : 函数原型、const/volatile 1990 : C90 (ISO C) : 国际标准采纳 1999 : C99 : restrict、VLA、long long 2011 : C11 : 内存模型、原子操作 2018 : C17 : 缺陷修复版本 2024 : C23 : nullptr、constexpr、#embed 2029 : C29(预计) : defer、命名循环

2.1 起源:一台 8KB 内存的机器和一个"顺手"的语言

1969 年,AT&T 贝尔实验室的 Ken Thompson 面对一台 DEC PDP-7 小型机。这台机器只有 8KB 内存、没有操作系统,但 Thompson 想在上面运行一个叫"太空旅行"(Space Travel)的游戏。他写了一个简陋的操作系统——后来成为 Unix——但需要一门语言来写系统程序。

当时的选择只有汇编。Thompson 从剑桥大学的 BCPL(Martin Richards 设计)中提取了一门更简化的语言,命名为 B 语言。B 是无类型的——唯一的数据类型是机器字。它还是解释型的,性能很差。但 Thompson 做了一件事后来被证明是天才之举:他从 PDP-7 的自动递增内存地址单元中得到启发,给 B 语言加上了 ++-- 操作符。

转折点出现在 1970 年,贝尔实验室买了 PDP-11。这台 16 位机器能寻址 8 位字节,与 B 语言的字导向设计产生了根本冲突。Dennis Ritchie 接手了 Thompson 的工作,开始把 B 扩展为"New B"(NB)。他加入了类型声明(intchar),把解释改为编译,改进了指针和数组的处理。

到 1972 年,这门语言已经足够不同,需要一个新的名字。Ritchie 选了 C——"BCPL 的第二个字母"。

这里有一个被反复讲述但很少有人细想的细节:PDP-11 只有 24KB 的总 RAM,其中 12KB 要给操作系统内核。留给用户程序的空间极小。这个约束直接塑造了 C 的设计哲学——没有冗余,所有特性都必须有直接的硬件映射。你写的每一行 C 代码,程序员都能大致预测它会被编译成几条汇编指令。这种"可预测性"是 C 的核心契约。

2.2 1973:首个用高级语言写成的操作系统

1973 年是个关键节点。Thompson 和 Ritchie 用 C 重写了 Unix 内核,使得 Unix 成为历史上第一个用高级语言实现的主要操作系统。

这个决策的后果远超两人的预期。Unix 因此获得了前所未有的可移植性——1977-78 年,团队将 Unix 从 PDP-11 移植到了 Interdata 8/32(一台 32 位主机)。为此,他们给 C 添加了 long 类型(最初用于 32 位文件偏移),定义了 short 用于 16 位值。Steve Johnson 的 Portable C Compiler(PCC)仅有约 20% 的代码是机器相关的。

C 不再只是 Unix 的实现工具,它本身成为了一种可移植系统编程语言。这个时期编译器极其原始:两遍设计、表格驱动指令选择、没有预处理器。但这不重要——C 在正确的时间站在了正确的位置:Unix 的流行绑定了 C 的命运。

2.3 K&R C:事实标准的确立

1978 年 2 月 22 日,Brian Kernighan 和 Dennis Ritchie 合著的《The C Programming Language》由 Prentice Hall 出版。这本昵称"K&R"或"白皮书"的小书,封面上是一只程序员用 C 语言写的"Hello, World!"——这个例子后来被无数语言教材照搬。

K&R C 成为了 C 语言长达十余年的事实标准。书中的代码风格——现在称为"K&R 风格"或"1TBS"——至今被 Linux 内核采用。但也正是在这本书中,C 留下了一些后来被证明很危险的特性:

  • int func(); 并不代表"没有参数",而是代表"可以接收任意数量和类型的参数"。编译不会报错,但运行时栈帧会被破坏。
  • 隐式声明:调用一个未声明的函数,编译器假设它返回 int

这些设计在 70 年代看起来没什么问题——写 Unix 的那几个人互相知道对方在干什么。但当 C 在 80 年代走向普及,这些"宽松"变成了"漏洞"。

2.4 C89/C90:契约精神的确立

1983 年,ANSI 成立了 X3J11 委员会,任务是给 C 制定正式的美国国家标准。这标志着 C 从"一个实验室的内部工具"迈向了"工业级国际标准"。

委员会的讨论持续了六年。1985 年出草案 C85,1986 年修订成 C86,1988 年再修成 C88——1989 年终获批准,即 ANSI X3.159-1989(C89)。次年 ISO 采纳为 ISO/IEC 9899:1990(C90)。C89 和 C90 本质相同,只有格式和组织的细微差异。

C89/C90 最核心的贡献是函数原型(Function Prototypes)。它要求显式声明参数列表——void func(void) 表示"真的没有参数"而不是"参数未知"。这使编译器能够拦截参数个数和类型不匹配的调用。另外引入的关键字 constvolatile,前者标记只读数据,后者强迫编译器对内存映射 I/O 寄存器生成真实的读写指令——这对驱动开发是决定性的。

标准库也首次被正式定义:<stdio.h><stdlib.h><string.h> 等 15 个头文件组成了一套最小化的标准库。C89 标准库的规模之小,至今仍是现代语言中最克制的——相比之下,即使是 Python 的标准库也数倍于它。

但 C89 保留了 K&R 的一个核心缺陷:隐式函数声明。调用一个没有原型的函数仍然是合法的——编译器默认返回 int。这个"兼容性"的代价在后面几十年里反复制造了安全漏洞。

2.5 C99 (1999):与 Fortran 的十年战争

C99 距离 C89 整整十年,是 C 标准史上最激进的修订版本。它的驱动力来自一个意想不到的方向:科学计算

90 年代末,Fortran 90/95 在超级计算机和高性能计算(HPC)领域占据统治地位。Fortran 的杀手锏是多维数组操作和默认无指针别名——编译器可以放心地对数组操作进行激进优化。而 C 的指针可能指向任何内存位置,编译器必须假设写入一个指针可能修改所有其他指针指向的内容,这严重阻碍了优化。

C99 的回应是一系列针对 Fortran 的功能:

  • restrict 关键字:程序员向编译器承诺,被 restrict 修饰的指针是访问所指向对象的唯一途径。这解除了编译器的"别名枷锁",允许生成与 Fortran 相当的高速代码。上面 vector_add 的例子很好地展示了这一点——没有 restrict,每次循环都要重新从内存加载 factor;有了 restrictfactor 驻留在寄存器中。
  • 复数类型_Complex / _Imaginary):Fortran IV 早在 1966 年就有的特性。
  • long long:64 位整数,当时科学计算需求已经普遍超过 32 位范围。
  • 变长数组(VLA):允许 int arr[n] 在运行时确定数组大小——类似 Fortran 的可调数组维度。
  • <tgmath.h>:类型泛型数学函数,对标 Fortran 的内建数学函数。

除了 HPC 方向的改进,C99 还从 C++ 借了一些顺手的东西:// 注释、变量声明与代码混合、for (int i=0;...) 循环作用域、inline 函数。其他创新包括:指定初始化器(struct foo f = { .field = value })、复合字面量、可变参数宏、__func__ 预定义标识符、柔性数组成员。

C99 的悲剧在于它太超前了。当时最大的 C 编译器之一 MSVC 对 C99 的支持长期严重不足——MSVC 从未实现 VLA,对 restrict 的支持也不完整。这意味着跨平台 C 代码实际上不能依赖 C99 特性。许多团队索性停留在 C89。这种碎片化在嵌入式领域尤其严重——一些芯片厂商提供的编译器在 C99 发布十年后才开始支持。

另一个争议是 VLA 的安全性。VLA 在栈上分配内存,而栈空间是非常有限的。一旦运行时的 n 值超出预期,程序立即发生栈溢出崩溃,且无法像 malloc 一样通过 NULL 返回值来检查。这让安全敏感项目对 VLA 敬而远之。C11 中将 VLA 降级为可选特性即是这个争议的直接结果。

2.6 C11 (2011):多核时代的阵痛

如果说 C99 是为"更快"而战,C11 就是为"并发安全"而战。

2005 年,Herb Sutter 发表了那篇著名的《The Free Lunch Is Over》,宣告单核 CPU 性能线性增长的终结。摩尔定律转向了多核——但 C 语言标准没有任何多线程的形式化内存模型。编译器可以静默地重排跨线程的内存操作,硬件(ARM、PowerPC 的弱内存模型 vs x86 的强模型)也可以在并发代码中重排加载和存储。结果是用 C 写的多线程程序的行为在不同架构上完全不同——这在 2000 年代后期已经成为一个严重的现实问题。

C11 的回应是引入了完整的内存模型<stdatomic.h> 原子操作库。这一套设计借鉴了 C++11(两者同年发布),定义了六个内存顺序:

内存顺序 含义
memory_order_relaxed 仅保证原子性,不保证任何顺序
memory_order_consume 数据依赖顺序(实践中大多编译器将其提升为 acquire)
memory_order_acquire 之后的读写不能被重排到 acquire 之前
memory_order_release 之前的读写不能被重排到 release 之后
memory_order_acq_rel acquire + release 的组合
memory_order_seq_cst 全局顺序一致性(默认)

核心保证是 DRF(Data-Race Freedom):如果程序无数据竞争,则表现为顺序一致性。这对于在 C 中安全地实现无锁数据结构至关重要。

C11 的其他新特性:

  • _Generic:编译期类型泛型表达式,让标准库可以写出类型安全的宏。
  • _Static_assert:编译期断言。
  • _Alignas / _Alignof:对齐控制。
  • 废除 gets():因为无法防止缓冲区溢出——这是 C 标准史上少有的"承认错误"的瞬间。
  • 可选特性机制:C11 首次允许某些特性不被实现——多线程、原子操作、复数、VLA 都变成了可选,通过 __STDC_NO_THREADS__ 等宏检测。这有点像给编译器厂商递了一根"我不实现"的杆子——嵌入式编译器厂商欢呼雀跃。

C11 的争议焦点之一是 Annex K(边界检查接口)。它定义了更安全的函数版本(如 fopen_sscanf_s),但设计存在严重问题,连主要推动者微软也没有按标准实现。Annex K 始终没有获得广泛采纳,成为 C 标准史上一个不太光彩的注脚。

2.7 C17/C18 (2018):没有新功能的版本,恰恰是最好的消息

C17 是 ISO/IEC 9899:2018——开发在 2017 年完成,标准编号为 2018 年。整个标准没有增加任何新语言特性。

这是纯粹的缺陷修复版本:整合了多个技术勘误表,修正了 C11 积累的小缺陷,提供了清晰的文本说明。对一个正在大规模工业应用的语言来说,一个稳定的、无新特性的基准版本本身就是一件好事。C17 至今仍是很多编译器(如 GCC 未指定 -std= 标志时)的默认标准。

一句话评价 C17:C11 本来应该长成的样子。

2.8 C23 (2024):五十年后的"另一次出生"

C23(ISO/IEC 9899:2024)于 2024 年 10 月 31 日正式发布,开发历时八年(工作名称 "C2x")。它是 C 语言自 C99 以来变化最大的一次修订。

清理历史包袱是 C23 最鲜明的主题。

彻底删除的:

  • K&R 旧式函数声明:不指定参数列表的函数定义方式(int func(); 不指定参数)被完全废除。所有函数必须使用函数原型。这个从 1978 年延续到 2024 年的"特性"——实际上是一个安全漏洞——终于被合上了。
  • 三字符组(Trigraphs):一种为缺少某些字符的键盘设计的替代方案(如 ??= 代表 #),在 Unicode 时代沦为 bug 制造机(?? 序列在字符串中意外触发转换)。
  • 非二进制补码整数表示:C 标准只承认二进制补码,移除了反码和符号-数值表示。实际上没有现代硬件使用另外两种表示,但它们的理论存在性在过去给编译器优化制造了大量不必要的"未定义行为"路障。

新增的关键语言特性:

  1. nullptrnullptr_t:在 C23 之前,NULL 通常被宏定义为 0(void*)0。这导致它在宏展开或泛型匹配时具有二义性(例如被识别为整型 int)。nullptr 具有专属的指针类型,解决了强类型校验下的安全漏洞。

  2. constexpr:标量对象的编译时常量。不是 C++ 的 constexpr 函数,只是"在编译期就确定值的对象"。但这已经足够消除一些"常量折叠"的歧义。

  3. typeof / typeof_unqual:类型提取操作符。类似 GCC 长期支持的扩展,现在正式进入标准。

  4. true / false 关键字:它们曾经是 <stdbool.h> 中定义的宏,现在是语言内建的关键字。

  5. #embed:编译期包含二进制资源。这解决了嵌入式开发中的一个痛点——以前你只能要么用 xxd -i 把二进制文件转成数组,要么用汇编 .incbin 指令。#embed 让标准 C 可以直接在编译期嵌入资源。

  6. [[]] 属性语法(借自 C++):[[maybe_unused]][[deprecated]][[fallthrough]][[likely]][[unlikely]][[noreturn]]。以前这些功能分散在编译器专用的 __attribute__ 扩展中。

标准库的补充:

  • <stdbit.h>:标准化的位操作函数集(全部以 stdc_ 前缀),包括 popcount、leading zeros、byte swap 等。
  • memset_explicit():编译器无法优化掉的敏感数据擦除函数——在密码学场景中至关重要的是,编译器之前会"聪明地"优化掉对已释放内存的 memset
  • strdup()strndup():字符串复制函数——虽然 POSIX 早有,但标准 C 一直没有。
  • 检查整数算术宏:ckd_add()ckd_sub()ckd_mul() ——带溢出检测的加减乘。

C++ 兼容性:C23 大量采纳了 C++ 特性以缩小差距——属性语法、constexprauto 类型推断、{} 空初始化器、枚举固定底层类型。这反映了 WG14 和 WG21 近年来的密切协调。

2.9 未来:C29/C2y 在路上

C23 的下一版工作名称是 "C2y",随着发布年份确定,现在被称为 C29(预计 2029 年底)。

已采纳的提案包括:if 声明(类似 C++17 的带初始化器的 if)、命名循环(带标签的 break/continue)、_Countof 数组元素计数操作符、0o/0O 八进制前缀(替代易混淆的前导零表示)、defer 语句(作用域退出时的清理块——这是长期呼声最高的 C 语言缺失特性)。

WG14 委员会 2026 年的会议安排包括与 WG21(C++ 标准委员会)的联合会议,这预示着 C 和 C++ 的协同进化将继续加速。


三、横向分析:C 语言在 2020 年代的竞争生态

3.1 C 语言目前的江湖地位

先说数字。TIOBE 编程语言指数在 2015-2025 这十年间,C 语言始终徘徊在第 1 到第 4 名之间。2020 年 5 月甚至重返过第一,但 Python 自 2022 年起永久占据榜首。2025 年 3 月 C 创下了历史新低第 4 位(9.84%),10 月又反弹至第 2 位(9.29%),主要推动力是 C23 的发布和 AI 推理引擎对底层代码的需求。

GitHub Octoverse 2025 的数据显示,C 的贡献者数量同比增长了 20.9%——这很有趣,因为"C 要被取代了"的说法喊了十年,但 C 的开发者社群反而在增长。当然,从绝对数量看,C 的 GitHub 份额约 3.6%,远落后于 JavaScript、Python 和 TypeScript。

C 的主要势力范围(2025 年):

领域 地位 原因
操作系统内核 绝对统治 Linux、Windows、macOS 核心用 C 编写
嵌入式/固件 超过 60% 市场份额 最小运行时、硬件级控制
数据库引擎 核心层 SQLite、MySQL、PostgreSQL 内核
编译器/运行时 实现语言 GCC、Clang、CPython、V8
实时系统 不可或缺 确定性无 GC 暂停
安全关键系统 高度固化 MISRA-C、DO-178C 认证流程

3.2 主要竞品逐一拆解

C vs C++:一对渐行渐远的兄弟

C 和 C++ 的关系在 2024 年发生了一个微妙的变化:哲学分歧已经大到双方都认为对方"做错了"

C++ 从 C with Classes(1979)发展而来,其核心信念是"用抽象换取安全"——RAII、智能指针、模板、异常处理。现代 C++(C++11 以后)在表达能力上远超 C,但代价是编译器复杂性爆炸式增长。

C 的立场在 2024 年的一个邮件列表讨论中被清晰表述:C 的设计基于"对象的底层表示",而 C++ 基于"对象的含义"。在 C 中,int 就是 32 位(或 16 位)的位模式;在 C++ 中,int 是一个整数,语义上不同于 boolenum。C 认为表达式的语义可以从其底层表示推导出来,C++ 则认为编译器应该通过类型系统来强制执行语义规则。

一个具体的例子:C 中 int_Bool 在底层都是位模式,类型转换是 reinterpret_cast 风格的重解释;C++ 中 intbool 之间是 static_cast 风格的显式转换。这在实践中意味着,编写"既是有效 C 又是有效 C++"的共享头文件越来越困难。

但 C++ 有一个 C 作为子集的历史包袱。"C 是 C++ 的子集"在 90 年代曾是事实,但 C99 开始分歧加大。到了 C23,C 有了 typeofnullptrconstexpr[[]] 属性——C++ 认为"你抄我",C 认为"你发明了这些,但我需要自己的版本"。

C vs Rust:最直接、最激烈的对决

Rust 是 C 在系统编程领域遇到的最认真的挑战者。两者的对比远不止"所有权和借用检查器"的技术层面——它背后是整个信息安全行业对内存安全漏洞的零容忍运动

数字触目惊心。CISA 和五眼联盟在 2024 年 6 月联合发布了一份关于关键开源项目中内存安全的报告:分析了 172 个关键开源项目(包括 Linux、Chromium、MySQL、TensorFlow、JDK 等),52% 的项目包含用内存不安全语言(C、C++)编写的代码,Linux 内核约 95% 的代码是内存不安全的。白宫 ONCD 的报告引用研究表明,多达 70% 的 CVE 与内存安全问题相关

这个背景下,Rust 进入 Linux 内核是一个标志性事件。2025 年,Rust 在内核中"不再被视为实验性"——所有顶级维护者一致同意,零反对。Greg Kroah-Hartman 的评价既直接又辛辣:"绝大多数内核错误都是 C 中愚蠢的小问题,这些问题在 Rust 中完全不存在。"——缓冲区溢出、错误路径清理、遗漏错误值检查、释放后使用。

但 Rust 的渗透速度被一个数字清晰界定:截至 2025 年 4 月,Linux 内核代码库中有约 3400 万行 C,约 2.5 万行 Rust。Rust 的比例不到万分之一。

性能方面,NSF 的研究给出了一个微妙的结论:Rust 在微基准测试中平均比 C 慢 1.77 倍,主要归因于运行时检查;禁用这些检查后,性能与 C 无异。换句话说,Rust 的安全性不是免费的——但你可以在安全模式调试,在性能模式发布

Linus Torvalds 本人对 C 标准的批评则更为尖锐(2025 年 2 月邮件列表)。他指责 C 标准在"内存访问是否算可观察行为"的问题上搞砸了——标准允许编译器将单次内存读取变成多次读取,这直接引发了 TOCTOU(检查时间 vs 使用时间)漏洞。Torvalds 的结论是"标准弄错了这一点"。

Linux 内核社区的内部分歧在于:H. Peter Anvin 等维护者认为 Rust 代码的语法差异使 C 维护者更难审查,而且重构 3500 万行现有 C 代码不切实际。当前的共识是一种分层策略:新开发的驱动程序用 Rust,核心内核和已稳定的子系统保持 C

C vs Zig:更务实的"更好的 C"

Zig 的定位异常清晰:不是要取代 C,而是提供一个"现代人会把 C 写成什么样"的答案

Zig 的关键差异:

  • 无运行时、无 GC:与 C 同样轻量,二进制体积相当。
  • 编译期计算(comptime):在编译期执行代码,替代 C 的预处理器宏。
  • 内置构建系统和包管理器:取代 make/cmake。
  • 错误联合类型!void 语法,替代 C 的 errno 和返回码。
  • 交叉编译是一等公民:为几乎所有目标平台提供 libc。

2024 年的一项系统编程开发者调查中,Zig 的用户满意度平均 7.9/10(中位数 8.5),C 只有 3.7/10(中位数 2.5)。差距巨大——但应该考虑到填写调查的 Zig 用户是自选的早期采用者,而 C 用户则包括大量"不得不写 C"的嵌入式工程师。

Zig 的核心痛点是还没有 1.0 版本(截至 2025 年仍是 0.15.x)。但已有生产级用户:Bun(JavaScript 运行时)、TigerBeetle(金融数据库)、Uber 的部分基础设施。

社区共识是:Zig 解决的是与 Rust 不同的问题——Zig 是"更好的 C"(显式、简单、可控),Rust 是"更好的 C++"(类型系统驱动、借用检查器)。

C vs Go:网络服务 vs 硬实时

Go 和 C 的竞争关系被高估了。两者的重叠区域其实很窄——主要在嵌入式 Linux 的上层应用。

Go 的优势在于 goroutine(轻量级协程)、快速编译、内置垃圾回收。但在硬实时场景,GC 的不可预测暂停是致命缺陷。C 在微控制器(<1MB 闪存)、RTOS、设备驱动、裸机固件领域没有挑战。

Stack Overflow 在 2022 年的一篇对比文章中给出的结论是:Go 赢了开发者体验,C 赢了硬件控制。在实际项目中,常见的是混合方案——底层驱动用 C,上层应用用 Go。

C vs Ada:被遗忘的竞品

在安全关键系统领域,C 实际上有一个技术上更优越的竞争对手——Ada。

Ada 从设计之初就是为嵌入式安全系统而生:自动数组边界检查、强类型系统(数组索引可以映射到领域类型,不仅仅从 0 开始)、有限且受控的指针、内置范围检查、内置任务处理(Ravenscar 剖面)。Ada 的 SPARK 子集甚至支持形式化验证。

Rogers 在 SIGAda 2011 上的论文直接评价:"C 和 C++ 的安全关键子集(MISRA-C)试图达到与完整 Ada 语言类似的安全水平,这种尝试通常失败。"

但 Ada 从来没有获得过像 C 那样的生态系统。 它在波音 777 和空客 A380 的电传飞控系统中使用,在 F-35 JSF 中也有部署——但在民用嵌入式领域,C 配合 MISRA-C 占据了绝大部分市场。原因不是技术,是生态:C 的开发者数量是 Ada 的数百倍,工具链更成熟,第三方库更丰富。

3.3 编译器生态:谁在支持什么

GCC 15+ 已默认使用 C23,对所有列出的 C23 特性提供完整支持。Clang 18+ 也基本支持 C23。目前对 C 标准支持最薄弱的是 MSVC——它默认仍是 C89(加微软扩展),C11/C17 需要显式用 /std:c11/std:c17 开关才能启用,对 C23 则根本没有宣布计划。

ARM 编译器的情况与 MSVC 类似——ARMCLANG 6.x 对 C11 的支持标记为"社区级"(未经过 ARM 本身测试),C17 支持直到 v24+ 才完整。IAR Embedded Workbench 的情况最好,自 v8.10.1 起完整支持 C11/C18,C23 特性也在逐步加入。

嵌入式领域大量停留在 C89/C99 的原因:

  1. 产品生命周期长达 20 年以上——一旦冻结了工具链,升级编译器需要重新认证。
  2. 芯片厂商锁定——很多 MCU 厂商发布的是特定版本的 GCC 分支,后续版本可能不再支持该芯片。
  3. VLA 在 DSP/小型 MCU 上不可行——栈空间极小。
  4. MISRA-C 历史上基于 C89——迁移到新标准意味着重写编码规范的所有规则。
  5. "能用就不要动"——嵌入式文化中最强大的力量。

3.4 安全之争:C 最大的存在危机

2024 年,美国白宫网络安全办公室(ONCD)发布报告,引用研究数据表明多达 70% 的 CVE 与内存安全问题相关,呼吁行业采用内存安全语言(C#、Rust、Go、Java、Ruby、Swift)。NSA 在 2022 年就表达了相同的立场,并在 2024 年重申。

欧洲的网络安全弹性法案(CRA)也在推动制造商为联网产品采用内存安全语言。

这是 C 语言五十多年历史上最严重的外部威胁。以前是语言之间的竞争,现在是政府政策层面的压力。

C 阵营的回应包括:

  • Checked C(微软研究院):给 C 扩展带边界声明的指针类型,运行时开销约 8.6%,代码变更约 17.5%。尚未可用于生产。
  • Clang -fbounds-safety(苹果):类似的边界标注系统,仍处于设计文档阶段。
  • C23 自身的安全改进:废除 K&R 声明、nullptr<stdckdint.h> 溢出检测、memset_explicit()[[nodiscard]]

这些努力的共同特点是增量改进——不改变 C 的核心内存模型,仅通过编译器扩展和标准改良来堵住已知漏洞。这在政治上是一个明智的姿态:与其被取代,不如自己变安全。


四、横纵交汇洞察

4.1 历史如何塑造了今天的竞争位置

C 在横向对比中有一个几乎所有竞品都不具备的特征:它是唯一一个操作系统内核用 C 写成的语言。这不是偶然的,而是 1973 年的那个决策——用 C 重写 Unix——在五十年的时间里通过正反馈循环不断强化的结果。

这个循环的逻辑是:Unix/Linux 的成功扩张→更多硬件驱动用 C 写→C 的编译器覆盖更多架构→更多操作系统项目选择 C→更多开发者学习 C→更多代码库累积。到 2025 年,Linux 内核的约 3400 万行 C 代码本身就是 C 语言最厚的壁垒。

对比 Rust:Rust 在技术设计上比 C 更安全,但它缺少这个五十年的累积。Rust 的 2.5 万行内核代码解决了"技术可行性"的问题,但没有解决"重新实现所有驱动程序"的经济学问题。

对比 Go:Go 的 GC 让其不可能进入内核和微控制器领域。

对比 Zig:Zig 的 1.0 尚未发布,企业级的信任积累需要时间。

C 最大的竞争优势不是技术,是惯性。

4.2 优势的历史根源

今天的优势 历史根源 关键时刻
极端可移植性 Unix 从 PDP-11 移植到 Interdata 8/32 的经验 1977-78
操作系统内核统治 Unix 用 C 重写 1973
极简运行时 PDP-11 只有 24KB RAM 的硬件约束 1970-72
编译器覆盖所有架构 五十年的 GCC/LLVM 支持积累 1987(GCC 诞生)-现在
开发者基数庞大 Unix/C 在 80-90 年代大学的 CS 教育统治 1980s-2000s

4.3 劣势的历史根源

今天的劣势 历史根源 关键时刻
内存安全漏洞 K&R C 设计的"自由优先"哲学 1978(标准化之前)
缺乏标准构建系统 Unix 文化中的"每个项目自己管" 持续
标准库极度简陋 1970 年代设计,目标是写操作系统不是写应用 1978
字符串处理危险 C 字符串是字符数组而不是一等类型 1972
模块化薄弱 头文件+预处理器模型是 1970 年代的方案 持续

最值得注意的劣势是 K&R C 的包容性设计。C89 保留了隐式声明以兼容 K&R 代码,这个"兼容性"在接下来的三十年里一直是安全漏洞的来源。C99 保留了 K&R 函数定义以"不破坏已有代码"。直到 C23,K&R 声明才被彻底废除。一个 1978 年的设计妥协,花了 46 年来修正。

4.4 未来推演:三个剧本

剧本一(最可能):共存分层

C 在底层(内核、驱动、固件、RTOS)保持统治地位。Rust 在上层系统软件(新开发的网络服务、安全敏感子系统)中稳步渗透。Go 在云基础设施上继续增长。三种语言形成明确的分层结构——C 在最底层,Rust 在中间,Go 在应用层。

关键假设:Linux 内核的 Rust 集成在 2028-2030 年达到 10 万行级别(不是 3400 万行的量级),但不会反超 C。C 的增量改进(C23 的安全特性、Clang 的边界检查扩展)减缓了安全政策压力。

剧本二(最危险):监管驱动替代

如果美国/欧盟在 2027-2028 年推出联网设备的强制内存安全要求——要求所有新开发的系统软件必须使用内存安全语言——C 的新项目在安全关键领域会急剧萎缩。现有的 C 代码库不会消失,但 C 会类似于今天的 COBOL:大量的遗留系统需要维护,但新项目几乎没人选。

关键假设:白宫 ONCD 的方向在 2027 年后变成立法行动。Rust 的编译器生态在 2028 年覆盖目前 C 能覆盖的所有嵌入式架构。

剧本三(最乐观):C 的"安全复兴"

C23 的安全改进、Clang 的边界检查扩展、微软 Checked C 的成熟,三者叠加使得现代 C 的内存安全性提升到可与 Rust 竞争的水平。同时 AI 推理引擎的需求推高了对 C 开发者的需求。2025 年 GitHub 上 C 贡献者增长 20.9% 就是这一趋势的早期信号。

关键假设:Checked C 在 2028 年前进入生产可用状态。主流编译器在 2030 年默认启用边界检查。硬件性能增长使得边界检查的运行时开销可以被接受。

4.5 一个回环

1973 年,Ken Thompson 用 C 重写了 Unix,因为汇编语言移植到新机器太痛苦了——当时 C 代表的是"可移植性"。

2025 年,Linus Torvalds 抱怨 C 标准的"可观察行为"定义搞砸了——而 Rust 支持者说 Rust 代表了"安全性"。

53 年,从"我不要再写汇编"到"我不想再出段错误",C 所解决和面临的问题一直在演变。但有一个事实没有改变:C 仍然是所有系统软件的最底层契约。当一台服务器启动、一个数据库查询、一段自动驾驶代码控制刹车时,最终执行的二进制大概率是通过 C 编译器生成的。在这个意义上,C 不是一个"活着的"语言,它是一个基础设施——就像混凝土,你不觉得它"活着",但没有它什么都建不起来。


五、信息来源

官方标准与委员会

历史与起源

标准版本详情

横向对比

安全与政策

C 语言生态与未来

所有 URL 最后访问时间:2026-06-23


方法论说明

本报告采用横纵分析法(Horizontal-Vertical Analysis)撰写,该方法融合了索绪尔的历时-共时语言学分析、社会科学中的纵向-横截面研究设计、以及商学院案例研究法的核心思想。纵向分析(第二章)沿时间轴追踪 C 语言从 1969 年至今的完整演进历程;横向分析(第三章)在当前时间截面上与 C++、Rust、Zig、Go、Ada 等竞品进行系统性对比;横纵交汇(第四章)交叉两条轴线,产出了关于 C 语言历史惯性、优势/劣势根源和未来走向的综合性判断。

posted @ 2026-06-23 12:07  炼金术  阅读(30)  评论(0)    收藏  举报