linux c/c++ 學習總結(3)-- static關鍵字

被static修飾的內容表示連接性爲內部,即static的函數或變量只會在當前翻譯單元內部使用,具有內部連接性的名字在編譯時就會生成,不需要等到鏈接時或是運行時,與之相對的是連接爲外部的變量和函數(static的反義詞可以認爲是extern),由於具有外部連接性的名字可能會被多個文件共享使用,根據“單一定義規則ODR”,我們應當保證他們不能重名(函數名和變量名相同也算重名,因爲在連接器眼中他們都是強符號),如果重名了,連接器在“符號解析”階段報錯。

需要注意的是通常在函數中定義的非static局部變量是在運行時棧幀上維護的,但static局部變量不是在棧幀上維護的,他們在編譯時就確定了,不會等到運行時。

要深入理解static的效果是如何做出來的,需要了解elf文件格式,虛擬存儲器的頁式管理機制等概念。簡單點說,我們通過gcc生成的可執行文件是一個elf(可執行可連接格式)格式的,它裏面包含一個“elf頭”,“段頭部表(又稱爲程序頭部表)”,“節頭部表”,“代碼段”,“數據段”,可以通過readelf(1)查看詳情。當可執行文件被加載器加載時,bash會fork出一個子進程,並通過內核中得“struct vm_area struct”生成對應的“代碼段”和“數據段”,並賦予相應的權限,通常是“代碼段”爲r_x,“數據段”爲rw_,x與w兩種權限一般是互斥的,這樣做是爲了限制可執行代碼的區域,防止通過緩存區溢出的技巧進行攻擊。elf文件中“段頭部表”中有程序的“代碼段”和“數據段”到虛擬頁上相應區域的映射,當程序啓動時,物理內存是“冷”的,會產生一種暫時性的異常“缺頁”,此時虛擬內存會把相應的頁找到並放入物理內存的頁框中,由於程序的“局部性”原理,設計良好的程序的工作集(經常使用的頁面)往往能駐留在頁框中,程序便能良好運行。

在“代碼段”中又包含了3個節(section)“.data”表示代碼段,“.rodata”只讀數據段如c++中const變量,“.init”包含初始化信息;“數據段”中2個節“.data”表示已初始化的全局變量和static變量,“.bss”表示沒有初始的全局變量和static變量以及初始化爲0的全局變量和是static變量,這個節中得數據不會佔用磁盤空間,在程序加載時操作系統統一填充零,需要注意的是在c語言中,目標文件(.o)中的沒有初始化的全局變量是放在一個“僞節common”中,因此可以在不同文件中定義多個同名的未初始化全局變量,在鏈接時鏈接器會隨機挑選一個,這種特性可能會帶來潛在的隱患,可以通過選項“-fno-common”,來關閉它。

知道了以上這些內幕,應該就能理解爲什麼static變量和其他變量不相同。
回到c++中static變量,類中聲明的static成員變量和成員函數,是類對象共享的,它不依附某一個對象,可以直接通過“classname::”調用,當然也可以通過“對象::”調用,它的初始化要在類外進行,需要加上”classname::”,但不能加上“static”;static成員函數不能調用非static成員函數,因爲static成員函數沒有隱式的“this”指針,但是非static成員函數有“this”指針;const也不能放在函數最後來修飾static成員函數,原因也是因爲static函數沒有“this”指針。

關於static等與作用域相關的關鍵字的理解,可以參看csapp一書中“鏈接”章節。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章