2013-04-25 24 views
1

假設一個純粹的非優化編譯器,初始化一個變量並在聲明後爲它賦值的機器代碼是否有區別?初始化一個變量並在聲明之後立即爲它賦值是否有區別?

初始化方法

int x = 2; 

分配方法

int x; 
x = 2; 

我用GCC來輸出用於這兩種不同的方法生成的彙編和都導致在單個機器指令:

movl $2, 12(%esp) 

該指令只是將x變量所保存的內存設置爲值2。 GCC可能會優化它,因爲它可以識別操作的最終結果;但我認爲這是解釋這兩個版本的唯一方法。我的推理是兩個版本都做同樣的事情:將一部分內存設置爲特定值。

爲什麼那麼往往區分術語「初始化」和「分配」之間進行,如果產生的機器代碼是一樣的嗎?

純粹是用來區分具有分配在那些具有任何價值的垃圾留在記憶(未初始化變量)的特定值的變量術語「初始化」?

+0

可能是因爲初始化是第一次分配。 – Nikolai 2013-04-25 14:46:24

+0

'x = 1 + 1'也會生成與'x = 2'相同的代碼。這是否意味着分配和添加也是一樣的? – 2013-04-25 20:25:27

+0

@BoPersson「巧合不是因果關係」的好例子。 – Sebivor 2013-04-26 05:27:48

回答

3

行爲必須相同,但生成的代碼中的任何差異都取決於編譯器。

例如,編譯器可以用於初始化的變量產生這樣的:

somefunction: 
pushl %ebp 
movl  %esp, %ebp 
pushl $2 ; allocate space for x and store 2 in it 
... 

這對於未初始化的,但後來分配的變量:

somefunction: 
pushl %ebp 
movl %esp, %ebp 
subl $4, %esp ; allocate space for x 
... 
movl $2, -4(%ebp) ; assign 2 to x 
... 

C標準並沒有規定所生成的代碼在這些情況下是相同的或不相同的。它只是強制要求這兩種情況下的程序具有相同的行爲。而相同的行爲並不一定意味着相同的機器碼。

+1

這真的很有趣,因爲它意味着通過C標準,編譯器理論上可以在最終執行分配之前執行無意義的代碼(例如循環300次)。雖然它在效率方面會失敗,但它在技術上仍然符合C標準。 – 2013-04-25 15:23:21

+1

@VilhelmGray我認爲他們稱之爲「JIT(Just In Time)優化」...... – Sebivor 2013-04-25 15:26:00

+2

沒錯。關於C標準中的性能(如保證或要求)沒有一個字。但這裏並不令人意外。你不能從CPU本身獲得有保證的性能(因爲高速緩存,由於OS中的調度,由於中斷,由於磁盤/網絡速度,緩衝等原因)。 – 2013-04-25 15:28:21

6

假設一個純粹非優化編譯器,是否有初始化變量和聲明之後分配一個值 之間在 機器代碼的任何差異?

當然。

  • char fubar[] = "hello world";是有效的。
  • char fubar[]; fubar = "hello world";不是。

更多?

  • int fubar[128] = { [60] = 42 };有效。
  • int fubar[128]; fubar = { [60] = 42 };不是。

更多?

  • struct foo bar = { .foo = 13, .bar = 42 };是有效的。
  • struct foo bar; bar = { .foo = 13, .bar = 42 };不是。

更多?

  • const int fubar = 0;有效。
  • const int fubar; fubar = 0;不是。

我可以繼續下去......因此,機器代碼威力一個存在,同時它很可能不會爲其他。在那個筆記上,你有沒有聽說過C不是編譯器的實現?

爲什麼在初始化 和賦值之間經常做出區分,如果生成的機器代碼是相同的?

呃,因爲他們是不同的概念...

純粹是用來區分變量 具有分配在這些特定值的「初始化」一詞(未初始化) 變量哪些垃圾值留在內存中?

雖然「初始化」的變量不會包含任何「垃圾值」(或陷阱表示),但這不是它唯一的影響。

在我的第一個例子中,初始化將提供否則不完整數組的大小。使用賦值運算符的等價物需要明確提供數組的長度,並使用strcpy,這相當繁瑣。

在我的第二個例子中,索引60處的int將被初始化爲40,而其餘的,否則未初始化的項目將被初始化爲0.使用賦值運算符的等價物也相當單調乏味。

以我第三示例中,成員foobar將被初始化爲13和42,而剩餘,否則未初始化的成員將被初始化爲0的當量使用賦值運算符將是相當繁瑣的,雖然我偶爾使用一個複合文字來實現類似的結果。

在我的第四個示例中,初始化設置變量將在其整個生命週期中包含的值。這個變量不可以賦值。

+0

@undefinedbehaviour在你的第二個例子中,索引60之後的所有元素都被初始化爲'0',那麼索引60之前的所有元素都將被初始化(即包含垃圾數據)嗎? – 2013-04-25 15:37:32

+1

@VilhelmGray編號所有未初始化的項目將被隱式初始化爲零。 – Sebivor 2013-04-25 15:44:45

3

的一個重要區別進場時加const預選賽:

int const x = 2; 

是有效的C

int const x; 
x = 2; 

不是。另一個重要的區別是static變量:

static int x = f(); 

無效Ç

static int x; 
x = f(); 

是有效的。

0
int x = 2; 

計算機將創建變量x,併爲其分配值2 幾乎在同一時刻


int x; 
x = 2; 

計算機將創建變量x。然後將其分配給它的值2 這似乎是沒有任何區別,但...

...讓我們假設你的代碼是這樣的:

int x; 
{some operators}; 
x = 2; 

計算機可能必須訪問變量x才能爲其賦值2.這意味着,在運行程序計算機時將花費更多時間訪問x以便爲其賦值一些值,而不像它將創建變量並在此時分配此變量。

無論如何,Deitel HM,Deitel PJ在C How to Program中描述了這一點。

+2

int x;'不創建*變量,它*聲明*一個。計算機不會創建變量:變量具有由其聲明確定的*範圍*。執行進入和退出變量的範圍。即使對於非優化編譯器,執行進入或退出變量範圍通常也沒有可辨別的個人成本(就時間而言)。與分配一樣,初始化代價昂貴,但如果需要的話,編譯器可以將初始化或分配更接近首次使用,因此不會有任何困難。 – 2013-04-25 19:30:56

相關問題