靜態全局變量和靜態局部變量的探索
一、靜態全局變量
1.特點
靜態全局變量具有文件作用域,生命期是所處模塊裝載到所處模塊卸載,處於同一文件中的代碼能直接訪問它,外部文件不能直接訪問。假設寫了如下代碼,編譯通過。
現在在同一工程下新建文件,輸入如下代碼,編譯通過,鏈接失敗。
如果換成全局變量,則編譯連接成功:
由此可見,在用extern定義變量時,只能在本文件使用,其他文件無法直接訪問。
2.機制
靜態全局變量本質上是受編譯器按語法約束的全局變量,如果未初始化,則分配在數據區的未初始化去,如果已初始化,則分配在數據區的已初始化區域。
可以觀察到已初始化的靜態全局變量和全局變量地址緊鄰,未初始化的靜態全局變量和全局變量地址緊鄰。已初始化的全局變量和未初始化的全局變量之間有一定差距。
3.適用場合
某些數據或函數不想被外部文件訪問時,可定義為靜態全局變量。好處是提供了一定程度的封裝,高內聚、低耦合、分責任。
二、靜態局部變量
1.特點
靜態局部變量只能在定義所在作用域內訪問,具有塊作用域,生命期是所處模塊裝載到所處模塊卸載。
假設寫了如下代碼,編譯無法通過,報錯“error C2065: “nTest1”: 未聲明的標識符”:
2.機制
2.1初始化為常量時
寫如下代碼,可以發現靜態全局變量,靜態局部變量,全局變量的地址緊鄰。
2.2初始為變量時
C文件中,編譯不通過:
cpp文件可編譯通過(使用VC6):
第一次給nTest1賦值時,00427E50處變為1
單步一次,變為00427E50處變為3
再單步一次,變為07
推測每一個bit保存局部靜態變量是否初始化,為0則表示未初始化,1表示已初始化,所以運行時訪問此bit時,為1則跳過初始賦值代碼。
照此推測,修改為0,應該會再次賦值:
單步,發現確實修改了
繼續單步
繼續單步
照此推測,將00427E50處改為5,則00427E58處會被賦值,其余兩處不會。先手動修改:
單步一次:
單步兩次:
單步三次:
符合推測,所以vc6編譯器用一個bit保存局部靜態變量是否初始化,為0則表示未初始化,1表示已初始化。
2.3 編譯器如何控制它跨界訪問
c文件中定義static_ABC的局部靜態變量,更改調用約定,參數,返回值,每次改一個。再去main.obj里搜索static_ABC,發現均無變化,在static_ABC周圍加上花括號,則有變化。
?static_ABC@?3??Foo1@@9@9 2層花括號,函數名Foo1
?static_ABC@?2??Foo1@@9@9 1層花括號
?static_ABC@?1??Foo1@@9@9 無額外層花括號
?static_ABC@?3??Foo2@@9@9 2層花括號,函數名2
?static_ABC@?2??Foo2@@9@9 1層花括號
?static_ABC@?1??Foo2@@9@9 無額外層花括號
在cpp文件中則和返回類型,參數類型,參數個數,調用約定均有關聯。表現如下:
//?static_ABC@?1??Foo1@@YAXXZ@4HA
//?static_ABC@?2??Foo1@@YAXXZ@4HA
//?static_ABC@?3??Foo1@@YAXXZ@4HA
//?static_ABC@?3??Foo1@@YAXH@Z@4HA
//?static_ABC@?3??Foo1@@YAXHH@Z@4HA
//?static_ABC@?3??Foo1@@YAXHHH@Z@4HA
//?static_ABC@?3??Foo1@@YAHHHH@Z@4HA
//?static_ABC@?3??Foo1@@YANHHH@Z@4HA
//?static_ABC@?3??Foo1@@YGNHHH@Z@4HA
//?static_ABC@?3??Foo1@@YINHHH@Z@4HA
//?static_ABC@?3??Foo1@@YANHHHZZ@4HA