C語言中變參函式傳參的實現示例

2022-09-24 20:00:08 字數 2292 閱讀 9912

目錄

近期在看一本書,叫做《嵌入式c語言自我修養》,寫的內容對我幫助很大,是一本好書。在第6章 c編譯器擴充套件語法精講一節,這本書給出了一些變參函式的例子:

//1.變參函式初體驗

#include

void print_num(int count,...)

}int main(void)

上面的**很好理解:定義乙個變參函式print_num,在函式內部先取得第乙個引數的位址賦值給一指標,然後將指標後移,取得後面的引數並列印出來。在main函式中,傳給print_num 6個引數,按這個邏輯,應該是列印出:

*args:1

*args:2

*args:3

*args:4

*args:5

但是結果卻出人意料:

列印出的值和傳進去的值完全不相等,甚至毫無規律可言。

上述**中,是通過取首個引數的位址,並往後移動這個指標來獲得後面引數的,那麼問題很可能出在兩個地方:

我們乙個乙個來看,先暫且假定這些引數位址是連續的,且相隔一樣的距離。那麼我們就可以聚焦於指標的移動方式了。指標移動是「args++」這一行語句來控制的。筆者修改了一下書上的**:

#include

void print_num(int count,...)

}int main(void)

主要增加了對於每個引數的位址的列印,執行結果如下:

筆者發現這個"args++"每次往後移動4個位元組,這是因為對於"int"型指標的移動操作,是以4(sizeof(int))為基本單位的。同理,對於"char"型指標的移動操作,以1(sizeof(char))為單位。

乙個"int"型指標大小如果等於4,那麼上述對於指標移動操作就沒問題。可是"int"型指標大小真的等於4嗎?

筆者用**來測試下:

#include

int main()

; struct st *structpoint;

printf("sizeof(char*):%ld\n",sizeof(charpoint));

printf("sizeof(int*):%ld\n",sizeof(intpoint));

printf("sizeof(double*):%ld\n",sizeof(doublepoint);

printf("sizeof(struct*):%ld\n",sizeof(structpoint));

return 0;

}執行結果:

可以看到,不僅"int"型指標是8位元組大小,"char"、"double"和結構體指標也都是8位元組大小。這是因為筆者電腦安裝的是64位系統。所以書上**的"int"型指標自增操作不適用於筆者,筆者將其改為「args += 2」,在dev c++這個ide中可以得到正確的結果,但在www.cppcns.comubuntu gcc下還是不對。

解決了第乙個指標移動步長問題,還是得不到正確答案。筆者懷疑引數位址很可能不連續。如何看函式的引數位址資訊?方法有很多,筆者就選一種比較快捷的方式——看彙編**。

在ubuntu的終端框輸入

gcc -s [原始檔]

就能得到乙個帶".s"字尾的彙編**檔案。

我們對比著看main函式與print_num函式中關於引數傳遞的部分:

在main函式中,各個引數被放入不同的暫存器,在print_num函式中,又從暫存器中將引數取出來放入print_num的函式堆疊中。仔細看各個引數最終被放入的堆疊位置,發現第乙個引數位址和第二個引數位址差了28個位元組,而後面的引數位址之間都是差8個位元組。這也就解釋了為何之前的**結果不對了。

所以只要在第乙個引數位址的基礎上加上偏移量28即可("char*"型)。

執行結果符合預期:

但是為什麼第乙個引數和第二個引數間隔28位元組,筆者暫時還不清楚,盲猜需要去看gcc中編譯器的相關知識。

以往對於固定引數個數的普通函式的傳參,是這樣處理的:前幾個引數放入暫存器,若個數超出,則壓入函式堆疊。筆者有點好奇變參函式是否也如此,就給這個print_num傳了18個引數:

彙編**如下:

這說明了變參程式設計客棧函式的傳參規則和普通函式並無兩樣。

在看書的時候,我喜歡邊看邊敲**,這一次照著書上敲的**執行結果不對,就有了上面的一些**過程。如果我沒有動手實踐,以後碰到類似問題時很可ts**ofeb能會矇圈。所以動手實踐很有必要。

另外,書上的東西並不一定全對,並且它的正確性需要有特定的前提做保證。比如,要是我使用的是32位系統,且編譯器在處理變參函式時將引數連續壓棧,那麼書上的**就是完全正確的。我們無需害怕這些坑,我們需要做的就是去找到這些前提條件,去找到問題的本質點,最後解決問題。

《嵌入式c語言自我修養——從晶元、編譯器到作業系統》

C語言中的變參原理

在c c 中,對函式引數的掃瞄是從後向前的。c c 的函式引數是通過壓入堆疊的方式來給函式傳引數的 堆疊是一種先進後出的資料結構 最先壓入的引數最後出來printf的第乙個被找到的引數就是那個字元指標,就是被雙引號括起來的那一部分,函式通過判斷字串裡控制引數的個數來判斷引數個數及資料型別,通過這些就...

C語言(變參函式)

c語言雖然沒有c 的函式過載特性,但也可以實現變參,但要保證第乙個引數資訊的完整性。拓展 定義變參函式時,第乙個引數一般是字串,攜帶後續變參的型別和數量資訊,變參使用三點來表示,如 void sumup const char info,再使用va list va start va arg 和va e...

C語言變參函式的實現原理

1.變參函式簡單示例 include include int accumlate int nr,va end arg return result int main 2.變參函式的實現原理 define va list void define va start arg,start arg va lis...