C 编程/stdint.h
stdint.h 是 头文件,它包含在 C 标准库 中,并于 C99 标准库 的 7.18 节中引入,允许程序员通过提供一组类型定义来编写更可移植的代码,这些类型定义指定了精确宽度整数类型,以及使用宏[1] 定义的每个类型的允许最小值和最大值。此头文件对于嵌入式编程尤其有用,嵌入式编程通常涉及对特定硬件 I/O 寄存器的相当多的操作,这些寄存器需要具有固定宽度、特定位置和精确对齐的整数数据。stdint.h(对于 C 或 C++) 和 cstdint(对于 C++) 可以下载或快速创建,如果它们没有提供的话。
精确宽度整数类型的命名约定为 intN_t 用于 带符号 int,uintN_t 用于 无符号 int [1] 。例如,int8_t
和 uint64_t
可以与定义它们各自范围的 INT8_MIN
到 INT8_MAX
和 0
(零)到 UINT64_MAX
声明在一起;同样使用类似但大写字母的命名约定。此外,stdint.h 还定义了能够容纳对象指针的整数类型的限制,例如 UINTPTR_MAX
,其值取决于处理器及其地址范围[1]。
精确宽度类型及其相应的范围仅在该特定编译器/处理器存在的情况下才会包含在该头文件中。请注意,即使在同一处理器上,两个不同的编译器实现也可能不同。使用 #if
或 #ifdef
将允许通过使用编译器的预处理器包含或排除类型,以便为编译器及其处理器目标选择正确的精确宽度集。
相关的包含文件 <limits.h>
提供了通用整数变量类型范围限制的宏值。在 C 中,<limits.h>
已经包含在 <stdint.h>
中,但与 <stdint.h>
不同的是,它与实现无关;<limits.h>
中定义的所有最大和最小整数值都是编译器实现特定的。例如,生成 32 位可执行文件的编译器将定义 LONG_MIN 为 −2,147,483,648 [−231],但对于 64 位处理器目标,LONG_MIN 可以是 −9,223,372,036,854,775,808 [−263]。
C 标准有一个“对应整数类型”的概念。非正式地说,这意味着对于任何整数类型 T
typedef signed T A;
typedef unsigned T B;
类型 A
和类型 B
被称为对应整数类型(注意:typedef
不会创建新的类型,它创建了一个新的标识符作为给定类型的同义词)。这很重要,有两个原因
- 对应类型对别名和类型双关语友好
- 对应类型具有类似的对象表示
这两者的结合需要像这样的代码
A a = 1;
B b = 1;
*(B*)&a = 0;
*(A*)&b = 0;
通过标准具有定义的行为(而不是在一般情况下是未定义的)。关于你能将此推到多远,有很多注意事项,因此阅读 C 标准以了解什么是合法或不合法非常重要(这部分内容主要涉及填充位和超出范围的表示)。
C99 标准阐述了值表示和对象表示之间的区别。
整数的对象表示由 0 个或多个填充位、1 个或多个值位[1] 以及 0 个或 1 个符号位(这不算作值位)组成,具体取决于整数类型的符号。
值表示是整数的概念表示。值表示忽略任何填充位,并对位进行(可能的)重新排列,以便整数按顺序从最高有效值位到最低有效值位排序。大多数程序员处理此表示,因为它允许他们通过仅处理 −0 和超出范围的值来轻松地编写可移植的代码,而不是同时处理这两种值,以及如果他们选择直接处理对象表示,则还会处理棘手的别名规则和陷阱表示。
C 标准仅允许编译器编写器指定的三个有符号整数表示
- 符号和大小
- 反码
- 补码(最广泛使用)
类型 <something>_t
和 u<something>_t
必须是对应的有符号和无符号整数类型。对于标记为可选的类型,实现必须同时定义 <something>_t
和 u<something>_t
,或者两者都不定义。这些类型的限制应使用类似于下面描述的格式使用宏定义。
如果一个类型是 [u]<something>N_t
的形式(或类似地用于预处理器定义),N 必须是一个正十进制整数,没有前导 0。
这些是 intN_t
和 uintN_t
的形式。两种类型都必须由正好 N 位表示,没有填充位。intN_t
必须被编码为补码有符号整数,而 uintN_t
则被编码为无符号整数。这些类型是可选的,除非实现支持宽度为 8、16、32 或 64 的类型,那么它应将它们类型定义为具有对应 N 的对应类型。任何其他 N 都是可选的[1] 。
说明符 | 签名 | 位 | 字节 | 最小值 | 最大值 |
---|---|---|---|---|---|
int8_t
|
带符号 | 8 | 1 | −27 等于 −128 | 27 − 1 等于 127 |
uint8_t
|
无符号 | 8 | 1 | 0 | 28 − 1 等于 255 |
int16_t
|
带符号 | 16 | 2 | −215 等于 −32,768 | 215 − 1 等于 32,767 |
uint16_t
|
无符号 | 16 | 2 | 0 | 216 − 1 等于 65,535 |
int32_t
|
带符号 | 32 | 4 | −231 等于 −2,147,483,648 | 231 − 1 等于 2,147,483,647 |
uint32_t
|
无符号 | 32 | 4 | 0 | 232 − 1 等于 4,294,967,295 |
int64_t
|
带符号 | 64 | 8 | −263 等于 −9,223,372,036,854,775,808 | 263 − 1 等于 9,223,372,036,854,775,807 |
uint64_t
|
无符号 | 64 | 8 | 0 | 264 − 1 等于 18,446,744,073,709,551,615 |
这些类型的限制使用以下格式的宏定义
INTN_MAX
是 intN_t 的带符号版本的最大值 (2N−1 − 1)。INTN_MIN
是 intN_t 的带符号版本的最小值 (−2N−1)。UINTN_MAX
是 uintN_t 的无符号版本的最大值 (2N – 1)。
这些类型的格式为 int_leastN_t
和 uint_leastN_t
。int_leastN_t
是一个有符号整数,uint_leastN_t
是一个无符号整数[1]。
标准要求这些类型至少包含 N 个位,并且没有比它们更小的具有相同符号的类型包含 N 个或更多位。例如,如果一个系统只提供 uint32_t
和 uint64_t
,那么 uint_least16_t
必须等同于 uint32_t
。
实现必须为以下 N 定义这些类型:8、16、32、64。其他任何 N 都是可选的。
这些类型的限制使用以下格式的宏定义
INT_LEASTN_MAX
是int_leastN_t
有符号版本的最大值(2N−1 − 1 或更大)。INT_LEASTN_MIN
是int_leastN_t
有符号版本的最小值(−2N−1 + 1 或更小)。UINT_LEASTN_MAX
是uint_leastN_t
无符号版本的最大值(2N − 1 或更大)。
stdint.h 还应该定义宏,用于转换常量十进制、八进制或十六进制值,这些值保证适用于相应的类型,并且可以使用 #if
INTN_C(value)
将被替换为适合int_leastN_t
的值。例如,如果int_least64_t
被 "typedef" 为signed long long int
,那么INT64_C(123)
将对应于123LL
。UINTN_C(value)
将被替换为适合uint_leastN_t
的值。
这些类型的格式为 int_fastN_t
和 uint_fastN_t
。
标准没有对这些类型进行任何强制要求,除了它们的宽度必须大于或等于 N 之外。它还将如何定义 "快速" 整数类型留给实现者决定。
实现必须为以下 N 定义这些类型:8、16、32、64[2]。
这些类型的限制使用以下格式的宏定义
INT_FASTN_MAX
是int_fastN_t
有符号版本的最大值(2N−1 − 1 或更大)。INT_FASTN_MIN
是int_fastN_t
有符号版本的最小值(−2N−1 + 1 或更小)。UINT_FASTN_MAX
是uint_fastN_t
无符号版本的最大值(2N − 1 或更大)[1]。
intptr_t
和 uintptr_t
分别是有符号和无符号整数,保证能够容纳指针的值。这两个类型是可选的。
这些类型的限制通过以下宏定义:
INTPTR_MIN
是intptr_t
的最小值(−32,767 [−215 + 1] 或更小)。INTPTR_MAX
是intptr_t
的最大值(32,767 [215 − 1] 或更大)。UINTPTR_MAX
是uintptr_t
的最大值(65,535 [216 − 1] 或更大)[3]。
intmax_t
和 uintmax_t
是支持的最大宽度的有符号和无符号整数。换句话说,它们是限制最大的整数类型。
这些类型的限制使用以下格式的宏定义
INTMAX_MAX
是intmax_t
有符号版本的最大值(9,223,372,036,854,775,807 [263 − 1] 或更大)。INTMAX_MIN
是intmax_t
有符号版本的最小值(−9,223,372,036,854,775,807 [−263 + 1] 或更小)。UINTMAX_MAX
是uintmax_t
无符号版本的最大值(18,446,744,073,709,551,615 [264 − 1] 或更大)。
还定义了用于转换常量十进制、八进制或十六进制值的宏,这些值将适合相应的类型。
INTMAX_C(value)
将被替换为适合intmax_t
的值。UINTMAX_C(value)
将被替换为适合uintmax_t
的值[1]。
PTRDIFF_MIN
是ptrdiff_t
的最小值。PTRDIFF_MAX
是ptrdiff_t
的最大值。SIZE_MAX
是size_t
的最大值(216 − 1 或更大)。WCHAR_MIN
是wchar_t
的最小值。WCHAR_MAX
是wchar_t
的最大值。WINT_MIN
是wint_t
的最小值。WINT_MAX
是wint_t
的最大值。SIG_ATOMIC_MIN
是sig_atomic_t
的最小值。SIG_ATOMIC_MAX
是sig_atomic_t
的最大值。
- 一些(不符合标准的)实现将 C99 支持附加到 C89 运行时库之上。[需要引用] 这样做的结果之一是,新的
printf
和scanf
指定符不被识别,可能会导致一些未定义的行为。解决此问题的典型方法是- 最常见(也是最错误)的方法是使用
long
或unsigned long
类型作为中间步骤,并将这些类型传递给printf
或scanf
。对于小于 32 位的精确、最小和快速整数类型,这种方法可以正常工作,但在ptrdiff_t
和size_t
以及大于 32 位的类型(通常在使用 32 位long
和 64 位指针的平台上)可能会出现问题。 - 不直接使用
scanf
,而是手动读入缓冲区,调用strto<i|u>max
,然后将其转换为所需类型。不过,这不能帮助打印整数。 - 使用与 C99 兼容的第三方
printf
和scanf
库。 - 使用 C99 标准打印格式指定符。例如 PRId64。这些指定符在 inttypes.h 中声明。
- 最常见(也是最错误)的方法是使用
- 整数等级和对应整数类型的规则可能会迫使实现者在不支持整数类型、做出糟糕的妥协或以不符合标准的方式支持整数类型之间做出两难选择。
- 例如,有些机器要么对极大的有符号整数寄存器提供特殊支持,要么对极大的无符号整数寄存器提供特殊支持,而没有对其他类型提供支持。[需要引用] 实现可以选择不将此功能暴露给 C 实现,合成一个缓慢的类型作为对应的整数类型,合成一个奇怪的对应整数类型,或者将整数暴露给程序员,而不将其设置为
[u]intmax_t
类型或合成一个对应的整数类型。
- 例如,有些机器要么对极大的有符号整数寄存器提供特殊支持,要么对极大的无符号整数寄存器提供特殊支持,而没有对其他类型提供支持。[需要引用] 实现可以选择不将此功能暴露给 C 实现,合成一个缓慢的类型作为对应的整数类型,合成一个奇怪的对应整数类型,或者将整数暴露给程序员,而不将其设置为
[u]intN_t
类型是在希望拥有保证的二进制补码整数类型和希望拥有保证的没有填充位的类型之间的折衷方案(与更细粒度的方案相比,后者将定义更多类型)。由于[u]intN_t
类型采取 "全有或全无" 的方式,因此根据它们是否关心速度、程序员的便利性或标准符合性,实现可能不得不玩上面描述的同类游戏。
- ↑ a b c d e f g h 从技术上讲,它实际上允许 0 个或多个值位,但你构建它的唯一方法是使用一个有符号整数的单比特位域
- ↑ http://www.tuxgraphics.org/common/src2/article09043/avr-libc-user-manual-1.6.4/group__avr__stdint.html
- ↑ http://linux.die.net/man/3/intptr_t
- : 整数类型 – 基础定义参考,单一UNIX®规范,第7版来自开放组
由于stdint.h没有随旧版本的C++编译器和Visual Studio C++产品(早于Visual Studio 2010)一起提供,因此可以使用第三方实现
- pstdint.h – 来自Paul Hsieh的跨平台、免费实现。此实现已在以下编译器上进行测试,所有编译器在各自最高设置下均无警告:Borland Turbo C 2.0、WATCOM C/C++ 11.0(16位和32位)、Microsoft Visual C++ 6.0(32位)、Microsoft Visual Studio.net(VC7)、Intel C++ 4.0、GNU gcc v3.3.3。
- msinttypes – 适用于Microsoft Visual Studio的ISO C9x兼容stdint.h和inttypes.h可在Google Code上获得。此实现已在Microsoft Visual Studio 6.0、Microsoft Visual Studio .NET 2003、Microsoft Visual Studio 2005和Microsoft Visual Studio 2008中进行测试。
- boost/cstdint.hpp – 此文件来自Boost库,包含stdint.h中的数据类型。使用<boost/cstdint.hpp>的优势在于它可以在许多平台上使用。代码自然可移植,只要可以使用boost库,就可以在任何平台上进行编译而无需更改。