C程序的內存管理
熟悉Java語言的肯定知道,Java中內存管理是由虛擬機幫助我們完成的,在C/C++中可不是這樣,程序員需要自己去分配和回收內存空間。本文記錄了C程序在內存中存儲結構、C變量和函數常見的存儲類型、分配和回收內存等方面的內容。以下C程序所使用的編譯器版本是GCC 4.4.7。
從一個C程序說起
文件的結構
對於以下這段Hello.c程序再熟悉不過了
#includeint main(void) { printf("Hello World\n"); return 0; }
可以看出,可執行文件Hello在存儲時(沒有調入內存時)分為代碼區(text),數據區(data)和未初始化數據區(bss)3個部分。另外3個字段中,dec表示十進制總和,hex表示十六進制總和,filename表示文件名。各段的具體說明如下:
(1)代碼段(text segment)<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqO6tOa3xUNQVda00NC1xLv6xvfWuMHuoaPNqLOjtPrC68f4yse/ydLUubLP7bXEo6i8tMHtzeK1xNa00NCzzNDyv8nS1LX308PL/KOpoaO0+sLrx/jNqLOjysfWu7bBtcSjrNLUt8DWubPM0PLS4s3itcTQ3rjEy/y1xNa4we6hozxzdHJvbmc+s6PBv8r9vt3U2rHg0uvKsdTatPrC68f4t9bF5MTatOY8L3N0cm9uZz6ho7T6wuvH+LXE1rjB7rD8wKiy2df3wuu6zbLZ1/e21M/zo6i78rbUz/O1xLXY1rfS/dPDo6mho8jnufvKx8GivLTK/aOsvs3Wsb3TsPy6rNTatPrC69bQo7vI57n7yse+1rK/yv2+3aOsvavU2tTL0NDKsbXE1bu/1bzk1tC31sXko6zIu7rz1NrS/dPDuMPK/b7dtcS12Na3o7vI57n7ysdic3PH+LrNyv2+3cf4o6zU2rT6wuvW0M2s0fnKx9L908O4w8r9vt21xLXY1rehozwvcD4KPHA+o6gyo6k8c3Ryb25nPsirvtaz9cq8u6/K/b7dx/gvvrLMrMr9vt3H+KOoaW5pdGlhbGl6ZWQgZGF0YSBzZWdtZW50L2RhdGEgc2VnbWVudKOpPC9zdHJvbmc+o6y78tXfvPKzxsr9vt22zqO6uMPH+NPysPy6rMHL1NqzzNDy1tDD98i3sbuz9cq8u6+1xMirvtax5MG/o6zS0b6ts/XKvLuvtcS+ssysseTBv6OosPzAqMirvta+ssysseTBv7rNvtayv76yzKyx5MG/o6mho9Do0qrXotLitcTKx6OsPHN0cm9uZz6xu2NvbnN0yfnD97XEseTBv7rN19a3+7Sus6PBv9TatPrC67bO1tC31sXkxNq05jwvc3Ryb25nPqGj1eK6zbvjseDT79HU1tC1xMr9vt22zrXEuMXE7srHwOAmIzIwMjg0O7XEoaM8L3A+CjxwPqOoM6OpPHN0cm9uZz7OtLP1yry7r8r9vt3H+GJzc6OoQmxvY2sgU3RhcnRlZCBCeSBTeW1ib2yjqTwvc3Ryb25nPqO6tOa0orXEysfOtLP1yry7r7XEyKu+1rHkwb+6zc60s/XKvLuvtcS+ssysseTBv6GjPHN0cm9uZz5ic3PH+NPytcTK/b7d1NqzzNDy1rTQ0Mewu+Gxu8Tausuz9cq8u6/OqjC78tXfv9XWuNXro6hOVUxMo6k8L3N0cm9uZz6jrNXius3Vu9bQtcSx5MG/yseyu82stcSjrNW71tC1xLHkwb+jqL7Wsr+x5MG/o6nI57n7w7vT0LP1yry7r77NyrnTw6Osz7XNs7vhy+a7+rfWxeTSu7j2JiMyMDU0MDu4+Mv8o6zV4srHsruwssirtcShozwvcD4KPHA+yc/K9tXi0Km2vMrHv8nWtNDQzsS8/rXEtOa0or3hubm31s72o6zG5Mq11MvQ0MqxtcTE2rTmveG5ubrN1eK49squt9bA4CYjMjAyODQ7o6zWu7K7uf224MHLttHE2rTmus3Vu8TatObH+NPyo6zU2rrzw+a74bfWzva1vaGjz8LD5s2ouf28uLj2wP3X09Hp1qTWrqGjPC9wPgo8cD67ucrH0tRIZWxsby5js8zQ8s6qwP08L3A+CjxwPjxpbWcgc3JjPQ=="http://www.2cto.com/uploadfile/Collfiles/20141002/20141002091002229.png" alt="\">
我們在Hello.c中增加了一句代碼,定義一個常量i,通過分析比較,可以發現代碼段text區大小增加了4個字節(一個int類型占4個字節),其他區域不變,可知常量是分配在代碼段的。
在上述的基礎上,在添加一句,定義一個全局變量a,並給它賦值為2,觀察各區域變化
通過比較發現,只有數據段的大小增加了4個字節,也證明了明確被初始化的全局變量是被分配在數據區的。靜態變量也是一樣,可自行證之。
在上述的基礎上,我們在定義一個全局變量b,但是這一個不要賦值,觀察各區域變化
可以發現,這一次只有bss區域增加4個字節,也證明了未初始化的全局變量是分配在bss區域的。未初始化的靜態變量同理,可自行證之。
進程的結構
一個程序執行的時候就表現為一個或者多個進程,其實進程內核的數據結構和上述文件的存儲結構很相似,主要是多了堆內存和棧內存區域。主要的布局如下圖所示
各部分說明如下:
(1)代碼區(text segment):加載的是上述可執行文件的代碼段,其加載到內存中的位置由加載器完成。
(2)全局初始化數據區/靜態數據區(Data Segment):加載的是上述可執行文件的數據段,位置位於可執行代碼段後面,可以是不相連的。在程序運行之初就為數據段申請了空間,程序退出的時候釋放空間,其生命周期是整個程序的運行時期。
(3)未初始化數據區(BSS):加載的是上述可執行文件的BSS段,位置在數據段之後,可以不相連。其生命周期和數據段一樣。
(4)棧區(Stack):由編譯器自動分配釋放,存放函數的參數值、返回值、局部變量等。在程序運行過程中動態的分配和釋放,棧區位於BSS後,是向上有限擴展的。
(5)堆區(Heap):用於動態內存分配。位於棧區的後面,是向下有限擴展的。一般由程序員進行分配和釋放,若不釋放,在程序結束的時候,由OS負責回收。
(未完待續)