簡體   English   中英

目標文件中已初始化的自動變量(局部變量)放在哪里?

[英]Where does initialized auto variables (local variables) placed in object file?

就像初始化的全局變量數據放在.data節中,而單元化的全局變量放在.bss節中一樣,編譯器在目標文件的哪一部分中放置了初始化的自動變量(局部變量)數據?

自動變量沒有(也不能)在目標文件中分配位置,因為在聲明它們的塊作用域的每個活動實例中,它們存在於一個實例中,而不是像靜態存儲對象那樣存在於單個實例中。 如果已初始化,則必須為函數的每個實例進行初始化,並且編譯器可以自由發出它喜歡設置其初始值的任何代碼。 當值是常量表達式, 可以這樣做,為memcpy從節目圖像中的部分(在對象/可執行文件從而existant),但它可以和通常不會只是涉及立即操作數內嵌在可執行代碼。

通常,具有自動存儲期限的對象(例如在函數內部定義的int x 不能存儲在目標文件中。 這是因為函數可以遞歸地(直接或間接地)調用任意次,並且每次調用都必須存在對象的不同實例,因此在對象文件中只有一個位置(允許對象文件的各個部分)可能在程序執行期間以一種或另一種方式映射到內存)不能用作對象的內存。

在特定情況下,對象模塊中的一個數據副本可用於自動存儲持續時間的對象,因為當編譯器可以確定未遞歸調用該函數時,因此一次只能存在該對象的一個​​實例。

但是,要實現初始化的對象,編譯器必須提供將對象設置為其初始值的條件。 程序執行期間使用的實際對象可能在堆棧上或在寄存器中,但是編譯器必須設置其初始值。 對於某些初始值,例如零或小的常數,編譯器可能在程序執行期間“即時”創建初始值,可能使用指令中的立即操作數。 (在這種情況下,初始值有效地存儲在對象模塊的代碼部分中。)對於不容易動態構造的常量,編譯器可以將值作為數據存儲在對象模塊中,通常以讀-僅(恆定)數據部分。 當然,由於此數據是供編譯器內部使用的(它是編譯器保存用於初始值的東西;它不是對象本身),因此不會用對象名稱標記。 編譯器將使用內部名稱或某個位置的數字偏移量來標識它。

同樣,可以使用非恆定值初始化自動存儲持續時間的對象。 當然,這些根本不存儲在對象模塊中。 它們是在程序執行期間計算的。

以上所有內容均會通過優化和C標准的“仿佛”規則進行修改-如果編譯器可以通過其他方式獲得相同的所需可觀察到的行為 ,則在執行過程中程序存儲器中可能根本不存在自動對象。作為將對象的用途折疊到其他計算中。

這意味着沒有一個位置會始終存儲自動存儲持續時間的對象或該對象的初始值。

這取決於。 通常,局部聲明如int x = 1; 將被分配給一個寄存器,並編譯為一條將常數1裝入該寄存器的指令。 較少地,將在堆棧上分配一個內存位置,並將值存儲在堆棧中。

如果編譯器(認為它)可以證明以這種方式重構程序不會改變其可觀察的行為,則可以完全優化掉任何變量。 例如,如果您編寫static const int ok = 0; 那么當你寫x = ok; 任何明智的編譯器都只會將x設置為常數0

如果使用局部變量的地址並取消引用,則編譯器必須將變量置於某個內存位置。 在編譯時已知的常量可能會存儲在文本段的只讀存儲器中。 靜態局部變量通常會與其他靜態變量一起存儲在數據段中。 否則,它將進入堆棧。

在更復雜的情況下,例如

double matrix[4][3] = { { 1,  0,  0, -1 },
                        { 0,  1, -1,  0 },
                        { 9,  0,  0,  1 } };

通常,您會看到初始值存儲在靜態數組中,然后復制到堆棧上的存儲位置,或者如果編譯器可以對算法進行矢量化,則將其復制到矢量寄存器。 換句話說,就好像您將初始值聲明為static const本地數組,然后將其復制到工作副本中一樣。

您可能會發現在GodBolt測試此類程序的一些變體很有幫助

double f (const double x)
{
  double matrix[2][2] = { {1, 0},
                          {0, 1} };

  double* const begin = &matrix[0][0];
  const double* const end =
    begin + sizeof(matrix)/sizeof(matrix[0][0]);

  for ( double* p = begin; p < end; ++p ) {
    *p *= x;
  }

  return (matrix[0][0] * matrix[1][1]) -
         (matrix[0][1] * matrix[1][0]);
}

編譯器生成的代碼差異很大。 GCC優化了循環和除數組初始值以外的所有變量,數組的初始值存儲在地址.LC0靜態存儲器中並復制到寄存器中。 Clang生成一些向量指令,除了寄存器外,它們根本不分配任何存儲空間。 從理論上講,一個好的靜態分析器可以優化此函數以return x*x;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM