C 有各种小的、大一点的和最大的整数类型,比如 int、long 之类。你可以查看讲限制的那一节,看看用 INT_MAX 等宏能得到的最大的 int 是多少。
这些类型有多大?也就是说,它们占多少个字节?我们可以用 sizeof 得到答案。
但如果我想反过来呢?如果我需要一个正好 32 位(4 字节)的类型,或者至少 16 位之类的类型呢?
我们怎样才能声明一个特定大小的类型呢?
头文件 <stdint.h> 给了我们办法。
无论有符号还是无符号整数,我们都能指定一个特定位数的类型,当然,这有一些注意事项。
这些类型主要有三大类(在这些示例中,N 会替换成具体的位数):
intN_t)int_leastN_t)int_fastN_t)188fast 到底快多少?肯定也许快了那么一些吧。大概吧。规范并没有说快多少,只说它们在这套架构上是最快的。不过大多数 C 编译器都相当不错,所以你大概只会在需要保证尽可能快的场合才见到它(而不是只指望编译器生成相当快的代码——而它确实能做到)。
最后,这些无符号类型会在前面加一个 u 来区分。
例如,这些类型分别具有如下含义:
int32_t w; // w 恰好是 32 位,有符号
uint16_t x; // x 恰好是 16 位,无符号
int_least8_t y; // y 至少是 8 位,有符号
uint_fast64_t z; // z 是最快的表示形式,至少 64 位,无符号以下类型保证会被定义:
int_least8_t uint_least8_t
int_least16_t uint_least16_t
int_least32_t uint_least32_t
int_least64_t uint_least64_t
int_fast8_t uint_fast8_t
int_fast16_t uint_fast16_t
int_fast32_t uint_fast32_t
int_fast64_t uint_fast64_t也可能还有其他不同宽度的类型,但那些都是可选的。
嘿!像 int16_t 这样的定宽类型哪儿去了?原来它们完全是可选的……除非满足某些条件189。如果你用的是普普通通的现代计算机系统,那些条件多半是满足的。而如果满足,你就会有这些类型:
int8_t uint8_t
int16_t uint16_t
int32_t uint32_t
int64_t uint64_t其他不同宽度的变体也可能被定义,但它们是可选的。
有一种类型可以保存系统上能表示的最大整数,有符号和无符号的都有:
intmax_t
uintmax_t
想尽可能取大值时,就用这些类型。
显然,同符号的任何其他整数类型的值都必然能放进这个类型。
如果你有一个常量,希望它能放进特定的位数里,可以用这些宏自动给数字加上合适的后缀(例如 22L 或 3490ULL)。
INT8_C(x) UINT8_C(x)
INT16_C(x) UINT16_C(x)
INT32_C(x) UINT32_C(x)
INT64_C(x) UINT64_C(x)
INTMAX_C(x) UINTMAX_C(x)
再次强调,这些宏只对整数常量值有效。
例如,我们可以用其中一个宏像这样给常量赋值:
uint16_t x = UINT16_C(12);
intmax_t y = INTMAX_C(3490);
我们还定义了一些限制,让你能取得这些类型的最大值和最小值:
INT8_MAX INT8_MIN UINT8_MAX
INT16_MAX INT16_MIN UINT16_MAX
INT32_MAX INT32_MIN UINT32_MAX
INT64_MAX INT64_MIN UINT64_MAX
INT_LEAST8_MAX INT_LEAST8_MIN UINT_LEAST8_MAX
INT_LEAST16_MAX INT_LEAST16_MIN UINT_LEAST16_MAX
INT_LEAST32_MAX INT_LEAST32_MIN UINT_LEAST32_MAX
INT_LEAST64_MAX INT_LEAST64_MIN UINT_LEAST64_MAX
INT_FAST8_MAX INT_FAST8_MIN UINT_FAST8_MAX
INT_FAST16_MAX INT_FAST16_MIN UINT_FAST16_MAX
INT_FAST32_MAX INT_FAST32_MIN UINT_FAST32_MAX
INT_FAST64_MAX INT_FAST64_MIN UINT_FAST64_MAX
INTMAX_MAX INTMAX_MIN UINTMAX_MAX
注意所有无符号类型的 MIN 都是 0,因此也就不存在对应的宏。
要打印这些类型,必须给 printf() 传正确的格式说明符。(用 scanf() 获取输入时也有同样的问题。)
但你怎么知道这些类型在底层是多大的呢?幸运的是,C 又一次提供了一些宏来帮忙。
所有这些都在 <inttypes.h> 里。
现在我们有了一大堆宏,简直是宏的复杂度爆炸。所以我就不再逐一列出了,只在本该根据你的需要填 8、16、32 或 64 的地方放一个小写字母 n。
来看看用于打印有符号整数的宏:
PRIdn PRIdLEASTn PRIdFASTn PRIdMAX
PRIin PRIiLEASTn PRIiFASTn PRIiMAX留意其中的规律。你能看到定宽、least、fast 和 max 几种类型的变体。
此外还有小写的 d 和小写的 i,它们分别对应 printf() 的格式说明符 %d 和 %i。
所以如果我有一个如下类型的值:
int_least16_t x = 3490;我就可以用 PRIdLEAST16,以等价于 %d 的格式说明符把它打印出来。
但怎么做?我们究竟该怎么用那个宏?
首先,那个宏给出的字符串里,包含 printf() 打印该类型时需要用到的那个或那几个字母。比如,它可能是 "d" 或 "ld"。
所以我们只要把它嵌进传给 printf() 调用的格式字符串里就行了。
为此,我们可以利用 C 的一个你也许已经忘了的事实:相邻的字符串字面量会自动拼接成单个字符串。例如:
printf("Hello, " "world!\n"); // 打印 "Hello, world!"既然这些宏都是字符串字面量,我们就可以像这样使用它们:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main(void)
{
int_least16_t x = 3490;
printf("The value is %" PRIdLEAST16 "!\n", x);
}
我们还有一大堆用于打印无符号类型的宏:
PRIon PRIoLEASTn PRIoFASTn PRIoMAX
PRIun PRIuLEASTn PRIuFASTn PRIuMAX
PRIxn PRIxLEASTn PRIxFASTn PRIxMAX
PRIXn PRIXLEASTn PRIXFASTn PRIXMAX在这里,o、u、x 和 X 分别对应 printf() 文档中说明的格式说明符。
和之前一样,其中的小写 n 应替换为 8、16、32 或 64。
可就在你以为宏已经够多的时候,原来我们还有一整套与之配套的宏,专供 scanf() 使用!
SCNdn SCNdLEASTn SCNdFASTn SCNdMAX
SCNin SCNiLEASTn SCNiFASTn SCNiMAX
SCNon SCNoLEASTn SCNoFASTn SCNoMAX
SCNun SCNuLEASTn SCNuFASTn SCNuMAX
SCNxn SCNxLEASTn SCNxFASTn SCNxMAX
记住:当你想用 printf() 或 scanf() 打印一个固定大小的整数类型时,要从 <inttypes.h> 里取出对应的正确格式说明符。