以下几节介绍了一些可供应用程序开发者使用的资源,可帮助编写能够同时支持 32 位编译和 64 位编译的单一源代码。
系统头文件 <sys/types.h> 和 <inttypes.h> 中包含有助于使应用程序对于 32 位和 64 位均安全的常量、宏和派生类型。尽管详细讨论这些内容超出了本文档的范围,但是以下各节以及附录 A,派生类型更改 中仍讨论了其中的部分内容。
包含 <sys/types.h> 的应用程序源文件可以通过包括 <sys/isa_defs.h> 使编程模型符号 _LP64 和 _ILP32 的定义可用。
有关预处理程序符号(_LP64 和 _ILP32)和宏(_LITTLE_ENDIAN 和 _BIG_ENDIAN6)的信息,请参见 types(3HEAD)。
使用系统派生类型有助于使代码对于 32 位和 64 位均安全,这是由于派生类型本身对于 ILP32 和 LP64 数据模型均安全。通常,使用派生类型以便于更改是良好的编程做法。如果数据模型在将来发生变化,或者在移植到其他平台时,只需更改系统派生类型即可,而无需更改应用程序。
<sys/types.h> 头文件中包含大量应在适当时机使用的基本派生类型。特别是以下几种类型颇受关注:
clock_t
类型 clock_t
表示系统时间(以时钟周期为单位)。
dev_t
类型 dev_t
用于设备号。
off_t
类型 off_t
用于文件大小和偏移量。
ptrdiff_t
类型 ptrdiff_t
是一种带符号整数类型,用于对两个指针执行减法运算后所得的结果。
size_t
类型 size_t
用于内存中对象的大小(以字节为单位)。
ssize_t
带符号的大小类型 ssize_t
供返回字节计数或错误指示的函数使用。
time_t
类型 time_t
用于时间(以秒为单位)。
所有这些类型在 ILP32 编译环境中都保持 32 位值,并会在 LP64 编译环境中增加到 64 位值。
其中某些类型的用法将在本章转换为 LP64 的指导原则中更详细地介绍。
<inttypes.h>
文件在 Solaris 2.6 发行版中添加了头文件 <inttypes.h>,程序员可利用它提供的常量、宏和派生类型使其代码与显式指定大小的数据项兼容,而不管编译环境如何。该文件中包含用来处理 8 位、16 位、32 位和 64 位对象的机制,它是 ANSI C 议案的一部分,可以与 ISO/JTC1/SC22/WG14 C 委员会对当前 ISO C 标准(即 ISO/IEC 9899:1990 编程语言-C)所进行修订的工作草案保持一致。
<inttypes.h> 提供的基本功能包括:
一组定宽的整数类型
uintptr_t
和其他有用的类型
常量宏
限制
格式字符串宏
以下各节中将对这些功能进行更详细的讨论。
<inttypes.h> 提供的定宽整数类型同时包括带符号整数类型(如 int8_t
、int16_t
、int32_t
和 int64_t
)以及无符号整数类型(如 uint8_t
、uint16_t
、uint32_t
和 uint64_t
)。定义为可具有指定位数的最短整数类型的派生类型包括 int_least8_t
、int_least64_t
、uint_least8_t
和 uint_least64_t
。
不应不加选择地使用这些定宽类型。例如,类型 int
可以继续用于循环计数器和文件描述符,类型 long
可用于数组索引。另一方面,对于以下各项的显式二进制表示形式,则应使用定宽类型:
盘上数据
线上数据
硬件寄存器
二进制接口规格(包含显式指定了大小的对象,或者涉及 32 位和 64 位程序之间的共享或通信)
二进制数据结构(供 32 位和 64 位程序通过共享内存和文件等进行使用)
uintptr_t
和其他有用的类型
<inttypes.h> 提供的其他有用类型包括大小足以包含一个指针的带符号整数类型和无符号整数类型。这些类型以 intptr_t
和 uintptr_t
形式提供。此外,还会将 intmax_t
和 uintmax_t
分别定义为可用的最长(以位为单位)带符号整数类型和无符号整数类型。
选用 uintptr_t
类型作为指针的整数类型比使用基本类型(如 unsigned long
)要好。尽管在 ILP32 和 LP64 数据模型中,类型 unsigned long
与指针的长度相同,但如果使用 uintptr_t
,则只需在使用其他数据模型时更改 uintptr_t
的定义即可。这会使其可移植到许多其他系统中,并且还可以在 C 中更为清楚地表达意图。
需要执行地址运算时,intptr_t
和 uintptr_t
类型对于强制转换指针非常有用。因此,应使用这些类型,而不是使用 long
或 unsigned long
类型。
使用 uintptr_t
进行强制类型转换通常比使用 intptr_t
安全,在进行比较时尤为安全。
提供宏的目的在于指定给定常量的大小和符号。这些宏包括 INT8_C(c)、INT64_C(c)、UINT8_C(c) 和 UINT64_C(c) 等。基本上,这些宏会在常量的末尾放置一个 l、ul、ll 或 ull(如有必要)。例如,对于 ILP32,INT64_C(1) 会在常量 1 后面附加 ll;对于 LP64,则附加 l。
用来使常量成为最长类型的宏包括 INTMAX_C(c) 和 UINTMAX_C(c)。这些宏对于指定转换为 LP64 的指导原则中介绍的常量类型会非常有用。
由 <inttypes.h> 定义的限制是用于为各种整数类型指定最小值和最大值的常量,其中包括每个定宽类型的最小值(INT8_MIN
和 INT64_MIN
等)和最大值(如 INT8_MAX
和 INT64_MAX
等)及其对应的无符号的最小值和最大值。
该文件还提供了每个最短长度类型的最小值和最大值,其中包括 INT_LEAST8_MIN
、INT_LEAST64_MIN
、INT_LEAST8_MAX
和 INT_LEAST64_MAX
等及其对应的无符号的最小值和最大值。
最后,该文件定义了支持的最长整数类型的最小值和最大值,其中包括 INTMAX_MIN
和 INTMAX_MAX
及其对应的无符号的最小值和最大值。
<inttypes.h> 文件还提供了用于指定 printf 和 scanf 格式说明符的宏。实际上,这些宏会根据内置于宏名称中的参数的位数,在格式说明符前面放置一个 l 或 ll,将参数指定为 long
或 long long
类型。
printf(3C) 的宏以十进制、八进制、无符号和十六进制格式列显 8 位、16 位、32 位和 64 位整数以及最短和最长整数类型。例如,以十六进制表示法列显 64 位整数:
int64_t i; printf("i =%" PRIx64 "\n", i);
同样,scanf(3C) 的宏也以十进制、八进制、无符号和十六进制格式读取 8 位、16 位、32 位和 64 位整数以及最长整数类型。例如,读取无符号的 64 位十进制整数:
uint64_t u; scanf("%" SCNu64 "\n", &u);
请勿毫无限制地使用这些宏,最好将其与定宽类型一起使用。有关更多详细信息,请参阅定宽的整数类型一节。