跳转到内容

C 编程/stdint.h

来自维基教科书,自由的教科书

stdint.h头文件C 标准库 中引入 C99 标准库 第 7.18 节,以允许程序员通过提供一组类型定义来编写更可移植的代码,这些类型定义指定了固定宽度的整型,以及使用宏定义的每个类型的允许的最小值和最大值[1]。此头文件对于嵌入式编程特别有用,嵌入式编程通常涉及对硬件特定 I/O 寄存器的相当多的操作,这些寄存器需要固定宽度的整数数据、特定位置和精确的对齐方式。stdint.h(用于 C 或 C++)cstdint(用于 C++) 可以下载或快速创建,如果它们没有提供。

固定宽度整型的命名约定是 intN_t 用于 带符号的 intuintN_t 用于 无符号的 int [1]。例如,int8_tuint64_t 以及其他一些可以声明,并定义其相应的范围 INT8_MININT8_MAX0(零)到 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>_tu<something>_t 必须是对应的带符号和无符号整型。对于标记为可选的类型,实现必须同时定义 <something>_tu<something>_t,或者两者都不定义。这些类型的限制应使用与下面描述的类似方式的宏来定义。

如果类型为 [u]<something>N_t(或类似于预处理程序定义),N 必须是正十进制整数,没有前导 0。

固定宽度整型

[编辑 | 编辑源代码]

这些类型为 intN_tuintN_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_tuint_leastN_t 的形式。int_leastN_t 是有符号整数,uint_leastN_t 是无符号整数[1]

标准要求这些类型的宽度大于或等于 N,并且没有更小的类型具有相同的符号并且具有 N 或更多位。例如,如果一个系统只提供 uint32_tuint64_tuint_least16_t 必须等价于 uint32_t

实现需要为以下 N 定义这些类型:8、16、32、64。其他任何 N 都是可选的。

这些类型的限制使用以下格式的宏定义:

  • INT_LEASTN_MAXint_leastN_t 的有符号版本的最大值 (2N−1 − 1 或更大) 。
  • INT_LEASTN_MINint_leastN_t 的有符号版本的最小值 (−2N−1 + 1 或更小) 。
  • UINT_LEASTN_MAXuint_leastN_t 的无符号版本的最大值 (2N − 1 或更大)。

stdint.h 还应该定义宏,这些宏将转换常量十进制、八进制或十六进制值,这些值保证适合相应的类型并且可以使用 #if

  • INTN_C(value) 被替换为适合 int_leastN_t 的值。例如,如果 int_least64_t 被“typedef”为 signed long long intINT64_C(123) 对应于 123LL
  • UINTN_C(value) 被替换为适合 uint_leastN_t 的值。

最快的最小宽度整数类型

[编辑 | 编辑源代码]

它们采用 int_fastN_tuint_fastN_t 的形式。

标准没有对这些类型进行任何强制要求,除了它们的宽度必须大于或等于 N。它还留给实现者决定“快速”整数类型意味着什么。

实现需要为以下 N 定义这些类型:8、16、32、64[2]

这些类型的限制使用以下格式的宏定义:

  • INT_FASTN_MAXint_fastN_t 的有符号版本的最大值 (2N−1 − 1 或更大) 。
  • INT_FASTN_MINint_fastN_t 的有符号版本的最小值 (−2N−1 + 1 或更小) 。
  • UINT_FASTN_MAXuint_fastN_t 的无符号版本的最大值 (2N − 1 或更大)[1]

足够大以容纳指针的整数

[编辑 | 编辑源代码]

intptr_tuintptr_t 分别是有符号和无符号整数,它们保证能够容纳指针的值。这两种类型都是可选的。

这些类型的限制使用以下宏定义:

  • INTPTR_MINintptr_t 的最小值 (−32,767 [−215 + 1] 或更小) 。
  • INTPTR_MAXintptr_t 的最大值 (32,767 [215 − 1] 或更大) 。
  • UINTPTR_MAXuintptr_t 的最大值 (65,535 [216 − 1] 或更大)[3]

最大宽度整数类型

[编辑 | 编辑源代码]

intmax_tuintmax_t 是有符号和无符号整数,它们具有最大的支持宽度。换句话说,它们是具有最大限制的整数类型。

这些类型的限制使用以下格式的宏定义:

  • INTMAX_MAXintmax_t 的有符号版本的最大值 (9,223,372,036,854,775,807 [263 − 1] 或更大) 。
  • INTMAX_MINintmax_t 的有符号版本的最小值 (−9,223,372,036,854,775,807 [−263 + 1] 或更小) 。
  • UINTMAX_MAXuintmax_t 的无符号版本的最大值 (18,446,744,073,709,551,615 [264 − 1] 或更大) 。

还定义了宏,这些宏将转换常量十进制、八进制或十六进制值,这些值将适合相应的类型。

  • INTMAX_C(value) 被替换为适合 intmax_t 的值。
  • UINTMAX_C(value) 被替换为适合 uintmax_t 的值[1]

其他整数限制

[编辑 | 编辑源代码]
  • PTRDIFF_MINptrdiff_t 的最小值。
  • PTRDIFF_MAXptrdiff_t 的最大值。
  • SIZE_MAXsize_t 的最大值 (216 − 1 或更大) 。
  • WCHAR_MINwchar_t 的最小值。
  • WCHAR_MAXwchar_t 的最大值。
  • WINT_MINwint_t 的最小值。
  • WINT_MAXwint_t 的最大值。
  • SIG_ATOMIC_MINsig_atomic_t 的最小值。
  • SIG_ATOMIC_MAXsig_atomic_t 的最大值。

批评和注意事项

[编辑 | 编辑源代码]
  • 一些(不符合标准的)实现将 C99 支持附加到 C89 运行时库的顶部。[需要引用] 这样做的后果之一是,新的 printfscanf 说明符不被识别,并且可能会导致一些未定义的行为。解决此问题的典型方法是
    • 最常见(也是最错误)的方法是使用 longunsigned long 类型作为中间步骤,并将这些类型传递给 printfscanf。这对于小于 32 位的精确、最小和快速整数类型来说效果很好,但可能会对 ptrdiff_tsize_t 以及大于 32 位的类型造成问题,通常在使用 32 位 long 和 64 位指针的平台上。
    • 不直接使用 scanf,而是手动读取缓冲区,调用 strto<i|u>max,然后将其转换为所需的类型。但这对打印整数没有帮助。
    • 使用与 C99 兼容的第三方 printfscanf 库。
    • 使用 C99 标准打印格式说明符。例如,PRId64。这些在 inttypes.h 中声明。
  • 整数等级和相应整数类型的规则可能会迫使实现者在不支持整数类型、做出糟糕的妥协或以不符合标准的方式支持整数类型之间选择两害相权取其轻。
    • 例如,有一些机器要么对极其大的有符号整数寄存器或极其大的无符号整数寄存器有特殊支持,而对另一种类型却没有支持。[需要引用] 实现可以选择不将其公开给 C 实现,合成一个缓慢的类型作为相应的整数类型,合成一个奇怪的相应整数类型,或者将整数公开给程序员而不将其设置为 [u]intmax_t 类型或合成一个相应的整数类型。
  • [u]intN_t 类型是对希望获得保证的二进制补码整数类型和希望获得保证的没有填充位的类型的折衷方案(与更细粒度的方案相比,更细粒度的方案将定义更多类型)。由于对 [u]intN_t 类型采取“全有或全无”的方法,实现可能不得不玩上面描述的同类游戏,具体取决于他们是否关心速度、程序员便利性或标准一致性。

说明和参考资料

[编辑 | 编辑源代码]
  1. a b c d e f g h 从技术上讲,它实际上允许 0 个或多个值位,但唯一可以构造它的方法是用有符号整数的单比特位域
  2. http://www.tuxgraphics.org/common/src2/article09043/avr-libc-user-manual-1.6.4/group__avr__stdint.html
  3. http://linux.die.net/man/3/intptr_t
  • stdint.h: 整数类型 – 基础定义参考,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.hinttypes.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 库,就可以在任何平台上编译,无需修改。
华夏公益教科书