標籤:

c語言為什麼可以通過變數名來訪問變數的值?變數的值是存儲在計算機中,那麼變數名也同時存儲在計算機中嗎?


局部變數:可能是放一個寄存器里,也可能是放內存里,地址是一個寄存器的值和一個常數的和。變數名完全不存儲。

靜態變數:放內存里,地址一般是一個鏈接時定死的常數;如果是動態鏈接庫的,那麼地址一般是一個鏈接時定死的常數加上一個動態鏈接時的基地址。變數名完全不存儲。

全局變數:放內存里,地址一般是一個鏈接時定死的常數;如果是動態鏈接庫的,那麼地址一般是一個鏈接時定死的常數加上一個動態鏈接時的基地址;如果是用-fPIC之類的方法編譯的動態鏈接庫,那麼第一次動態庫之外的代碼訪問這個變數時需要通過GOT表去查詢地址。 變數名對於非動態鏈接庫,在鏈接前存儲,鏈接後不存儲;對於動態鏈接庫在鏈接前存儲,鏈接後依然存儲(等著動態鏈接呢)。


寫一小段c語言吧

int g(int x)
{
return x + 2017;
}

然後編譯出來

g:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $2017, %eax
popl %ebp
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size g, .-g
.globl f
.type f, @function

.」 開通的行是代表鏈接有關的代碼,刪除後是:

g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $2017, %eax
popl %ebp
ret

你看這一行

movl 8(%ebp), %eax

就是原來的x在棧中,表示形式為棧址加偏移量。這一行的作用是,把x的值從參數中用棧址+偏移量取得,放到了eax里,從頭到尾都沒出現過x吶。然後下一行就是把eax+=2017了

(g其實也是不存在的,這只是個tag,為了簡寫彙編程序的。變成機器碼後就相當於沒有g了)


總結:

變數名什麼的根本就是不存在的啊,這些變數都是搞的西方那一套 棧址 + 偏移的形式,咱們不認識沒關係,能運行,效果等價就好了嘛。

(哎,這句話只能套在c或者類似c的編譯好的程序啊,某些腳本不適合啊)


變數的值也不一定存儲在計算機中,可以搜索一下 as-if rule 。

有的變數是可以被優化掉的。

變數名在開始編譯的時候就沒了。

不過函數名可能因為 __function__ 標識符而存儲。

改正:鏈接所需的變數名和函數名會存儲於對象文件中,鏈接後是不再需要的。


更新,評論區人才輩出

你說可以通過變數名訪問,那你能寫個程序,輸入變數名輸出值嗎

要局部變數也能輸出的那種


變數名對於編譯器來說是有的,然而在編譯後就沒了。


首先,內存是有地址的,也就是編號,程序在編譯時,會有一個對應的表,每個符合和地址的對應表,鏈接之後,載入到內存中,所有符號都變成了地址,變數名只是內存地址的名字,方便程序員使用


變數名會換成值的存儲地址


看看彙編的結果吧……無所謂變數名。活到最後的,都是地址


變數是個抽象的概念

變數是編譯期及之前的東西

被編譯為可執行程序這玩意就沒了


天知,地知,編譯器知,你知,我不知。除非你把那個變數也extern給我。


編譯連接完變數名就沒了


局部變數實質上也是通過編譯擁有了一個相對pc的偏移地址,所以其實除非是動態分配的東西,地址都是在編譯階段唯一的確定下來了。變數名本身也就沒有意義了,所以編譯以後變數名可以說是沒了。


全局變數在程序編譯鏈接以後,都會分配一個地址,並且編譯器也會生成map文件或者符號表文件(比如elf),這些文件中就包含有變數和地址的對應關係,調試的時候首先會去讀這些文件找到你想要看的變數的地址,然後再到這個地址上去讀取它的值。


簡單來說變數就是內存地址的助記符,方便編程人員用的,編譯鏈接之後就不存在了。。。


建議學習彙編,學完就懂了


推薦閱讀:

C 語言有什麼奇技淫巧?
如何快速便捷地打小括弧?
c語言里的char大小到底是4還是1?
為什麼不都用memmove代替memcpy?
反編譯工具能反編譯出注釋嗎?

TAG:C編程語言 |