2016-03-16 80 views
-2

變量a在兩個單獨的函數中聲明,但僅在其中一個函數中初始化。主函數調用聲明和初始化a的函數,然後調用第二個函數,該函數在不初始化的情況下重新聲明該變量。它打印42,儘管a在一個不同的函數範圍內被初始化,其數據應該在函數完成後被銷燬。這是爲什麼發生?在兩個函數中聲明變量,但只初始化其中的一個

#include <stdio.h> 
void foo() { 
    int a = 42; 
} 
void bar() { 
    int a; 
    printf("%d",a); 
} 
main() { 
    foo(); 
    bar(); 
} 
+0

他們不同'a's。閱讀關於變量*範圍*。 – Roddy

+1

你告訴我們,它打印42嗎? – pm100

+0

順便說一句,它實際上定義了兩個函數,但只在一個函數中初始化。它的措辭在 – TinyTheBrontosaurus

回答

0

Oooo,這是一個很好的。正式它沒有定義。但我敢打賭,他們中的很多人會打印42,因爲a很可能會在堆棧中使用相同的內存地址。

因此,這是如何工作的基礎知識是,當一個函數被稱爲程序應該返回的PC(程序計數器)的值與參數一起被壓入堆棧時。在這種情況下,foo和bar都沒有參數,所以只有一個指針會被壓入堆棧(所以總共4個字節從相對地址0開始)。

然後當一個函數啓動時,它的變量被壓入堆棧。在這種情況下,它們都有一個將被推送的內容,這又是每個4個字節(總共8個字節,從相對地址4開始)。

這是一種在C/C++中從其他功能/程序中獲取數據的常用方法,否則該功能不應該被允許訪問。

+0

我敢打賭,更多的會從'foo'中完全優化'a'。 – Roddy

+0

是的。好點子。 – TinyTheBrontosaurus

1

,官方的回答是,這

void bar() { 
    int a; 
    printf("%d",a); 
} 

所調用 '未定義行爲'。任何事情都可能發生。它可以打印詩歌,格式化硬盤,打開白宮聖誕樹上的燈光......它甚至可以打印42,有些人會告訴你這是怎麼回事。

其他地方有另一個叫a變量無關

0

未定義行爲的事實。您正在打印未初始化的變量。 afoo在概念上與a不同。雖然UB可以(根據標準)執行@ pm100提出的所有奇怪的事情,但在現實世界中,它將打印出隨機存儲器位置或寄存器中發生的任何事情。所以,一個未定義的值。

它會打印42嗎?有可能。 a:因爲存在一個隨機的機會,未定義的值是42,b:因爲可能存儲器/寄存器是用foo寫42而沒有被別的東西覆蓋。

我可以依靠它始終打印42嗎? 100%,不!

0

未定義的行爲!

如果優化關閉,觀察到的行爲是clang和gcc打印42。如果添加-O1,則foo函數會有效消失,然後printf會打印一些垃圾。

以下程序顯示了您的有效地址,該一個點是兩個功能相同

#include <stdio.h> 
#include <stdint.h> 
void foo(); 
void bar(); 

void foo() { 
    uint8_t a = 255; 
    uint8_t b = 255; 
    printf("%p\n",&a); 
} 

void bar() { 
    uint16_t a; 
    printf("%p\n",&a); 
    printf("%hu\n",a); 
} 
int main(void) { 
    foo(); 
    bar(); 
} 

輸出

0x7fff54dd259e 
0x7fff54dd259e 
65535 

這是今日觀察我的機器上的行爲此時在這些天氣條件下。你當然可以觀察飛行的豬,粉紅色的大象或任何方式的Heisenbug,同時試圖觀察上面的代碼實際上做了什麼。

相關問題