2017-09-01 85 views
4
for(int i=0;i<10;i++) 
{ 
    int x=0; 
    printf("%d",x); 
    { 
    int x=10; 
    printf("%d",x); 
    } 
    printf("%d",x); 
} 

這裏我想知道變量x的內存將被分配兩次還是僅僅在退出第二個塊並且內存被分配一次(對於x)後才重置的值?變量的塊結構內存分配

+5

如果機率很高,即使是一個內存,也可能不會分配內存。 –

+3

內存分配兩次。在第2塊內,您可以通過「:: x」訪問第一個x更多 - https://en.wikipedia.org/wiki/Variable_shadowing – Macias

+2

這些是2個不同的變量。保留2個值意味着2個「分配」 –

回答

5

從視圖C編程模型的點,的x兩個定義是兩個完全不同的對象。內部塊中的賦值不會影響外部塊中的x的值。

此外,循環的每次迭代的定義也計算爲不同的對象。在一次迭代中將值分配給x將不會影響後續迭代中的x

就實際實現而言,假設沒有進行優化,有兩種常見的情況。如果你打開了優化,你的代碼很可能會被丟棄,因爲編譯器很容易找出除了i之外的循環對其他任何東西都沒有影響。

兩個常見的情況是

  1. 變量存儲在堆棧中。在這種情況下,編譯器將在外部x的堆棧上預留一個插槽,並在內部x的堆棧上預留一個插槽。理論上它應該在範圍的開始處分配槽並在範圍的末尾釋放槽,但這只是浪費時間,所以它會在每次迭代中重新使用槽。

  2. 變量存儲在寄存器中。這是現代64位體系結構更可能的選擇。同樣,編譯器應該在範圍的開始處「分配」(分配不是真正的合適的單詞)寄存器,並在結束時「釋放」,但它只會在現實生活中重複使用相同的寄存器。

在這兩種情況下,你會注意到,因爲編譯器使用相同的存儲空間,從每個迭代的值將被保留到下一次迭代。但是,如果你編譯並運行上述(與沒有優化),你可能會發現它打印合理的值,但是,每次你去一輪循環的時間從來沒有做到這一點

for (int i = 0 ; i < 10 ; ++i) 
{ 
    int x; 
    if (i > 0) 
    { 
     printf("Before %d\n", x); // UNDEFINED BEHAVIOUR 
    } 
    x = i; 
    printf("After %d\n", x); 
} 

x在理論上是一個全新的對象,所以第一個printf訪問一個未初始化的變量。這是未定義的行爲,所以程序可能給你的價值從以前的迭代,因爲它使用相同的存儲或它可能 firebomb你的房子和賣你的女兒成奴隸制。

+1

剛注意到這個問題也被標記爲[C++]。可以肯定的是,大部分上述情況仍然適用,只是由於繼承,編譯器也可以將您的孫子女賣給奴隸。 – JeremyP

+0

平心而論,關於firebombs和奴隸制的事情只適用於Borland C/C++,現在強烈建議不要使用它。 – Persixty

+0

@Persixty是的,我知道現在沒有這樣做的實現,例如,Clang在多樣性上很強大,並且不會因爲只將順式異性女性後代賣給奴隸而歧視。 – JeremyP

2

這是一個實現特定的細節。

例如,在代碼優化階段,它可能會檢測到那些未使用。所以不會爲他們分配空間。

即使某些編譯器沒有這個東西,那麼你可以預料到可能會出現兩個不同的變量空間未被分配的情況。

對於您的信息,事物大括號並不總是意味着它是不同的內存或堆棧空間。這是一個範圍問題。而對於變量,它可能是在CPU寄存器中分配的情況。

所以你不能說一般。你可以說的是他們的範圍不同。

+0

因此,在堆棧中,變量x只被推送一次,當遇到第二個x時,我們只是改變這些值而不是再次推送它? – Zephyr

+0

@Zephyr:你根本不知道......我的意思是,這是可能的,也可能是它被分配兩次。編譯器具體實現。編譯器不必生成代碼,以便在入口/出口範圍內的入口/出口處推送/彈出任何內容。 – coderredoc

+0

C或C++編譯器會發生什麼情況? – Zephyr

1

我希望大多數編譯器都會使用堆棧中的內存來存儲這種類型的變量,如果任何內存都需要的話。在某些情況下,CPU寄存器可能用於一個或兩個x。兩者都有自己的存儲空間,但是與編譯器相關的是,該存儲的生命週期是否與源中聲明的變量範圍相同。因此,例如,用於「內部」x的內存可能會繼續在超出該變量超出範圍的地方使用 - 這實際上取決於編譯器的實現。

3

留出編譯器優化,可能會刪除這些未使用的變量,答案是兩次。

x口罩(技術術語)在其範圍內的其他定義如下其聲明第二個定義。 但是第一個定義在該範圍之後再次可見。 因此邏輯上(忘記優化)xx=0)的第一個值必須在某個地方舉行,而x=10「在場」。因此,兩件存儲(邏輯上)是必需的。

執行下面的C程序。典型部分輸出:

A0 x==0 0x7ffc1c47a868 
B0 x==0 0x7ffc1c47a868 
C0 x==10 0x7ffc1c47a86c 
D0 x==0 0x7ffc1c47a868 
A1 x==0 0x7ffc1c47a868 
B1 x==0 0x7ffc1c47a868 
C1 x==10 0x7ffc1c47a86c 
//Etc... 

通知僅點C如何看待變量x具有值10,並與值0的變量是可見再次在點D也可參見如何的x兩個版本都存儲在不同的地址。 從理論上講,每次迭代的地址可能不同,但我不知道實際執行的操作,因爲這是不必要的。但是,如果你做了這些非trival C++對象,它們的構造器和析構器會在每個循環中被調用,儘管它們仍然駐留在相同的地址處(實際上)。

人類讀者隱藏這樣的變量顯然令人困惑,不推薦。

#include <stdio.h> 

int main(void) { 
    for(int i=0;i<10;i++) 
    { 
     int x=0; 
     printf("A%d x==%d %p\n",i,x,&x); 
     { 
      printf("B%d x==%d %p\n",i,x,&x); 
      int x=10; 
      printf("C%d x==%d %p\n",i,x,&x); 
     } 
     printf("D%d x==%d %p\n",i,x,&x); 
    } 
} 
+0

感謝您的回答。但從邏輯上講,沒有必要分配堆棧內存兩次,因爲我們可以在範圍結束後更改變量的值,並將變量x的內存分配一次。我對嗎? – Zephyr

+2

@ Zephyr:不,那是不對的。假設第一個賦值是(比如說)'int x = f();'其中'f()'是函數調用或者其他計算值。 (在編譯時未知)值是什麼時候被隱藏,比如說'int x = g();'(內部定義)。如果第二個範圍之後有第三個範圍 - 'int x = h();'可以與第二個範圍共享空間,但是在邏輯上我們需要兩個變量的空間。 – Persixty

+0

@ Zephyr我已經修改了答案來顯示'x' - '&x'的地址。它們的兩個定義需要單獨存儲,程序大致相同,就好像第二個是'int y = 10'。唯一的區別是,在C語言中,在聲明之後,您不能在語法上引用內部塊中的外部版本。 – Persixty