static靜態局部變量初始化問題


第一次寫博客之類的東西,主要是為了記錄自己的學習過程,以便於記憶的加深和與各位大神進行探討,學習更多的東西。

  

本次上傳主要是關於靜態局部變量的初始化問題。

首先,靜態局部變量和全局變量一樣,數據都存放在全局區域,所以在主程序之前,編譯器已經為其分配好了內存,但在C和C++中靜態局部變量的初始化節點又有點不太一樣。在C中,初始化發生在代碼執行之前,編譯階段分配好內存之后,就會進行初始化,所以我們看到在C語言中無法使用變量對靜態局部變量進行初始化,在程序運行結束,變量所處的全局內存會被全部回收。而在C++中,初始化時在執行相關代碼時才會進行初始化,主要是由於C++引入對象后,要進行初始化必須執行相應構造函數和析構函數,在構造函數或析構函數中經常會需要進行某些程序中需要進行的特定操作,並非簡單地分配內存。所以C++標准定為全局或靜態對象是有首次用到時才會進行構造,並通過atexit()來管理。在程序結束,按照構造順序反方向進行逐個析構。所以在C++中是可以使用變量對靜態局部變量進行初始化的。

 

后面再來談談另一個問題,假如我們在一個循環中,定義了一個靜態局部變量並進行初始化,循環過程中,編譯器怎么知道當前的靜態局部變量已經初始化過了呢?

這個問題C和C++的處理方式也是不一樣的。C中編譯器會直接跳過這一個語句,因為在編譯的時候已經對靜態局部變量進行過分配空間並初始化,所以代碼執行過程中根本不需要再次執行。而在C++中,編譯器會在編譯器分配內存后,在全局區域(當前靜態局部變量的地址)附近同樣分配一塊空間,進行記錄變量是否已經進行過初始化。之所以說附近是根據編譯器不同,處理方式不同導致的。在網上有博客介紹某種編譯器(該吧主並沒有透露編譯器名字),會在當前變量后面的一個字節進行改變,具體上代碼:

(Ps:若編譯器已經發現當前變量初始化,則直接將整行代碼跳過,若等式后面為n++,則不會繼續執行++命令)

 

從地址內存中我們可以看到在變量地址的后面一個字節中,有一個01用來記載當前靜態變量是否初始化。

而在VS2012中,發現了一個很奇怪的現象,先上代碼。

 

 

這段代碼a為變量,右側為執行代碼的匯編語句,可以看到先從0x00306214中提取出來值,如果是0,則會繼續執行,進行初始化,如果是1,則會跳過當前下面的動作,此處可以看到標志位地址是在變量地址之前0x80個字節,但繼續后面幾種情況

 

 

 

可以看到只要變量名稱發生改變,每次標志位所在的地址與變量地址之間的差距變化都比較大。可見VS編譯器對標志位地址的選擇是根據變量名稱變化的。具體選擇方法,還希望有鑽研過的大神指點一二。

 

上面說到的僅僅針對單線程的程序,如果在多線程下的執行,g++編譯器會在在初始化過程中對標志位進行加鎖控制,詳情參考一下 http://www.cnblogs.com/xuxm2007/p/4652944.html

在不確定編譯器是否會對多線程環境下靜態局部變量初始化加鎖的情況下,盡量不要使用初始化的局部靜態變量,如果需要使用,則需要自己定義一個全局鎖進行管控,如一個線程正在對某個變量(對象)進行構造,另一個線程需要直接避開構造。

 

 第一次發此類分享類博客,如果有大神看出其中漏洞,請及時指出,以便於本人能夠對這方面的了解更加深入

 

補充:使用G++調試后,查看地址發現對於一個靜態局部變量,編譯器會開辟8個字節的大小的存放空間,同時存放位置剛好在變量前面的8個字節位置。若有兩個靜態局部變量,則從第一個靜態局部變量前16個字節,分別8個字節對應一個變量。用事實說話,上圖:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM