C++基本類型-變量在可執行文件中的存放位置及初始化時機
環境如下
如下代碼
使用objdump -D -C反彙編-D會輸出所有段,-C可以輸出變量名
rodata 只讀數據段
只讀數據段中存放了
全局字符串常量char *f="123456"的值
局部字符串常量char *k="abcdef"的值
全局變量const int d=3
全局變量static const int e=4
main函數中的局部static const int j=8
由此可知,全局const變量,字符串常量的值,static const修飾的變量,會存放在rodata,而const修飾的局部變量並不存放在rodata段,而是存放在棧中,存放在只讀數據段的變量在編譯期就要確定其值
data 可讀寫數據段
可讀寫數據段中存放了
全局已初始化變量int b=1;
全局已初始化變量static int c=2;
全局已初始化變量char *f=0x08048640;0x08048640是rodata段中存放"123456"的起始地址
main函數中局部static int h=6;
由此可知全局已初始化變量,static 修飾的已初始化變量會存放在data段,存放在可讀寫數據段的變量,其值在編譯期已經完成了初始化,同時還可以看出可執行文件中的多字節是以小端模式存放的
bss 未初始化變量段
bss段的變量在可執行文件中並不佔用存儲空間,只記錄總體大小
bss段存放了
全局未初始化變量int a;
main函數中的局部未初始化變量static int m,已初始化的局部變量static int n=0;
由此可知全局/局部static未初始化或全局/局部static初始化爲0的變量會存放在bss
text 代碼段
我們只看main函數
可執行文件中並沒有stack段,棧是對一塊內存該怎樣使用的一種約束,棧上的局部變量只有代碼運行後纔會被初始化,棧是由編譯器生成的代碼在管理,棧是一塊連續的虛擬內存,在我的虛擬機中一個線程的棧默認最大是8M
代碼段存放的是我們編寫的代碼產生的cpu指令,編譯器通過構造棧楨來實現C++中的函數調用,棧楨是棧中的一個連續片段由ebp標識的棧底和esp標識的棧頂來確定,一個線程中的每一個函數調用都會在此線程的棧上生成一個棧楨用來保存局部變量,調用參數,寄存器的值及返回地址,最近一個被調用的函數的棧楨位於線程棧的最頂部,棧楨上的局部變量通過,基址寄存器ebp和變量的相對偏移量來確定,函數返回後棧楨回退,但棧的大小不會變小,棧會隨着函數調用鏈的深入一直增長直至達到ulimit -s的值然後線程崩掉,棧楨是棧上的一個動態的函數調用過程
如下代碼
反彙編代碼如下
第三條指令sub $0x100018,%esp爲函數調用分配1M的棧楨
每次fun函數調用編譯器都會爲其分配1M的棧楨空間
默認線程棧大小爲8M
查看線程函數調用棧
當函數遞歸調用達到第8層時,由於棧大小超過了線程棧的上限8M,操作系統發送了signal 11(sigsegv)信號給此進程,進程崩掉
main函數的棧上存放了
main函數中的局部變量int g=5
main函數中的局部變量const int i=7
main函數中的局部變量char *k=0x8048647,0x8048647是字符串常量的值"abcdef"在rodata段的起始地址
存放在棧中的變量只有程序運行到這行代碼時纔會被初始化
總結一下:
函數中的局部變量,const修飾的局部變量會存放在棧中
全局已初始化變量及static修飾的已初始化變量會存放在data段
全局未初始化/初始化爲0及static修飾的未初始化/初始化爲0的變量會存放在bss段
const修飾的全局變量,字符串常量的值,static const 修飾的變量會存放在rodata段
位於data及rodata段的變量在編譯期已完成初始化
位於stack中的變量在程序運行時被初始化
在可執行文件的反彙編代碼中並未找到初始化bss段的變量的代碼,bss段的初始化只需要分配一塊可容納所有未初始化變量的內存並初始化爲0即可
開啓優化後編譯器並不會爲const修飾的變量分配存儲空間,對於const變量的使用在編譯期編譯器會直接進行值替換,並不會通過地址間接的去內存中訪問該變量,在對const變量進行取址時,編譯器纔會爲const變量分存內存,因此在取得const變量的地址後,通過指針去修改一個const變量的值,雖然可以修改成功,但通過指針取得的值與直接使用變量得到的值是不同的值,前者來自內存,後者是一個字面值,在彙編代碼中作爲立即數出現