C C 函式引數讀取順序

2021-09-07 22:15:09 字數 1439 閱讀 6624

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

1 #include 2

3int

main()

按照從右向左讀取,想當然的結果應該是: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的實現是一致的。

C C 函式引數壓棧順序

函式引數的壓棧順序,一般由編譯器決定,不同的編譯器可能規則不一樣。但是一般情況下,是從右到左。談一談printf 函式,printf函式的原型是 printf const char format,沒錯,它是乙個不定參函式,那麼我們在實際使用中是怎麼樣知道它的引數個數呢?這就要靠format了,編譯器...

c c 的函式引數壓棧順序

c c 的函式引數壓棧順序 c c 的函式引數壓棧順序.曾看到一篇文章上面說 c c 引數壓棧順序是從右到左,pascal引數壓棧是從左到右.為了這句話丟了很多次人.無所謂了,反正咱臉皮厚.總結一下 編譯出來的c c 程式的引數壓棧順序只和編譯器相關 下面列舉了一些常見的編譯器的呼叫約定 vc6 呼...

C C 語言函式中引數的入棧順序

對於函式,之前認為會用就行了,對其中的原理並不是很了解,就比如函式中引數的入棧順序 在這說明一下,函式的引數是儲存在棧中的,還有一些區域性變數也是存放在棧中 這個問題 於某網際網路的面試題,當然答得很不好,查了很多大牛的部落格做一下總結。include using namespace std voi...