什么ABI,如果有的话,限制 intmax_t的大小?

从1999版本开始,ISO C标准定义了一个标准头文件 ,它定义了typedef intmax_tuintmax_t 。 它们分别指定“能够表示任何(有符号|无符号)整数类型的任何值的一个(有符号|无符号)整数类型”。

例如,如果典型地,最宽的有符号和无符号整数类型是long long intunsigned long long int ,两者通常都是64位,那么intmax_tuintmax_t可能在定义如下:

 typedef long long int intmax_t; typedef unsigned long long int uintmax_t; 

有一组有限的预定义有符号和无符号整数类型,范围从signedunsigned和plain charsignedunsigned long long int

C99和C11还允许实现定义扩展的整数类型 ,这些类型不同于任何标准类型,并且具有实现定义的关键字的名称。

gcc和clang在某些但不是所有目标上都支持__int128unsigned __int128类型。 这些行为类似于128位整数类型,但它们被视为扩展整数类型 ,并且两个编译器的文档都声明它们不支持任何扩展整数类型。 由于这些不是整数类型,因为标准定义了术语,因此intmax_tuintmax_t适用于64位类型,而不是128位类型。

这些都不违反C标准(实现不需要具有任何扩展的整数类型,并且只要它们不破坏任何严格符合的程序,它们就被允许具有任意扩展)。 但在我看来,将__int128unsigned __int128视为扩展整数类型,并将intmax_tuintmax_t视为128位类型是intmax_t uintmax_t

这样做的基本原理是改变intmax_tuintmax_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_tuintmax_t ,甚至标头。 它确实指定了预定义整数类型的大小和对齐方式(如图3.1所示)。

最后,我的问题是:ABI限制的intmax_tuintmax_t的大小是否有效? 如果是这样,ABI有什么要求? (顺便说一下,为什么?)

(在我看来,这样的要求,如果它存在,是不明智的。它违背了C标准允许定义扩展整数类型的目的,以及intmax_tuintmax_t的预期含义。这使得使用128-更加困难在支持它们的系统上有效地对整数类型进行排序,同时在其他系统上回退到更窄的类型。)

参考文献:

  • N1256 ,C99标准的草案
  • N1570 ,C11标准的草案
  • System V AMD64 ABI

正如Colonel Thirty Two所说,单方面进行此更改的编译器会破坏传递uintmax_t参数或返回uintmax_t值的编译单元之间的调用。 尽管SysV ABI没有定义这些类型的传递方式,但实际上保持它们的定义是符合平台ABI的一部分。

即使不是因为这个ABI问题,编译器仍然无法单方面进行此更改,因为它需要对每个目标平台的C标准库进行匹配更改。 具体来说,它至少需要更新printfscanf函数系列, imaxabsimaxdivstrtoimaxstrtoumax及其变体。

更改intmax_tuintmax_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也就不足为奇了。