C 语言灵活整数大小并非设计失误
C's Flexible Integer Sizes Were Not a Design Mistake
很多初学者误以为 C 语言中 int 类型必须是 32 位,但这其实是现代思维的误区。在 C 语言诞生的 1970 年代,计算机架构千差万别,存在 12 位、18 位、36 位甚至 60 位的机器。C 语言采用灵活的整数大小,正是为了在 DEC PDP-11、Honeywell 6000 等差异巨大的硬件上实现真正的可移植性和高效运行。如果当时强制规定 int 为 32 位,在 16 位机器上会导致性能减半,在 36 位机器上则需额外的掩码操作。这种看似“不标准”的设计,实则是为了适配当时复杂的硬件环境而做出的深思熟虑的权衡。
C 语言的灵活类型大小并非疏忽,而是该语言实现可移植性的关键所在。
HN 评论区
165- adrian_b
我同意 C 语言灵活的整数大小在其诞生时确实是必要的,因为当时一些重要的计算机的字长并非 2 的幂次。
不过,我直到 1990 年才真正开始用 C 语言编程,那时我获得了 Microsoft C 和 Borland Turbo C 编译器。
也就是说,在 36 年前的那个时间点,C 语言灵活的整数大小其实已经过时了。
从那时至今,我在各种各样的计算机上使用 C 语言,从服务器、工作站到最小的微控制器,我目睹了由灵活整数大小引发的无数可移植性问题。
唯一没有可移植性问题的程序,是那些从未使用灵活整数大小、只使用固定大小整数(例如 8 位、16 位、32 位或 64 位)的程序。
虽然 sizeof 可以解决内存分配或复制的问题,但它无法帮助防止意外的整数溢出,因为甚至连 "char" 的大小都可能未知;即便 "char" 的大小已知,编写包含多条路径来针对不同整数大小进行检查或防止溢出的代码也极其繁琐。
灵活的整数大小只有在旧式计算机上才能发挥良好作用,因为在那类机器上,整数溢出会触发硬件异常,因此只需安装一个溢出处理程序,就能确保 C 代码无论原生整数大小如何都能正确运行。
- layer8
> 像 char、int、short 和 long 这样的语言类型并没有保证它们在内存中占用多少字节。
实际上,C 标准确实保证 char 在内存中恰好占用 1 个字节。只是 C 语言中的“字节”可以包含超过 8 个比特。“字节”仅仅是指针可寻址的最小内存单位。
文章后文也承认了“C 要求 char 至少有 8 个比特(CHAR_BIT >= 8),而非恰好 8 个”,并以 Honeywell 6000 为例,指出这是一种 char 为 9 比特(且 int 为 36 比特)的 C 实现。
从计算机历史来看,字节的大小取决于硬件,并未标准化。维基百科关于“字节”的文章引用了 Knuth 1968 年的《计算机程序设计艺术》(TAOCP),其中将字节定义为一个“包含未指定数量信息……至少能容纳 64 个不同值……至多 100 个不同值”的单位。因此,在二进制计算机上,一个字节必须由 6 个比特组成。
- TheCoelacanth
我会争辩说,灵活的整数本身并不是设计失误,问题在于它们的命名。
根本不应该存在一个名为 `int` 却持有可变字节数的类型;本来应该直接使用类似 int_least32_t 和 int_fast32_t 的现代别名,这些名称才能真正传达数值可能的大小。
- habitue
这确实是有意的,没错。这是为了解决某一类特定问题而做出的尝试。
不过,回过头来看,这是一个错误。
证据是:当世界转向 64 位时,我们并没有简单地让 int 在 amd64 上代表 8 个字节。这清楚地表明,一旦我们对事物有了更深的理解,就承认了当初的设计并不正确。
- InvisibleUp
灵活整数大小造成最大破坏的地方在于 ABI(应用程序二进制接口)。在动态链接出现之前,ABI 并不是一个大问题,但如今它却至关重要。此外,不知出于何种原因,我们决定将 C 头文件作为定义库 ABI 的标准方式。这意味着每个人都必须担心精确地定义整数大小,以及 size_t 或 intmax_t 等更冷门的类型。关于这些内容的精彩文章见:https://thephd.dev/to-save-c-we-must-save-abi-fixing-c-funct...
- ack_complete
68000 处理器就是一个实际例子,说明了即使在单颗 CPU 内部,将 int 映射为“最方便”的大小也并不明确。其指令集对 32 位整数处理得很好,但从执行效率来看,16 位整数更快。因此,像 Lattice C 这样的编译器将 int 定义为 int32,而像 Aztec C 这样的编译器则将其映射为 int16。
结果之一就是,大量针对 68000 系统编写的 C 代码到处使用 short 而非 int,随后又不得不针对那些更偏好 int32 而非 int16 的 CPU 进行更新。偶尔还能看到由此造成的附带损害,比如一些莫名其妙的注释:“检查缓冲区是否太 int”。
- codedokode
我认为它之所以没能行得通,是因为 "int" 的大小不统一让编程变得困难。例如,一个系统必须管理多达 100,000 条记录。我能用 int 来存储记录编号吗?如果它是 16 位的怎么办?如果需要在机器之间传输数据,当 "int" 的大小可能不同时,我该如何使用它?
可能有人已经意识到这很不方便,所以在 64 位机器上,int 仍然是 32 位而不是 64 位。
拥有 16 位 int 或 9 位字节的计算机早已销声匿迹,但语言却仍不得不背负这份遗产。
- quelsolaar
好文章。
C 语言如果没有这种灵活性,恐怕早就无法生存了。
但这不仅仅是个历史遗留问题。如今仍有像 DSP 这样的现代平台,它们的 char 大小为 32 位,因为那是它们最小的可寻址类型。这些平台依赖 C 语言作为工具链,即使大多数“可移植”的 C 代码无法在其上正确运行。能够构建这样的硬件,而无需发明一种新语言或方言来编程,这对整个世界来说是一个巨大的胜利。
<编辑> 我第一遍读的时候没注意到关于 DSP 的脚注 <编辑>