C/C++多引數函式引數的計算順序與壓棧順序

_程式兔發表於2020-05-30

一、前言

  今天在看Thinking in C++這本書時,書中的一個例子引起了我的注意,具體是使用了下面這句

  

單看這條語句的語義會發現僅僅是使用一個簡單的string的substr函式將所得子串push_back到strings。但是在閱讀時我卻對於substr的引數傳遞產生了疑惑,到底是先執行了++current,還是先執行了last-current?

經過查閱資料,發現了兩個相關知識點----引數的計算順序與壓棧順序。

二、引數壓棧順序

  C/C++中規定了函式引數的壓棧順序是從右至左,對於含有不定引數的printf函式,其原型是printf(const char* format,…);其中format確定了printf的引數(通過format的%個數判斷)。假設是從左至右壓棧,那麼先入棧的是format(這裡我們簡化理解為引數個數),然後依次入棧未知引數,此時想要知道引數個數,就必須找到format,而要找到format,就必須知道引數個數,陷入一個邏輯矛盾。因此C/C++中規定引數壓棧為從右至左,這樣對於不定引數,最後入棧的是引數個數,只需要取棧頂就可以得到。可以通過下面的程式驗證:

#include <stdio.h>
void foo(int x, int y, int z)
{
        printf("x = %d at [%X]\n", x, &x);
        printf("y = %d at [%X]\n", y, &y);
        printf("z = %d at [%X]\n", z, &z);
}
int main(int argc, char *argv[])
{
        foo(100, 200, 300);
        return 0;
}

通過輸出結果可以看到x,y,z的棧內地址依次是 x < y < z;而棧的生長方向是從高到低,也就是先入棧的佔高地址,因此z先入棧,其次是y,最後是x,即壓棧順序從右至左。

三、引數計算順序

  知道引數壓棧順序從右至左,是不是可以得出結論strings.push_back( s.substr(++current, last-current)); 先執行last-current,再執行++current呢?其實不然,先執行哪個引數和引數的計算順序有關,而C/C++中沒有規定函式引數的計算順序,即計算順序依照編譯器,編譯器規定從右至左計算就先執行last-current,規定從左至右就先執行++current,筆者試過codeblocks與vscode的計算順序都是從右至左。

  也正因為函式引數的計算順序依照編譯器的實現,因此,C/C++的程式碼編寫中並不支援編寫諸如 func(++x, x+y)這種的程式,在不同編譯器下可能產生不同的結果,所以上述程式碼應該分開寫為:

        int len = last - current;
        ++current;
        strings.push_back(
            s.substr(current, len));

 

相關文章