在C程序中整数的历史typedef汤是什么?
这可能是一个可能的谜题,我可能应该知道他的答案。
十五年前,我所看到的很多C代码在平台特定的#ifdef
中都有大量的整型定义。 似乎我看到的每一个程序或图书馆都有自己的互不相容的typedef汤。 当时我对编程知之甚less,看起来像是一堆奇怪的东西,只是为了告诉编译器你想使用什么types的整数。
我在脑海中编写了一个故事来解释这些typedef是关于什么的,但是我实际上并不知道它是否属实。 我的猜测基本上是,当C最初被开发和标准化时,没有意识到能够平台独立地获得一定大小的整数types是多么的重要,因此所有原始的C整数types可能是不同的大小在不同的平台上。 因此,每个试图编写可移植C代码的人都必须自己做。
它是否正确? 如果是这样,程序员如何期望使用C整数types? 我的意思是,在一个低级别的语言中,有很多地方可以说,“这是一个32位的整数”是不是很重要? 而且自1989年语言标准化以来,当然有人认为人们会试图编写可移植的代码?
当C开始的时候,计算机的连接性比现在less得多,连接也less得多。 对于可移植性来说,inttypes是计算机的自然尺寸被认为是更重要的。 要求在一个36位系统上的一个完整的32位整数types可能会导致代码效率低下。
然后沿着普遍的networking,你正在使用特定的线上大小的领域。 现在互操作性看起来很不一样。 而“八位字节”成为事实上的数据types的量子。
现在你需要8位精确的倍数,所以现在你得到了typedef汤,然后最终标准赶上,我们有他们的标准名称和汤是不是需要的。
C早期的成功归功于它可以灵活地适应几乎所有现有的变体架构@John Hascall :
1)8,16,18,24,32,36等位的原始整数大小,
2)变体有符号整数模型:2的补码,1的补码,有符号整数和
3)各种sorting,大,小等 。
随着编码的发展,algorithm和数据交换推动了更高的统一性,因此需要跨平台满足1和2以上的types。 编码器在#if ...
内部像typedef int int32
一样滚动。 OP的许多变化创造了汤。
C99引入了(u)int_leastN_t, (u)int_fastN_t, (u)intmax_t
来创build可移植的但有点最小的位宽的types。 N = 8,16,32,64需要这些types。
还引入了半可选types(见下面的**),如(u)intN_t
,它具有附加属性,它们必须是2的补码和不填充。 正是这些广泛需要和用来稀释整体汤的stream行types。
程序员如何期望使用C整数types?
通过编写不依赖位宽的灵活代码。 使用LONG_MIN, LONG_MAX
编码strtol()
是相当容易的LONG_MIN, LONG_MAX
不考虑bit-width / endian / integer编码。
然而,许多编码任务要求精确的宽度types和2的补码以实现简单的高性能编码。 在这种情况下,最好不要将其移植到36位机器和32位符号级别,并坚持2 N (整数的2的补码)整数。 各种CRC和encryptionalgorithm和文件格式浮现在脑海。 因此需要固定宽度types和一个指定的(C99)方式来完成它。
今天仍然存在需要pipe理的陷阱 。 例子:通常的促销int/unsigned
失去了一些控制,因为这些types可能是16,32或64。
**
这些types是可选的。 但是,如果一个实现提供宽度为8,16,32或64位的整数types,没有填充位,并且(对于带符号的types)有二进制补码表示,它应该定义相应的typedef名称。 C11 7.20.1.1整数宽度的整数types3
我记得那段时间,我也犯了同样的错误!
一个问题是int
的大小,它可以是相同的short
,或long
或介于两者之间。 例如,如果您使用的是二进制文件格式,则所有内容都必须alignment。 字节sorting复杂的事情。 许多开发者走了懒惰的路线,只是做了什么,而不是逐字节地选取数字。 当机器升级到更长的字长度时,所有的地狱都破灭了。 所以typedef
是一个简单的黑客来解决这个问题。
如果性能是一个问题,因为它经常是当时的情况, int
保证是机器最快的自然尺寸,但是如果你需要32位,而int
比那个短,那么你有翻滚的危险。
在C语言中, sizeof()
不应该在预处理器阶段解决,这使事情变得复杂,因为你不能这样做,例如#if sizeof(int) == 4
。
就个人而言,一些基本原理也是从汇编语言的思维方式出发,不愿意抽象出short
, long
和long
的概念。 那时候,汇编语言被频繁地用在C语言中。
现在,有很多非二进制文件格式,JSON,XML等等,二进制表示法无关紧要。 另外,许多stream行的平台已经在32位int
或更长的时间上定义了,通常这对于大多数用途来说已经足够了,所以滚动问题就不会那么严重了。
C是20世纪70年代早期的计算生态系统非常不同的产物。 而不是数以百万计的计算机通过一个扩展的networking相互通话,你可能有十万个全球系统,每个系统运行几个单一的应用程序,几乎没有系统之间的通信。 你不能假定任何两个体系结构都有相同的字大小,或者以相同的方式表示有符号整数。 市场仍然很小,没有任何标准化的需要,计算机之间没有太多的交stream,也没有太多可移植性。
如果是这样,程序员如何期望使用C整数types?
如果你想编写最大可移植的代码,那么你没有任何超出标准保证的范围。 在int
的情况下,这意味着你没有假设它可以代表范围以外的任何东西[-32767,32767]
,也没有假设它会以2的补码表示,也没有假设它是一个(它可能比16位宽,但如果它包含任何填充位,它们仍然只代表一个16位的范围)。
如果你不关心可移植性,或者你正在做的东西本质上是不可移植的(通常是这样做的),那么你可以使用任何types的东西来满足你的需求。
我做了大部分的高级应用程序编程,所以我比较less关心范围的问题。 即使如此,我偶尔也需要深入二进制表示,它总是咬我的屁股。 我记得在九十年代早期编写的代码必须在经典的MacOS,Windows 3.1和Solaris上运行。 我为32位掩码创build了一堆枚举常量,在Mac和Unix框中工作正常,但是在Windows上无法编译,因为在Windows上int
只有16位宽。
C被devise成一种语言,可以被移植到尽可能广泛的机器上,而不是像这样一种语言,它允许大多数types的程序在没有修改的情况下在这样的机器上运行。 对于大多数实际的目的,C的types是:
-
一个8位types(如果有的话),或者是至less8位的最小types。
-
一个16位的types,如果有的话,或者是最less的16位的types。
-
一个32位的types,如果有的话,或者至less有32位的types。
-
如果系统能够像16位types那样有效地处理这种事情,那么将是32位的types,否则是16位。
如果代码需要8位,16位或32位的types,并且不太可能在不支持它们的机器上使用,那么对于这种types的代码来说,没有任何特殊的问题,比如8 char
,16 char
, 32位。 唯一没有将这些名称映射到那些types的系统将是那些不能支持这些types的系统,并且将不能有效地处理需要它们的代码。 这样的系统将被限制于编写已被写入与他们使用的types兼容的代码。
我认为C可能最好被看作是将系统规范转换成语言方言的一个方法。 使用36位存储器的系统实际上不能有效地处理与使用基于八位字节的存储器的系统相同的语言方言,但是学习一种方言的程序员仅通过学习什么整数表示就能够学习另一方言后者使用。 告诉一个需要为36位系统编写代码的程序员“这台机器就像其他机器一样,除了char
是9位, short
是18位, long
是36位”比说“你必须使用汇编语言,因为其他语言都需要整数types,这个系统不能有效地处理”。
并非所有机器都具有相同的原始字大小。 虽然你可能会想要一个更小的variables会更有效率,但事实并非如此。 事实上,使用与CPU的本地字大小相同的variables对于算术,逻辑和位操作操作来说要快得多。
但究竟是“本地字大小”呢? 几乎总是这意味着CPU的寄存器大小,这与算术逻辑单元(ALU)可以使用的相同。
在embedded式环境中,还有8位和16位CPU(还有4位PIC控制器?)。 还有很多的32位处理器。 所以“本地字大小”这个概念对于C开发人员来说是非常好的。
对于64位处理器,通常可以很好地支持32位操作数。 实际上,使用32位整数和浮点值通常可能比全字大小更快。
另外,在布局C结构时,在本地字alignment和整个内存消耗之间有折衷。
但是,两种常见的使用模式仍然存在:为了正确性或互操作性,在需要的地方使用大小不可知的代码来提高速度(int,short,long)或固定大小(int32_t,int16_t,int64_t)。