資料結構與演算法之美

HowieLee59發表於2019-01-18

在看極客時間的專欄的時候遇到了一個很有趣的問題:

想一下以下的程式碼執行之後答案為多少?

int main(int argc, char* argv[]){
    int i = 0;
    int arr[3] = {0};
    for(; i<=3; i++){
        arr[i] = 0;
        printf("hello world\n");
    }
    return 0;
}

按照正常的思維大概是輸出三次hello world之後便停止,但是結果並非如此。

放到編譯器跑一下之後發現是無限迴圈的列印,這是為什麼呢?

我們知道,在C語言中,只要不是訪問受限的記憶體,所有的記憶體空間都是可以自由訪問的。a[3]也會被定位到某塊不屬於陣列的記憶體地址上,而這個地址正好是儲存變數i的記憶體地址,那麼a[3]=0就相當於i=0,所以就會導致程式碼無限迴圈。

這個與定義的順序有關,如果在i和陣列的定義中間插入一個其他的定義則不會出現這種情況,這又是為什麼呢?

因為在作業系統中函式體內的區域性變數存在棧上,且是連續壓棧。在Linux程式的記憶體佈局中,棧區在高地址空間,從高向低增長。變數i和arr在相鄰地址,且i比arr的地址大,所以arr越界正好訪問到i。當然,前提是i和arr元素同型別,否則那段程式碼仍是未決行為。

需要額外注意的是:

例子中死迴圈的問題跟編譯器分配記憶體和位元組對齊有關 陣列3個元素 加上一個變數a 。4個整數剛好能滿足8位元組對齊 所以i的地址恰好跟著a2後面 導致死迴圈。。如果陣列本身有4個元素 則這裡不會出現死迴圈。。因為編譯器64位作業系統下 預設會進行8位元組對齊 變數i的地址就不緊跟著陣列後面了(64位中一個int佔32位,也就是四個位元組,讀取的時候最後陣列中的位元組和定義的整數正好組成了一個8位元組,則產生了無線列印)

相關文章