我用在線編譯器寫了一個簡單的c 代碼:
int main()
{
int a = 4;
int&& b = 2;
}
以及由gcc 11.20編譯的匯編代碼的主要功能部分如下所示
main:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], 4
mov eax, 2
mov DWORD PTR [rbp-20], eax
lea rax, [rbp-20]
mov QWORD PTR [rbp-16], rax
mov eax, 0
pop rbp
ret
我注意到在初始化“a”時,該指令只是簡單地將立即運算元直接移動到記憶體,而對于 r 值參考“b”,它首先將立即值存盤到暫存器 eax,然后將其移動到記憶體,還有是 [rbp-8] ~ [rbp-4] 之間未使用的記憶體,我認為無論是什么直接值,它們都只是存在,所以它必須在某個地方,或者它只是簡單地使用信號來初始化(我的猜測),我想了解更多底層邏輯。
所以我的問題是:
- 為什么 inilization 不同?
- 為什么堆疊上有一個空的 4 位元組未使用記憶體?
uj5u.com熱心網友回復:
我先說第二個問題。
請注意,此函式中實際上定義了三個物件:int變數a、參考b(實作為指標)和具有指向的值的未命名臨時int物件。在未優化的編譯中,這些物件中的每一個都需要存盤在堆疊上的某個唯一位置,并且編譯器天真地分配堆疊空間,逐個處理變數并在前一個下方分配每個空間。它顯然選擇按以下順序處理它們:2b
變數
a,int需要 4 個位元組。它進入第一個可用的堆疊槽,位于[rbp-4]。參考
b,存盤為需要 8 個位元組的指標。您可能認為它會在[rbp-12],但 x86-64 ABI 要求指標自然對齊在 8 位元組邊界上。所以編譯器再向下移動 4 個位元組來實作這種對齊,把bat[rbp-16].[rbp-8]到目前為止,4 個位元組尚未使用。臨時的
int,也需要 4 個位元組。編譯器將它放在先前放置的變數 at 的正下方[rbp-20]。的確,那里有空間[rbp-8]可以用來代替,這樣會更有效率;但是由于您告訴編譯器不要優化,它不會執行此優化。如果您使用其中一個-O標志,它會。
至于為什么a使用立即存盤到記憶體進行初始化,而臨時是通過暫存器初始化:要真正回答這個問題,您必須閱讀 GCC 源代碼的詳細資訊,坦率地說,我認為您不會發現背后有什么非常有趣的東西。據推測,編譯器中有不同的代碼路徑用于創建和初始化命名變數與臨時變數,而臨時變數的代碼可能恰好被撰寫為兩個步驟。
可能是為了方便,程式員選擇在中間表示(GIMPLE 或 RTL)中創建一個額外的物件,可能是因為它簡化了編譯器代碼以處理更一般的情況。他們不會費力避免這種情況,因為他們知道以后的優化程序會清理它。但是,如果您關閉了優化,則不會發生這種情況,并且會為這種不必要的傳輸發出實際指令。
uj5u.com熱心網友回復:
在
int a = 4;
您宣告一個(通常)4 位元組變數并要求編譯器用 4 的位表示填充它。在
int&& b = 2;
您宣告了對什么的參考(“r 值參考”)?字面意思?可能嗎?在 C 中,參考通常在匯編級別被翻譯成指標。因此可以預期這b將是“變相的指標”,即沒有*and->語意。但它可能會在 64 位機器上占用 64 位。現在,指標必須指向存盤在 RAM 中的一些記憶體,而不是暫存器、快取等。因此編譯器很可能會創建一個臨時(未命名)整數,用 2 對其進行初始化,然后將其地址系結到b. 我寫“最有可能”是因為我懷疑標準是否如此詳細地標準化了這一點。我們可以肯定的是,在 in 的初始化中涉及一個額外的未命名 b變數int&& b = 2;。
至于匯編器,我對它的了解太少,不敢向你解釋什么。但是,我想臨時變數和&&參考后面的指標的概念可以解決您的所有問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/454204.html
下一篇:如何從打字稿中的物件中洗掉所有鍵
