c++ - 在x86_64模式下,寄存器中无法容纳64位数字

标签 c++ c gcc assembly cpu-registers

我在我的电脑上有一个Intel处理器,它以64位模式x86_64运行,如果我使用字寄存器或使用某些标志优化功能,则寄存器的大小为64位,该变量往往放在寄存器中,但是,如果我将值设置为高于32位,则编译器会抱怨,即我的int不是64位,为什么会发生这种情况?如果将变量放在寄存器中,那不是64位,我什至没有得到它的内存地址?也就是说,它甚至没有放置在堆栈上。

#include <stdio.h>

int main(void) {

    register int x = 4294967296;

    return 0;
}

编译:

gcc example.c -o example -Wall -Wextra



输出:

warning: overflow in implicit constant conversion [-Woverflow] register int x = 4294967296;

最佳答案

C register关键字不执行任何操作来覆盖您选择的C类型的宽度。 int是所有32位和64位x86 ABI(包括x86-64 System V和Windows x64)中的32位类型。 (long在Windows x64上也是32位,但在Linux / Mac /其他x86-641上是64位。)

register int仍然是int ,但必须遵守INT_MAXINT_MIN和有符号溢出的所有限制,这些行为均未定义。 不会将您的C源代码转换为可移植的汇编语言。

使用register只是告诉编译器,即使在 Debug模式下,也可以将变量保留在寄存器的下半部分中。

If a register has 64 bits, why should the convention of int be 32 bits



int是32位的,因为没人希望int的数组在64位计算机上成为64位,并且具有两倍的缓存占用空间。

我不知道int = int64_t的任何C实现,即使在x86-64之外也是如此。即使是DEC Alpha(积极使用64位,并从头开始为64位设计),我认为仍然使用32位int。

int从16位机器增加到32位机器时,使其成为32位是有意义的,因为16位有时​​“太小”。 (但是请记住,ISO C仅保证int至少为16位。如果您需要更多,则在真正可移植的程序中,最好使用longint_least32_t。)

但是对于大多数程序而言,32位“足够大”,并且64位计算机始终具有快速的32位整数,因此当从32位计算机迁移到64位计算机时,int保持32位。

在某些计算机上,不是很好地支持16位整数。例如在MIPS上使用uint16_t实现到16位换行将需要额外的AND指令。因此,在那里将int设置为16位类型将是一个糟糕的选择。

在x86上,您可以仅使用16位操作数大小,并在复制时使用movzx而不是mov,但是在32位计算机上int为32位是“正常的”,因此x86 32位ABI都选择了该选项。

当ISA从32位扩展到64位时,与16-> 32情况不同,没有零性能的原因使int更宽。 (在那种情况下,short仍保留16位,因此即使在C99 stdint.h存在之前,也都有16位和32位整数的类型名)。

在x86-64上,默认的操作数大小仍为32位。 mov rax, rcxmov eax, ecx相比,需要额外的前缀字节(REX.W),因此32位的效率稍高。而且,在某些CPU上,64位乘法略慢,即使在当前的Intel CPU上,64位除法也比32位慢得多。 The advantages of using 32bit registers/instructions in x86-64

另外,如果编译器根本想提供可选的int32_t,则需要int32_t的原始类型。 (固定宽度2的补码类型是可选的,与int_least32_t等不同,它不能保证是2的补码或没有填充。)

带有16位short和64位int的编译器可以具有特定于实现的类型名称,例如__int32,它们用作int32_t / uint32_t的typedef,因此此参数不是总的toptop。但这很奇怪。

当从16增长到32时,将int更改为比ISO C最小值更宽是有意义的,因为您仍然将short作为16位的名称。 (此参数不是很好,因为在32位系统上,确实有long作为32位整数的名称。)

但是,当增长到64位时,您希望某种类型成为32位整数类型。 (而且long不能比int窄)。 char / short / int / long(或long long)涵盖所有4种可能的操作数大小。不一定保证_int32_t在所有系统上都可用,因此,如果每个人都希望使用32位带符号整数,则希望每个人都使用它不是可移植代码的可行选择。

脚注1 :

您可以用任何一种方法争论long是32位还是64位类型是否更好。微软选择将其保留为32位意味着使用long的结构布局可能不会在32位和64位代码之间改变(但是如果包含指针,它们会改变)。

ISO C要求long至少为32位类型(实际上,它们是根据可以表示的最小值和最大值来定义它的,但在其他地方,它们确实要求整数类型是带有可选填充的二进制整数)。

无论如何,有些代码使用long,因为它需要32位类型,但不需要64位类型。在许多情况下,更多的比特并没有更好,只是不需要它们。

在像x86-64 System V这样的单个ABI中,总是很长的长度与指针一样宽是不方便的,但是由于可移植代码始终需要根据用例使用unsigned long longuint64_tuint_least64_tuintptr_t,因此x86-64 System V选择64位长可能是一个错误。

OTOH,对于本地人来说,更广泛的类型有时可以通过在索引指针时避免符号扩展来保存指令,但是有符号溢出是未定义的行为这一事实通常使编译器在方便时可以扩展asm中的int

关于c++ - 在x86_64模式下,寄存器中无法容纳64位数字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55100284/

相关文章:

C++重载函数问题

c++ - 为什么在 C 中减去 '0' 会导致 char 所代表的数字?

c++ - int(int, int) 样式模板函数类型语法

c++ - 检查是否可以删除驱动器

c++ - 静态变量初始化顺序

c++ - 具有父命名空间的嵌套命名空间中的重载解析

c - TCP 套接字 : Can read() still fail with EINTR when select() indicates there are data available?

c - 从将其内存分配给同一函数的函数返回字符串是否安全/正确?

c - 用C语言读写32位wav文件

gcc - 相对路径与使用 $ORIGIN 作为 RPATH 的区别