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 声明,引入 nullptr、constexpr、typeof、#embed |
| C29(预计 2029) | C2y | WG14 | 已采纳 defer、命名循环、_Countof、if 声明 |
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)。他加入了类型声明(int、char),把解释改为编译,改进了指针和数组的处理。
到 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) 表示"真的没有参数"而不是"参数未知"。这使编译器能够拦截参数个数和类型不匹配的调用。另外引入的关键字 const 和 volatile,前者标记只读数据,后者强迫编译器对内存映射 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;有了restrict,factor驻留在寄存器中。- 复数类型(
_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_s、scanf_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 标准只承认二进制补码,移除了反码和符号-数值表示。实际上没有现代硬件使用另外两种表示,但它们的理论存在性在过去给编译器优化制造了大量不必要的"未定义行为"路障。
新增的关键语言特性:
-
nullptr和nullptr_t:在 C23 之前,NULL通常被宏定义为0或(void*)0。这导致它在宏展开或泛型匹配时具有二义性(例如被识别为整型int)。nullptr具有专属的指针类型,解决了强类型校验下的安全漏洞。 -
constexpr:标量对象的编译时常量。不是 C++ 的constexpr函数,只是"在编译期就确定值的对象"。但这已经足够消除一些"常量折叠"的歧义。 -
typeof/typeof_unqual:类型提取操作符。类似 GCC 长期支持的扩展,现在正式进入标准。 -
true/false关键字:它们曾经是<stdbool.h>中定义的宏,现在是语言内建的关键字。 -
#embed:编译期包含二进制资源。这解决了嵌入式开发中的一个痛点——以前你只能要么用xxd -i把二进制文件转成数组,要么用汇编.incbin指令。#embed让标准 C 可以直接在编译期嵌入资源。 -
[[]]属性语法(借自 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++ 特性以缩小差距——属性语法、constexpr、auto 类型推断、{} 空初始化器、枚举固定底层类型。这反映了 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 是一个整数,语义上不同于 bool 或 enum。C 认为表达式的语义可以从其底层表示推导出来,C++ 则认为编译器应该通过类型系统来强制执行语义规则。
一个具体的例子:C 中 int 和 _Bool 在底层都是位模式,类型转换是 reinterpret_cast 风格的重解释;C++ 中 int 和 bool 之间是 static_cast 风格的显式转换。这在实践中意味着,编写"既是有效 C 又是有效 C++"的共享头文件越来越困难。
但 C++ 有一个 C 作为子集的历史包袱。"C 是 C++ 的子集"在 90 年代曾是事实,但 C99 开始分歧加大。到了 C23,C 有了 typeof、nullptr、constexpr、[[]] 属性——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 的原因:
- 产品生命周期长达 20 年以上——一旦冻结了工具链,升级编译器需要重新认证。
- 芯片厂商锁定——很多 MCU 厂商发布的是特定版本的 GCC 分支,后续版本可能不再支持该芯片。
- VLA 在 DSP/小型 MCU 上不可行——栈空间极小。
- MISRA-C 历史上基于 C89——迁移到新标准意味着重写编码规范的所有规则。
- "能用就不要动"——嵌入式文化中最强大的力量。
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 不是一个"活着的"语言,它是一个基础设施——就像混凝土,你不觉得它"活着",但没有它什么都建不起来。
五、信息来源
官方标准与委员会
- ISO/IEC JTC1/SC22/WG14(C 标准委员会)官方页面:https://www.open-std.org/jtc1/sc22/wg14/www/
- WG14 文档索引(N-系列):https://www.open-std.org/jtc1/sc22/wg14/www/docs/
- Cppreference C 语言手册:https://en.cppreference.com/w/c
- GCC C 标准支持状态:https://gcc.gnu.org/projects/c-status.html
- Clang C 标准支持状态:https://clang.llvm.org/c_status.html
- MSVC
/std选项文档:https://learn.microsoft.com/en-gb/cpp/build/reference/std-specify-language-standard-version - ARM 编译器标准支持:https://developer.arm.com/documentation/101458/24-10/Standards-support/
历史与起源
- Dennis Ritchie 的 C 语言历史(贝尔实验室存档):https://www.bell-labs.com/usr/dmr/www/chist.html(Wayback Machine 存档)
- ACM 论文:Dennis Ritchie 的 C 语言发展回顾:https://dl.acm.org/doi/10.1145/155360.155580
- Wikipedia - The C Programming Language:https://en.wikipedia.org/wiki/The_C_Programming_Language
- ETHW - C 语言历史:https://ethw.org/C
- C User's Group Network 档案:https://www.tuhs.org/Usenet/comp.lang.c/
标准版本详情
- Wikipedia - C11:https://en.wikipedia.org/wiki/C11_(C_standard_revision)
- Wikipedia - C18:https://en.wikipedia.org/wiki/C18_(C_standard_revision)
- Wikipedia - C23:https://en.wikipedia.org/wiki/C23_(C_standard_revision)
- Wikipedia - C29:https://en.wikipedia.org/wiki/C29_(C_standard_revision)
- C23 特性总览:https://github.com/C23Nexus/modern-c-features
- Jens Gustedt 的 Modern C 博客:https://gustedt.gitlabpages.inria.fr/modern-c/
- Red Hat 开发者博客 - C11 Atomics:https://developers.redhat.com/blog/2016/01/14/toward-a-better-use-of-c11-atomics-part-1
- LWN.net - Linux driving C standards:https://lwn.net/Articles/549583/
横向对比
- TIOBE Index:https://www.tiobe.com/tiobe-index/
- GitHub Octoverse 2025:https://github.blog/news-insights/octoverse/
- Rust 进入 Linux 内核(Phoronix):https://www.phoronix.com/news/Greg-KH-On-New-Rust-Code
- Linus Torvalds 2025 年 2 月批评 C 标准(LKML):https://lkml.org/lkml/2025/2/26/2141
- NSF Rust vs C 性能研究:https://par.nsf.gov/biblio/10603953
- Infoworld - Zig 语言介绍:https://www.infoworld.com/article/2338081
- Stack Overflow - Go vs C in embedded:https://stackoverflow.blog/2022/04/04/comparing-go-vs-c-in-embedded-applications/
- ACM - C vs Ada 安全关键系统(Rogers SIGAda'11):https://dl.acm.org/doi/epdf/10.1145/2070337.2070363
安全与政策
- CISA 白皮书 - 关键开源项目中的内存安全(2024-06):https://www.cisa.gov/sites/default/files/2024-06/joint-guidance-exploring-memory-safety-in-critical-open-source-projects-508c.pdf
- The Register - 美国政府希望开发者停止使用 C 和 C++(2024-11):https://www.theregister.com/2024/11/08/the_us_government_wants_developers
- Microsoft Checked C:https://github.com/microsoft/checkedc
- Clang BoundsSafety 文档:https://clang.llvm.org/docs/BoundsSafety.html
C 语言生态与未来
- Beningo 嵌入式技术图谱 2026:https://www.beningo.com/embedded-technology-atlas/2026/
- Embedded Computing - Rust 嵌入式圆桌讨论:https://embeddedcomputing.com/technology/software-and-os/ides-application-programming/rust-embedded-community-roundtable-will-rust-replace-c
- Dev.to - Why Learning C Still Matters in 2025:https://dev.to/charan_gutti_cf60c6185074/why-learning-c-still-matters-in-2025-and-why-employers-love-it-4n5d
- Infoworld - Rust vs Go vs Zig:https://www.infoworld.com/article/3849417
所有 URL 最后访问时间:2026-06-23
方法论说明
本报告采用横纵分析法(Horizontal-Vertical Analysis)撰写,该方法融合了索绪尔的历时-共时语言学分析、社会科学中的纵向-横截面研究设计、以及商学院案例研究法的核心思想。纵向分析(第二章)沿时间轴追踪 C 语言从 1969 年至今的完整演进历程;横向分析(第三章)在当前时间截面上与 C++、Rust、Zig、Go、Ada 等竞品进行系统性对比;横纵交汇(第四章)交叉两条轴线,产出了关于 C 语言历史惯性、优势/劣势根源和未来走向的综合性判断。

浙公网安备 33010602011771号