在 C 語言,變數名字又分成內部名字和外部名字。名字編譯後會被對映成資料的地址(可以是絕對地址或者偏移地址),如何透過地址來訪問資料,本身就是程式碼的一部分。而外部變數的地址,在編譯的時候還不能完全確定,就先空著,等連結的時候再進行回填。為支援連結,外部變數的名字經過 name mangling 後需要儲存在符號表當中。
C 語言寫的程式,在執行時是不需要變數名字的,已經對映成資料的地址,成為程式碼的一部分了。
那變量表示的資料儲存在哪裡?儲存在記憶體當中啊。更準確地說是儲存在虛擬記憶體中。
圖所示的就是 32 位 Linux 程式在執行時的虛擬地址空間。程式資料可以儲存在棧當中,也可以儲存在堆當中。另外全域性資料(對應於全域性變數),一開始就分配好的,空間大小是固定的。而棧空間、和堆空間的大小是會變化的,見箭頭方向。
先將程式變數這個概念,細分成兩部分。一是變數本身的名字,二是變數引用的資料。這樣問題就會轉換成,變數的名字儲存在那裡?變數引用的資料儲存在哪裡?
在 C 語言,變數名字又分成內部名字和外部名字。名字編譯後會被對映成資料的地址(可以是絕對地址或者偏移地址),如何透過地址來訪問資料,本身就是程式碼的一部分。而外部變數的地址,在編譯的時候還不能完全確定,就先空著,等連結的時候再進行回填。為支援連結,外部變數的名字經過 name mangling 後需要儲存在符號表當中。
C 語言寫的程式,在執行時是不需要變數名字的,已經對映成資料的地址,成為程式碼的一部分了。
那變量表示的資料儲存在哪裡?儲存在記憶體當中啊。更準確地說是儲存在虛擬記憶體中。
圖所示的就是 32 位 Linux 程式在執行時的虛擬地址空間。程式資料可以儲存在棧當中,也可以儲存在堆當中。另外全域性資料(對應於全域性變數),一開始就分配好的,空間大小是固定的。而棧空間、和堆空間的大小是會變化的,見箭頭方向。
程式中用到的所有地址,都不是真實的記憶體地址,只是虛擬記憶體地址。程式用到虛擬記憶體,分成一頁頁,比如每頁是 4K。有些頁確實儲存在真實的記憶體當中,但有些頁儲存在磁碟當中,有些頁就單純空著。作業系統和硬體會將虛擬記憶體對映成真實的記憶體,具體的程式不用關心。當程式訪問的虛擬記憶體頁並不在真實記憶體中時,就會觸發缺頁中斷,作業系統這時就將對應的頁載入到真實記憶體,再重新訪問。假如這時真實記憶體滿了,就將太久沒有使用的頁轉出到磁碟當中。程式就認為自己獨立擁有了 4G 虛擬地址空間。
程式啟動時,也不需要真的將資訊載入到真實的記憶體,而只需要建立對映。這樣當程式執行時,就產生缺頁中斷。需要用到的資訊就很自然載入到真實記憶體當中,而沒有真正用到的資訊可以一直保留在磁碟當中。因此就算真實記憶體只有 1G,也可以執行 4G 的程式。
更具體的問題很難一下子就完全明白,再看看書吧,慢慢就會知道的了。知識是網狀的,而不是線性的,沒有可能從頭看到尾就一下子全明白,也不用一下子就全部弄清楚。