C語言入坑指南-被遺忘的初始化
前言
什麼是初始化?為什麼要初始化?靜態變數和區域性變數的初始化又有什麼區別?實際應用中應該怎麼做?本文將一一回答這些問題。
什麼是初始化
初始化指的是對資料物件或者變數賦予初始值。例如:
int value = 8; //宣告整型變數並初始化為8
int arr[] = {1,2,3}; //宣告整型陣列arr,並初始化其值為1,2,3
為什麼要初始化
我們來看一個示例程式。
test0.c程式清單如下:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int sum;
int randNum;
while(10 > sum)
{
randNum = rand() % 10;
sum += randNum;
printf("rand num is %d,sum is %d\n",randNum,sum);
}
printf("the final sum is %d\n",sum);
return 0;
}
程式隨機產生0到9的數字,使得sum的值大於或等於10時,退出程式。
編譯並執行:
gcc -o test0 test0.c
./test0
執行結果如下(每次執行結果可能不同):
rand num is 3,sum is -4040865
rand num is 6,sum is -4040859
rand num is 7,sum is -4040852
rand num is 5,sum is -4040847
rand num is 3,sum is -4040844
rand num is 5,sum is -4040839
(省略其他內容)
從執行結果來看,程式並沒有達到我們的預期,這是為什麼呢?
很多讀者可能已經知道,問題在於宣告sum之後,沒有為其賦初始值,在這樣的情況下,sum的值是隨機的,因此在一開始sum可能是一個很小的負數,導致多次迴圈出現。很顯然,初始化避免使用了變數的“髒值”。而將sum的宣告改成如下定義即可:
int sum = 0;
如果將sum宣告為靜態變數,情況又會如何呢?
//test1.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
static int sum;
int randNum;
while(10 > sum)
{
randNum = rand() % 10;
sum += randNum;
printf("rand num is %d,sum is %d\n",randNum,sum);
}
printf("the final sum is %d\n",sum);
return 0;
}
編譯並執行:
rand num is 3,sum is 3
rand num is 6,sum is 9
rand num is 7,sum is 16
the final sum is 16
在這種情況下,程式是能夠符合我們預期的結果,這又是為什麼呢?原因在於靜態變數會被預設初始化。例如,int型別會被初始化為0。那麼問題來了:
-
為什麼區域性變數未初始化的時候的值是“髒值”?
-
靜態變數和區域性變數為什麼又不一樣呢?
在解答上面這兩個問題之前,我們需要簡單瞭解一下程式的儲存空間佈局。
程式的儲存空間佈局
C程式主要由以下幾部分組成:
-
正文段。即機器指令部分,為防止意外被修改,設為只讀。
-
初始化資料段。它包含了程式中需要明確賦初值的靜態變數。
-
未初始化資料段。它包含了程式中未賦初值的或初始化為0的靜態變數,在程式開始執行之前,核心將此段中的資料初始化為0。
-
棧。它儲存了自動(區域性)變數以及函式呼叫所要的資訊。
-
堆。用於動態記憶體分配。例如使用malloc函式進行記憶體分配。
其中,正文段和資料段的內容是“靜態”的,因為在程式被編譯出來之後,在整個程式地址就確定了,而堆疊中的內容是”動態”變化的,它隨著進行的執行而不斷變化著,再加上棧隨機化的策略,使得程式每次執行時,棧的地址也是不確定的。
區域性變數和靜態變數的初始化有何不同
有了前面的鋪墊,就很好理解兩者的差別了。
未初始化的區域性變數位於棧中,它的位置是不確定的,因此其值也是不確定的。當然,在windows下它的值是0xcccccccc,而“燙”字在MBCS字符集中的值為0xcccccccc,你說巧不巧?
而靜態變數就不一樣的,它的地址是確定的,並且存放在了資料段,而程式在執行之前,未初始化資料段的內容可以很方便地統一被初始化為0。這也就解釋了前面的兩個示例程式的結果為什麼會不一樣。我們加上一些列印,來看一看是否真的如此?
//test2.c
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
static int sum;
int randNum;
while(10 > sum)
{
randNum = rand() % 10;
sum += randNum;
printf("rand num is %d,sum is %d\n",randNum,sum);
}
printf("the final sum is %d\n",sum);
printf("sum addr %p,randNum addr %p\n",&sum,&randNum);
return 0;
}
編譯並執行:
gcc -o test2 test2.c
執行結果1:
rand num is 3,sum is 3
rand num is 6,sum is 9
rand num is 7,sum is 16
the final sum is 16
sum addr 0x60104c,randNum addr 0x7ffd0ea8cf54
執行結果2:
rand num is 3,sum is 3
rand num is 6,sum is 9
rand num is 7,sum is 16
the final sum is 16
sum addr 0x60104c,randNum addr 0x7ffff5e3ddb4
在這裡,sum是靜態區域性變數,而randNun是區域性變數(自動變數),因此可以發現,sum的地址值總是不變的,而randNum的值卻不斷變化著。我們也可以通過nm命令檢視sum的地址:
nm test2 |grep sum
000000000060104c b sum.2805
總結
我們來總結一下本文的主要內容:
-
如果變數是靜態的,它會被初始化為0;如果變數是自動的,它不會被初始化。
-
靜態的變數包括全域性變數、靜態全域性變數、靜態區域性變數。
-
使用區域性變數之前對其進行初始化,避免使用“髒值”。
-
從可讀性考慮,靜態變數也建議顯示初始化。
-
初始化為0的靜態變數仍然存在未初始化資料段中(BSS段)。
送幾句熟悉的話給大家:
手持兩把錕斤拷,
口中疾呼燙燙燙。
腳踏千朵屯屯屯,
笑看萬物鍩鍩鍩。
思考
test1.c的程式碼執行結果每次都一樣嗎?為什麼?該如何修改才能使得每次的執行結果不一樣?
棧隨機化的作用是什麼?
推薦閱讀:
關注公眾號【程式設計珠璣】,第一時間獲取更多原創技術文章