在實驗中觀察指標——C++ 函式引數的壓棧順序

計算機知識雜談發表於2022-01-09

前言

好久沒寫東西了,突發奇想,寫寫函式引數的壓棧順序
先看看這個問題
https://q.cnblogs.com/q/137133/

然後看我簡化的程式碼,猜輸出結果是多少?

#include<bits/stdc++.h>
using namespace std;
int main(){
    int i=0;
    printf("%d %d",i++,i--);
    return 0;
}

根據++和--的特性,i++的時候數值不變,輸出0,i--時i才加上1,輸出1。
事實是這樣嗎?我在多臺編譯器上執行,輸出的結果都是:

-1 0

根據我之前寫過的指標篇的內容,函式的區域性變數儲存在棧中,都是獨立的,引數同樣儲存在棧中,才導致了swap函式改變函式引數必須使用指標。
那麼,函式引數,在棧中是如何排列的呢?順序?倒序?
我們寫一個簡短的程式碼,來實驗一下。

#include<bits/stdc++.h>
using namespace std;
void test(int a,int b){
    printf("a..%p, b..%p",&a,&b);
}
int main(){
    int a,b;
    test(a,b);
}

由於是地址,不同編譯器的結果不同。但肯定的是,a比b大4。
如果多加幾個變數進去,我們發現,地址的大小從大到小遞減。

棧模型

圖源:《征服C指標》

從這張圖中可以看出,C語言中,引數是從後往前堆積在棧中的。這種處理方式的好處在於,無論有多少個引數,總能找到第一個引數的地址,這樣就可以順次找到後面的引數。否則,從後往前,就無法找到第一個引數,也無法實現可變長引數的功能。
例如在printf中,我們找到第一個引數的位置,例如"%d %s",就可以順次解析後面的地址的引數,因為引數是連續在記憶體排列的。

問題解釋

既然引數是從後往前放入棧中的,那麼,我們就可以解釋這個問題了。

#include<bits/stdc++.h>
using namespace std;
int main(){
    int i=0;
    printf("%d %d",i++,i--);
    return 0;
}

開頭的程式碼。如果編譯成組合語言進行執行,應該是這個樣子(如果有錯誤請指正,手寫的)

sub [i],1 ;i--
push [i]
add [i],1 ;i++
push [i]
push offset string "%d %d" ;"%d %d"
call dword ptr_printf ;呼叫printf

在組合語言中,push是將引數壓入棧的一個指令,由於這篇文章不是講彙編的,大家看看就好。
因此,在推入棧的時候,先執行了i--,再執行i++,結果也當然是這樣了。

相關文章