![](/img/trans.png)
[英]C++ unix 32bit to Red Hat Linux running on an x86_64 chip (64-bit server)
[英]a 64-bit number does not fit in the register int in x86_64 mode
我的電腦上有一個英特爾處理器,在 64 位模式 x86_64 下運行,其中寄存器的大小為 64 位,如果我使用字寄存器,或者使用一些標志優化,變量往往被放置在寄存器中,但是如果我把一個值放在 32 位以上,編譯器就會抱怨,即我的 int 不是 64 位,為什么會發生這種情況? 如果變量放在寄存器中,我什至沒有得到它的內存地址,它就不是64位嗎? 也就是說,它甚至不放在堆棧上。
#include <stdio.h>
int main(void) {
register int x = 4294967296;
return 0;
}
編譯:
gcc 示例.c -o 示例 -Wall -Wextra
輸出:
警告:隱式常量轉換中溢出 [-Woverflow] 寄存器 int x = 4294967296;
C register
關鍵字不會覆蓋您選擇的 C 類型的寬度。 int
在所有 32 位和 64 位 x86 ABI 中都是 32 位類型,包括 x86-64 System V 和 Windows x64。 ( long
在 Windows x64 上也是 32 位,但在 Linux / Mac / 其他所有 x86-64 1上是 64 位。)
register int
仍然是int
,受制於INT_MAX
、 INT_MIN
所有限制,並且有符號溢出是未定義的行為。 它不會將您的 C 源代碼變成可移植的匯編語言。
使用register
只是告訴編譯器阻止您獲取變量的地址,因此即使在調試模式下(使用最少的優化),一個非常幼稚的編譯器也可以將變量保存在寄存器(的低半部分)中,而不會在稍后遇到任何意外功能。 (當然,現代編譯器通常不需要這種幫助,但對於某些register
實際上在調試模式下確實有影響。)
如果寄存器有 64 位,為什么 int 的約定應該是 32 位
int
是 32 位的,因為沒有人希望int
數組在 64 位機器上變成 64 位,緩存占用空間是原來的兩倍。
極少數 C 實現具有int
= int64_t
(例如在某些 Cray 機器上,我想我已經讀過),但即使在 x86-64 之外,其中 32 位是“自然”和最有效的操作數大小也極為罕見對於機器碼。 即使是 DEC Alpha(它是積極的 64 位並且從頭開始為 64 位設計)我認為仍然使用 32 位 int。
從 16 位機器增長到 32 位機器時將int
設為 32 位是有意義的,因為有時 16 位“太小”了。 (但請記住,ISO C 僅保證int
至少為 16 位。如果您需要更多,在真正可移植的程序中,您最好使用long
或int_least32_t
。)
但是 32 位對於大多數程序來說“足夠大”,並且 64 位機器總是具有快速的 32 位整數,因此當從 32 位機器移動到 64 位機器時int
保持 32 位。
在某些機器上,16 位整數沒有得到很好的支持。 例如,在 MIPS 上使用uint16_t
實現 16 位的包裝將需要額外的 AND 立即指令。 因此,將int
16 位類型將是一個糟糕的選擇。
在 x86 上,您可以只使用 16 位操作數大小,並在復制時使用movzx
而不是mov
,但int
在 32 位機器上為 32 位是“正常的”,因此 x86 32 位 ABI 都選擇了它。
當 ISA 從 32 位擴展到 64 位時,與 16->32 的情況不同,使int
更寬的性能原因為零。 (同樣在這種情況下, short
保持在 16 位,因此 16 位和 32 位整數都有一個類型名,甚至在 C99 stdint.h
存在之前)。
在 x86-64 上,默認操作數大小仍然是 32 位; mov rax, rcx
與mov eax, ecx
相比需要一個額外的前綴字節 (REX.W),因此 32 位的效率稍高一些。 此外,64 位乘法在某些 CPU 上稍慢,即使在當前的 Intel CPU 上,64 位除法也明顯比 32 位慢。 在 x86-64 中使用 32 位寄存器/指令的優點
此外,編譯器需要int32_t
的原始類型,如果他們想提供可選的int32_t
的話。 (固定寬度 2 的補碼類型是可選的,不像int_least32_t
等不能保證是 2 的補碼或沒有填充。)
具有 16 位short
和 64 位int
編譯器可能有一個特定於實現的類型名稱,如__int32
,它們用作int32_t
/ uint32_t
的 typedef,因此此參數並不是一個完整的展示器。 但這會很奇怪。
當從 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 位類型更好。 Microsoft 選擇將其保持為 32 位意味着使用long
結構布局可能不會在 32 位和 64 位代碼之間更改(但如果它們包含指針,則它們會更改)。
ISO C 要求long
至少是 32 位類型(實際上他們根據可以表示的最小值和最大值來定義它,但在其他地方他們確實要求整數類型是帶有可選填充的二進制整數)。
無論如何,有些代碼使用long
是因為它需要 32 位類型,但不需要 64; 在許多情況下,更多的位並不是更好,只是不需要它們。
在像 x86-64 System V 這樣的單個 ABI 中,始終將 long 與指針的寬度相同是半方便的,但是由於可移植代碼始終需要根據用例使用unsigned long long
或uint64_t
或uint_least64_t
或uintptr_t
,x86-64 System V 選擇 64 位長可能是錯誤的。
OTOH,更廣泛的局部類型有時可以通過在索引指針時避免符號擴展來保存指令,但有符號溢出是未定義行為的事實通常讓編譯器在方便時在 asm 中加寬int
。
register
關鍵字在這里無關緊要; int
數據類型在相關平台上保持 32 位。
#include <stdio.h>
#include <stdint.h>
int main(void)
{
int64_t x = 4294967296;
return 0;
}
該register
也無關緊要,因為它幾乎肯定會被忽略。 無論顯式指令如何,編譯器都會在有能力和有利的情況下使用寄存器存儲,同樣也可能不會。
在您的平台上int
可能是 32 位。
您要求編譯器將一個值 ( 4294967296
= 0x100000000
) 放入寄存器中,該值不能以 32 位的形式出現。
register int x = 4294967296;
但是無論如何,即使您刪除了register
關鍵字,編譯器仍然會出於同樣的原因而抱怨。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.