C 不允許“遍歷定義:”
goto jumpover;
int something = 3;
jumpover:
std::cout << something << std::endl;
這將按預期引發錯誤,因為不會宣告(或定義)“某事”。
但是,我跳過了使用匯編代碼:
#include<iostream>
using namespace std;
int main(){
asm("\njmp tag\n");
int ptr=9000;//jumped over
cout << "Ran" << endl;
asm("\ntag:\n");
cout << ptr << endl;
return 0;
}
它列印9000
了,雖然該int ptr=9000;//jumped over
行沒有被執行,因為程式沒有列印Ran
。我預計它會在ptr
使用時導致記憶體損壞/未定義的值,因為記憶體沒有分配(盡管編譯器認為它是,因為它不理解 ASM)。它怎么知道ptr
是9000?
這是否意味著 ptr 是在開始時創建和分配的main()
(因此沒有跳過,由于一些優化或其他原因)或其他原因?
uj5u.com熱心網友回復:
asm()
GCC 不支持陳述句之間的跳轉;
您的代碼具有未定義的行為。 從字面上看,任何事情都可以發生。
后面沒有__builtin_unreachable()
,而且您甚至沒有使用asm goto("" ::: : "label")
(GCC 手冊)告訴它 asm 陳述句可能會或可能不會跳轉到的 C 標簽。
當您這樣做時,無論在實踐中使用不同版本的 gcc/clang 和不同的優化級別會發生什么,這都是優化器實際所做的任何事情的巧合/實作細節/結果。
例如,在啟用優化的情況下,它會假設int ptr=9000;
將到達該陳述句進行持續傳播,因為它允許假設執行出現在第一個 asm 陳述句的末尾。
您必須查看編譯器的完整 asm 輸出才能了解實際發生的情況。例如https://godbolt.org/z/MbGhEnK3b顯示 GCC -O0 和 -O2。有了-O0
你確實讓它讀取了未初始化的堆疊空間,因為它跳過了 a mov DWORD PTR [rbp-4], 9000
,并且-O2
你得到了 constant-propagation:mov esi, 9000
在call std::basic_ostream<char,...
運算子 <<(int) 多載之前。
因為沒有分配記憶體
它的空間實際上是在函式序言中分配的;編譯器不會在每次遇到范圍內的宣告時生成代碼來移動堆疊指標。它們在函式開始時分配一次空間。即使是一次性的 Tiny C 編譯器也以這種方式作業,而不是使用單獨push
的分配 初始化單獨的int
變數。push
(在某些情況下,這實際上是一個錯過的優化,當在一條指令中對 alloc init 有用時:什么 C/C 編譯器可以使用 push pop 指令來創建區域變數,而不僅僅是增加 esp 一次?)
甚至比大多數其他型別的 C 未定義行為更重要的是,編譯器實際上無法在運行時檢測到以警告您。 asm
陳述句只是將文本插入到 GCC 的 asm 輸出中,然后輸入到匯編器中。您需要準確地向編譯器描述 asm 的功能(使用約束等asm goto
),以便為編譯器提供足夠的資訊來圍繞您的 asm 陳述句生成正確的代碼。
GCC 不決議asm 模板中的指令,它只是將其直接復制到 asm 輸出中。(或者對于擴展 asm,將 等運算元替換為%0
根據%1
運算元約束生成的文本。)
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/437340.html