据我了解,int
最初应该是“本机”整数类型,并另外保证它的大小至少应为16位-那时该大小被认为是“合理”的。
当32位平台变得更加普遍时,我们可以说“合理”的大小已更改为32位:
- 现代Windows
int
在所有平台上都使用32位。
- POSIX保证
int
至少为32位。
- C#,Java的类型
int
可以保证精确为32位。
但是当64位平台成为标准时,int
由于以下原因,没有人将其扩展为64位整数:
- 可移植性:很多代码取决于
int
32位的大小。
- 内存消耗:
int
在大多数情况下,将每种内存使用量翻倍可能是不合理的,因为在大多数情况下,使用的数量远小于20亿。
现在,你为什么会喜欢uint32_t
到uint_fast32_t
?出于相同的原因,C#和Java也总是使用固定大小的整数:程序员不会考虑考虑不同类型的可能大小而编写代码,而是为一个平台编写并在该平台上测试代码。大多数代码隐式依赖于数据类型的特定大小。这就是为什么uint32_t
在大多数情况下是更好的选择的原因-它不允许对其行为有任何歧义。
此外,uint_fast32_t
在大小等于或大于32位的平台上,真的是最快的类型吗?并不是的。考虑Windows上的GCC for x86_64的以下代码编译器:
extern uint64_t get(void);
uint64_t sum(uint64_t value)
{
return value + get();
}
生成的程序集如下所示:
push
sub $0x20,
mov
callq d <sum+0xd>
add
add $0x20,
pop
retq
现在,如果将get()
的返回值更改为uint_fast32_t
(在Windows x86_64上为4字节),则会得到以下信息:
push %rbx
sub $0x20,%rsp
mov %rcx,%rbx
callq d <sum+0xd>
mov %eax,%eax ; <-- additional instruction
add %rbx,%rax
add $0x20,%rsp
pop %rbx
retq
注意,除了mov %eax,%eax
函数调用后的附加指令(该指令将32位值扩展为64位值)外,生成的代码几乎相同。
如果仅使用32位值,则不会出现此类问题,但是您可能会使用带有size_t
变量的值(可能是数组大小?),而在x86_64上是64位。在Linux上uint_fast32_t
是8个字节,因此情况有所不同。
许多程序员int
在需要返回较小值时使用它们(例如,在[-32,32]范围内)。如果int
使用平台本机整数大小,这将是完美的选择,但是由于它不在64位平台上,因此与平台本机类型匹配的另一种类型是更好的选择(除非它经常与其他较小大小的整数一起使用)。
基本上,无论标准说什么,uint_fast32_t
总之在某些实现上都是破坏的。如果您关心某些地方生成的其他指令,则应定义自己的“本机”整数类型。或者,您可以size_t
为此目的使用它,因为它通常会匹配native
大小(我不包括像8086这样古老而晦涩的平台,仅包括可以运行Windows,Linux等的平台)。
另一个int
应该显示为本地整数类型的标志是“整数提升规则”。大多数CPU只能在本机上执行操作,因此32位CPU通常只能执行32位加法,减法等操作(Intel CPU在这里是一个例外)。仅通过加载和存储指令支持其他大小的整数类型。例如,应使用适当的“加载8位有符号”或“加载8位无符号”指令加载8位值,并在加载后将值扩展为32位。没有整数提升规则,C编译器将不得不为使用小于本地类型的类型的表达式添加更多代码。不幸的是,这在64位体系结构上已不再适用,因为在某些情况下编译器现在必须发出其他指令(如上所示)。