C語言可變引數講解

2021-09-11 05:34:05 字數 1745 閱讀 2737

一.可變引數

簡介:什麼是可變引數?

答:就是在函式宣告時引數是...表示後面可以有多個不同型別的引數傳遞進來

最著名的就是"printf",通過格式佔位符來判斷後面的引數型別!

printf("%c,%d",'c',1);

但是如果給:

printf("%s%f",'c',2);

就會列印不正確值,因為printf不會智慧型判斷型別,只能通過佔位符,原因:

在執行階段,沒有型別一說,只有資料,所以這些是編譯階段來確定生成怎樣的jmp來跳轉可執行**段,但是如果要強行想要其智慧型判斷是非常複雜,會大大降低使用可變引數的源**編譯速度!

原理:因為函式引數規則,預設都是從右向左入棧,而棧每次入棧都會讓sp棧指標發生改變,所以棧也就是先入後出!

如果使用可變引數,在編譯期間,編譯器會隱式計算傳的引數數量,然後申請4位元組(看編譯器)的指標來指向這些值!

實踐:好了知道了原理,那麼我們就可以來實踐了:

int test(...)

int main()

這樣的寫法是錯誤的,不能直接使用可變引數,因為上面說過了,可變引數後面編譯器會隱式增加指標來指向呼叫函式傳遞進來的參棧記憶體!

所以需要在函式第乙個引數裡手動宣告乙個變數作為引數,好用它來做偏移:

int test(int num,...)

int main()

然後在獲取num的位址,然後在+sizeof(int)(編譯器根據位數不同會調整型別大小,這樣做更加泛型)的偏移

int test(int num,...)

int main()

這裡注意上面為什麼是+1而不是+sizeof(int)?

原因:列印輸出:

正確是我們想要的值,注意我們無法手動獲取引數大小,因為如果依靠指向下乙個判斷是否為null的是完全不行的!

一般判斷null都是用來判斷自己當前程式段的指標內容,其它棧空間裡的很多地方都是程式設計師自己來開闢的,是編譯器為了保證執行環境的正確化會額外增加一些依賴庫在裡面!誰也不知道自己偏移後的下乙個位址是什麼!因為檔案到記憶體空間對映編譯器每次都會打亂重新分配的!(詳細參考pe檔案到記憶體的對映,以及編譯器的實現)

所以這也是為什麼在使用可變引數時如果定義為:

int test(...)
會報:至少需要乙個明確型別數量的錯誤了,編譯器要求程式設計師指定長度!

所以我們需要在第乙個引數裡給定大小,然後迴圈偏移就可以獲取到所有的值了!

編譯器也為我們提供了可以獲取這些值的:va_list鍊錶

存在於:stdarg.h標頭檔案中

va_list鍊錶用於儲存第乙個引數的位址,好用於後續的偏移

va_start是將第乙個引數的位址放入到煉表裡

va_arg可以從煉表裡獲取首位址然後根據第二個大小進行位址偏移

va_end釋放鍊錶

用法:

int test(int num, ...)

va_end(vga);

return 1;

}int main()

列印:

c 語言可變引數

1 當無法列出傳遞函式的所有實參的型別和數目時,可用省略號指定參數列 void foo void foo parm list,2 函式引數的傳遞原理 函式引數是以資料結構 棧的形式訪問,從右至左入棧.eg include void fun int a,int main output 12 343 獲...

c語言可變引數

ifdef debug print info x printk x,else print info x endif 一 什麼是可變引數 我們在c語言程式設計中有時會遇到一些引數個數可變的函式,例如printf 函式,其函式原型為 int printf const char format,它除了有乙個...

C語言可變引數

在gnu c中,巨集可以接受可變數目的引數,就象函式一樣,例如 1 2 define pr debug fmt,arg.printk kern debug fmt,arg 用可變引數巨集 variadic macros 傳遞可變參數列 你可能很熟悉在函式中使用可變參數列,如 1 voidprintf...