[英]Why doesn't the compiler know the addresses of local variables at compile-time?
以下陳述是什么意思?
本地和動態分配的變量具有編譯源文件時編譯器不知道的地址
我曾經認為局部變量在編譯時被分配地址,但是當它超出范圍然后在函數調用期間再次進入范圍時,該地址可以改變。 但是上面的聲明說編譯器不知道局部變量的添加。 然后如何分配局部變量? 為什么在編譯時可以知道全局變量的地址?
另外,您能否提供一個很好的鏈接來閱讀如何分配局部變量和其他變量?
提前致謝!
上面的引用是正確的 - 編譯器通常在編譯時不知道局部變量的地址。 也就是說,編譯器可能知道從局部變量所在的堆棧幀的基礎偏移,但是根據調用堆棧的深度,可能會在運行時轉換為不同的地址。 作為一個例子,考慮這個遞歸代碼(順便說一句,它不是任何好的代碼!):
int Factorial(int num) {
int result;
if (num == 0)
result = 1;
else
result = num * Factorial(num - 1);
return result;
}
根據參數num
,此代碼最終可能會進行多次遞歸調用,因此內存中將有多個result
副本,每個副本都包含不同的值。 因此,編譯器無法知道它們的全部內容。 但是,每個result
實例可能會從包含每個Factorial
調用的堆棧幀的基礎偏移相同的數量,但理論上編譯器可能會執行其他操作,例如優化此代碼,以便只有一個result
副本。
通常,編譯器通過維護堆棧幀的模型並跟蹤堆棧幀中下一個空閑位置的位置來分配局部變量。 這樣,可以相對於堆棧幀的開始分配局部變量,並且當調用函數時,可以使用相對地址,結合堆棧地址,在特定堆棧幀中查找該變量的位置。
另一方面,全局變量可以在編譯時知道它們的地址。 它們與本地人的不同之處主要在於程序中始終存在一個全局變量的副本。 局部變量可能存在0次或更多次,具體取決於執行的方式。 由於存在一個唯一的全局副本,編譯器可以對其進行硬編碼。
至於進一步閱讀,如果你想對編譯器如何布局變量進行相當深入的處理,你可能想要獲取Aho,Lam的編譯器:原理,技術和工具,第二版的副本, Sethi和Ullman。 雖然本書的大部分內容涉及其他編譯器構造技術,但本書的大部分內容專門用於實現代碼生成和可用於改進生成代碼的優化。
希望這可以幫助!
在我看來,該聲明並不是在討論變量或范圍的運行時訪問,而是試圖說一些更微妙的東西。
這里的關鍵是它的“本地和動態分配”和“編譯時間”。 我相信該聲明所說的是這些地址不能用作編譯時常量。 這與靜態分配的變量的地址形成對比,靜態分配的變量可以用作編譯時常量。 其中一個例子是模板:
template<int *>
class Klass
{
};
int x;
//OK as it uses address of a static variable;
Klass<&::x> x_klass;
int main()
{
int y;
Klass<&y> y_klass; //NOT OK since y is local.
}
對於不允許編譯的模板,似乎存在一些額外的約束:
int main()
{
static int y;
Klass<&y> y_klass;
}
但是,使用編譯時常量的其他上下文可能能夠使用&y
。
同樣地,我希望這是無效的:
static int * p;
int main()
{
p = new int();
Klass<p> p_klass;
}
由於p的數據現在是動態分配的(即使p是靜態的)。
例如:
void bar(); // forward declare
void foo ()
{
int i; // 'i' comes before 'j'
bar();
}
void bar ()
{
int j; // 'j' comes before 'i'
foo();
}
int main ()
{
if(...)
foo();
else
bar();
}
if
條件可以為true
或false
,結果僅在運行時知道。 基於int i
或int j
將在堆棧上的適當偏移處發生。
這是一個很好的問題。
執行代碼時,程序被加載到內存中。 然后局部變量獲取地址。 在編譯時,源代碼被轉換為機器語言代碼,以便可以執行
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.