最近在學習極客時間課程過程中碰到個挺有意思的關於陣列和linux 棧中增長方向的問題,特來與大家分享下。
話不多說,先上**
int main(int argc, char* ar**);
for(; i<=3; i++)
return 0;
}
**很簡單。當使用gcc 搭配上-fno-stack-protector(禁用堆疊保護)選項進行編譯、執行。小夥伴認為結果應該是什麼?不加上-fno-stack-protector 選項結果又是什麼?
當加上-fno-stack-protector 編譯選項後,執行時程式一直迴圈列印 hello world。
然而不加上-fno-stack-protector 編譯選項,程式只列印四次 hello world 就退出。
當加上-fno-stack-protector進行編譯時,編譯器是按照c語言以及linux中規定的方式對變數進行處理。
1)了解linux 程序記憶體分配的小夥伴肯定熟悉下圖,不熟悉的可以先看下,圖中箭頭表示堆疊的增長方向。
程式中的區域性變數i和陣列arr均儲存在棧空間,而linux中棧增長方向是遞減的,因此先定義的變數i位址肯定大於陣列arr位址。
假設i的位址為0xb200_0020,那麼陣列arr的起始位址就應該是0xb200_0014(arr[0] = 0xb200_0014、rr[1] = 0xb200_0018、arr[2] = 0xb200_001c)。可能會有小夥伴想為啥陣列變數的記憶體分布一定是這樣,而非下圖左側所示呢?
a[i]_address = base_address + i * data_type_size
所有的元素都是在陣列基位址的基礎上 + 對應的偏移量,這就使陣列中索引號越大的元素,其位址越大的原因。
結合上linux 棧中的記憶體分配情況,共同就導致了變數i後緊跟的是a[2]而非a[0]了。
通過以上兩點知識便知道這些變數在棧記憶體中的分布情況,相信聰明的小夥伴們能明白為什麼程式會一直列印hello world。當i = 3時,&a[3]其實指向的就是變數i位址,此時執行a[3] = 0,意味著i = 0,導致無限迴圈。這就是一直列印hello world根本原因。
至於為啥不加上-fno-stack-protector程式列印四次hello world就退出,了解到gcc編譯時預設就會開啟堆疊保護選項。
上圖中可看到預設情況下:變數i緊隨在陣列array[0]後面,意味著入棧順序為:array[2]、array[1]、array[0]、i
加上-fno-stack-protector選項後:變數i後面緊隨陣列array[2],意味著入棧順序為:i、array[2]、array[1]、array[0]
編譯時禁不禁用堆疊為何會導致該問題,原因後續博文追蹤。
由於對陣列的越界訪問未加限制以及指標訪問越界,此乃c語言設計本身的一大缺陷,同時也是c語言的魅力之處。
因此在日常使用過程,陣列乃至指標等直接涉及記憶體相關的操作,一定要注意是否存在越界的情況。很多時候未加思考,導致後期出現的bug可能極難解決。c語言涉及記憶體使用時一定要多思考,盡可能減少這種隱藏bug。
C C 分析陣列越界訪問導致死迴圈
閱讀下面 並分析導致其結果的原因 以下分析基於vs環境的除錯 include include intmain 擁有10個元素的整型陣列 for i 0 i 12 i 迴圈13次,越界訪問 system pause return0 分析 整型陣列arr有10個元素,for迴圈13次,導致陣列越界訪問。...
棧與陣列越界
棧 stack 又名堆疊,它是一種運算受限的線性表。其限制是僅允許在表的一端進行插入和刪除運算。這一端被稱為棧頂,相對地,把另一端稱為棧底。向乙個棧插入新元素又稱作進棧 入棧或壓棧,它是把新元素放到棧頂元素的上面,使之成為新的棧頂元素 從乙個棧刪除元素又稱作出棧或退棧,它是把棧頂元素刪除掉,使其相鄰...
C 陣列越界,陣列進棧
首先,展示一段最常見的陣列越界。先定義int型的變數i,再定義整型陣列,長度為10,然後for迴圈時,給i初始化,即i 0,判斷i 10,若為真,則給陣列中的所有值賦值為0,然後依次輸出i的變化 否則,迴圈結束。如下 include int main 結果如下圖 編譯器 visual studio ...