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 的类型,那么它应将它们 typedef 为具有对应 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
- : 整数类型 – 基础定义参考,The Single UNIX® Specification,来自 The Open Group 的第 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 – 符合 ISO C9x 的 stdint.h 和 inttypes.h 可用于 Microsoft Visual Studio,在 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 库,就可以在任何平台上编译,无需修改。