C/C++函數參數讀取順序


說到C/C++函數參數讀取順序,很多人都知道在入棧時是從右至左的,可是真的有那么簡單嗎?先看一個例子:

1 #include <cstdio>
2 
3 int main() {
4     int a = 10;
5     printf("%d %d %d\n", a++, ++a, a);
6     return 0;
7 }

按照從右向左讀取,想當然的結果應該是:11 11 10,執行后a = 12。可是真的是這樣嗎?寫個程序驗證一下吧,於是就得到了下面的結果:

 

 

 

 

 

 

 

 

很奇怪,這是為什么呢?要搞清楚什么情況恐怕得從匯編代碼入手,那我們就看一下匯編代碼嘍:

     通過查看匯編代碼,我們發現在參數入棧時順序的確是從右向左入棧,但是在入棧前先把參數列表里的表達式算一遍得到表達式的結果,最后再把這些運算結果統一入棧,這就解釋了為什么第三個參數a會輸出12,因為執行完a, ++a, a++后a = 12。那為什么第一項++a會輸出11呢,這就要看C++中的++運算符的實現機制了,通過上面的匯編代碼,可以看到:

++a對應的是: addl $0x1, 0x1c(%esp)

也就是說直接執行a+1;

a++對應的是: mov 0x1c(%esp), %eax

                     addl $0x1, 0x1c(%esp)  

先把執行到此時的a的值備份到%eax,然后再執行+1操作,所有的表達式都執行完了,該將參數入棧了。怎么入呢?

不管是a,還是++a,入棧的代碼都是:mov  0x1c(%esp), %edx

                                                  mov  %edx,0xc(&esp) | mov  %edx,0x8(&esp)

也就是說直接從變量a所在的內存地址中取值。所以得到的肯定是所有運算都執行完的結果,也就是12。

而a++的入棧代碼是:mov %eax,0x4(&esp)

直接把之前備份的值入棧了,而備份的時候a所在地址中保存的值是11,所以%eax中的值是11。

這樣一來是不是就說通了。

總結下來有兩點:

1. 在將參數入棧前,編譯器會先把參數的的表達式都處理掉,哪怕這些運算會改變其中某些參數的值,

2.對於a++操作,編譯器會開辟一個緩沖區來保存當前a的值,然后再對a操作,取值時是從緩沖區取,而不是直接從a的內存地址里取。

 

最后再驗證一下理論,a = 10, printf("%d %d %d %d\n", a++, ++a, a, a++),結果應該是:12 13 13 10!

哦耶!對了,撒花!

 

好吧,參數列表中表達式的計算順序並沒有在C/C++中明確定義,每個編譯器可以自由發揮,有自己的實現。沒有給定編譯環境的話結果是未知的,不過至少最新版的g++與msvs2013的實現是一致的。


免責聲明!

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



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