什么ABI,如果有的话,限制 intmax_t的大小?
从1999版本开始,ISO C标准定义了一个标准头文件 ,它定义了typedef
intmax_t
和uintmax_t
。 它们分别指定“能够表示任何(有符号|无符号)整数类型的任何值的一个(有符号|无符号)整数类型”。
例如,如果典型地,最宽的有符号和无符号整数类型是long long int
和unsigned long long int
,两者通常都是64位,那么intmax_t
和uintmax_t
可能在定义如下:
typedef long long int intmax_t; typedef unsigned long long int uintmax_t;
有一组有限的预定义有符号和无符号整数类型,范围从signed
, unsigned
和plain char
到signed
和unsigned
long long int
。
C99和C11还允许实现定义扩展的整数类型 ,这些类型不同于任何标准类型,并且具有实现定义的关键字的名称。
gcc和clang在某些但不是所有目标上都支持__int128
和unsigned __int128
类型。 这些行为类似于128位整数类型,但它们不被视为扩展整数类型 ,并且两个编译器的文档都声明它们不支持任何扩展整数类型。 由于这些不是整数类型,因为标准定义了术语,因此intmax_t
和uintmax_t
适用于64位类型,而不是128位类型。
这些都不违反C标准(实现不需要具有任何扩展的整数类型,并且只要它们不破坏任何严格符合的程序,它们就被允许具有任意扩展)。 但在我看来,将__int128
和unsigned __int128
视为扩展整数类型,并将intmax_t
和uintmax_t
视为128位类型是intmax_t
uintmax_t
。
不这样做的基本原理是改变intmax_t
和uintmax_t
的大小将是“ABI不兼容的变化”。
Clang C ++状态页面在脚注(5)中说:
对于没有提供任何扩展整数类型的Clang等实现,不需要进行编译器更改。
__int128
不被视为扩展整数类型,因为更改intmax_t
将是ABI不兼容的更改。
(是的,这主要讨论C ++,但规则与C相同)
在gcc错误报告中 ,声称:
sizeof(intmax_t)
由各种LP64 ABI修复,不能更改
在这两种情况下,都没有提到这种说法。
标题为“System V应用程序二进制接口,AMD64架构处理器补充,草案版本0.99.6”的x86_64 ABI文档未提及intmax_t
或uintmax_t
,甚至标头。 它确实指定了预定义整数类型的大小和对齐方式(如图3.1所示)。
最后,我的问题是:ABI限制的intmax_t
和uintmax_t
的大小是否有效? 如果是这样,ABI有什么要求? (顺便说一下,为什么?)
(在我看来,这样的要求,如果它存在,是不明智的。它违背了C标准允许定义扩展整数类型的目的,以及intmax_t
和uintmax_t
的预期含义。这使得使用128-更加困难在支持它们的系统上有效地对整数类型进行排序,同时在其他系统上回退到更窄的类型。)
参考文献:
- N1256 ,C99标准的草案
- N1570 ,C11标准的草案
- System V AMD64 ABI
正如Colonel Thirty Two所说,单方面进行此更改的编译器会破坏传递uintmax_t
参数或返回uintmax_t
值的编译单元之间的调用。 尽管SysV ABI没有定义这些类型的传递方式,但实际上保持它们的定义是符合平台ABI的一部分。
即使不是因为这个ABI问题,编译器仍然无法单方面进行此更改,因为它需要对每个目标平台的C标准库进行匹配更改。 具体来说,它至少需要更新printf
和scanf
函数系列, imaxabs
, imaxdiv
, strtoimax
和strtoumax
及其变体。
更改intmax_t
和uintmax_t
等类型也会更改使用它们的所有程序的ABI,因为它们现在引用不同的类型。
假设您有程序A,它使用共享库B中的函数和uintmax_t
参数。 如果GCC更改了uintmax_t
的定义并且A(但不是B)被重新编译,那么A中的uintmax_t
和B中的uintmax_t
现在指的是两种不同的类型,打破了ABI。
我认为这里理解的关键是因为ABI规范中没有记录某些内容并不意味着它不是ABI的一部分。 只要在库边界上使用类型,它的属性就会成为该库的ABI的一部分。
通过在标准头中定义(u)intmax_t并在标准库的函数中使用它们,它们成为该库的ABI的一部分,无论它们是否包含在任何正式的ABI规范中。
对于类似Unix的平台而言,这尤其是一个问题,其中C标准库被视为平台的一部分,而不是编译器的一部分。
现在可以转换这个。 Printf将宏用于类型说明符,因此可以根据intmax_t的大小对这些宏进行不同的定义。 宏可以类似地用于将标准库中的少数函数映射到不同的实现,但它是一堆额外的工作以获得可疑的收益,因此gcc采取阻力最小的路径来添加他们需要的function也就不足为奇了。