2012-02-08 99 views
8

以下聲明是什麼意思?編譯時爲什麼編譯器不知道局部變量的地址?

本地和動態分配的變量必須在源文件編譯

我曾經以爲,局部變量被分配在編譯時的地址不是由編譯器知道地址,但該地址可在函數調用期間它會超出範圍,然後再次進入範圍。但是上面的語句說本地變量的addresess是編譯器不知道的。那麼如何分配局部變量?爲什麼在編譯時可以知道全局變量的地址?

另外,你可以請提供一個很好的鏈接來閱讀局部變量和其他分配的方式嗎?

在此先感謝!

+5

從某種意義上說,只有在OS加載程序並在MMU中設置虛擬內存映射之後,纔會知道地址。編譯器知道佈局,即事物出現的順序,但一切都是從某個基地址開始測量的。對於靜態存儲持續時間,它是從二進制文件的加載地址開始測量的。對於自動存儲持續時間,它是從堆棧指針測量的。動態存儲的地址在運行時確定,但成員存儲在與動態地址偏移處。 – 2012-02-08 02:56:23

+0

@Ben:這是一個完全不同的問題。 – 2012-02-08 04:04:19

回答

13

以上引用是正確的 - 編譯器通常在編譯時不知道局部變量的地址。也就是說,編譯器可能知道本地變量所在的堆棧幀的偏移量,但取決於調用堆棧的深度,這可能會在運行時轉換爲不同的地址。作爲一個例子,考慮這個遞歸碼(其中,順便說一句,不以任何方式好代碼!):

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次或更多次。由於有一個唯一的全局副本,因此編譯器可以對其進行硬編碼。

至於進一步的閱讀,如果你想一個相當深度處理的編譯器是如何佈局的變量,你可能希望通過阿霍,林,塞西和烏爾曼回暖的Compilers: Principles, Techniques, and Tools, Second Edition副本。儘管本書的大部分內容涉及其他編譯器構建技術,但本書的很大一部分內容都致力於實現代碼生成和可用於改進生成代碼的優化。

希望這會有所幫助!

+5

*自動*,不*本地*(可能是靜態的) – 2012-02-08 03:06:40

+0

@ BenVoigt-我同意。我避免使用這個術語,因爲大多數初學程序員學習「本地」和「全局」而不是「自動」,「靜態」和「動態」,因爲前者更通用,後者通常只適用於C/C++ 。 – templatetypedef 2012-02-08 03:09:32

+0

@templatetypedef:我想我找到你了,但是請你指出user966379答案中的錯誤。他說:「在編譯時,源代碼被轉換成機器語言代碼,所以沒有地址是已知的。」以上 。 – 2012-02-08 11:50:11

0
  1. 動態變量的地址由於預期的原因未知,因爲它們是從內存池中動態分配的。
  2. 本地變量的地址是未知的,因爲它們駐留在 「堆棧」內存區域。基於代碼流的運行時間條件,程序的堆棧繞開 - 展開可以延遲。

例如:

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條件可以是truefalse並將結果僅在運行時是已知的。基於int iint j將發生在適當的偏移堆棧上。

+0

我想你回答說「局部變量地址在編譯時不知道」。但我問過爲什麼編譯器本身不知道地址。 – 2012-02-08 11:53:38

1

在我看來,聲明並不是在談論運行時訪問變量或作用域,而是試圖說些微妙的話。

這裏的關鍵是它的「本地和動態分配」和「編譯時間」。 我相信這句話是說這些地址不能用作編譯時間常量。這與靜態分配的變量的地址相反,它可以用作編譯時間常量。這方面的一個例子是在模板:

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是靜態的)。

+0

'Klass <&y>'在第二段中是合法的,很可能你的編譯器沒有完全實現標準。 「非模板非模板模板參數*的*模板參數*應該是以下之一:」...「一個常量表達式(5.19),指定具有靜態存儲持續時間和外部對象的地址或內部鏈接或「...(第14.3.2節'[temp.arg.nontype]')這是C++ 03的一個重大變化,它需要外部鏈接。 – 2012-02-08 04:10:36

+0

@BenVoigt我想在C++ 03模式下編譯。我很高興看到在C++ 11中對模板的一些限制已經減少了。 – 2012-02-08 05:19:08

0

這是一個很好的問題。

執行代碼時,程序被加載到內存中。然後局部變量獲取地址。在編譯時,將源代碼轉換爲機器語言代碼,以便可以執行。