本篇隨筆參考了http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html
1. 在C中,當無法列出傳遞函數的所有實參的類型和數目時,可以用省略號指定參數表。例如:
void foo(...); void foo(parm_list,...);
2. 函數參數的傳遞原理
函數參數是以棧的形式存取,從右至左入棧。
參數的內存存放格式:參數存放在內存的堆棧段中,在執行函數的時候,從最后一個開始入棧。因此棧底高地址,棧頂低地址,舉個例子如下:
void func(int x, float y, char z);
那么,調用函數的時候,實參 char z 先進棧,然后是 float y,最后是 int x,因此在內存中變量的存放次序是 x->y->z。從理論上說,我們只要探測到任意一個變量的地址,並且知道其他變量的類型,通過指針移位運算,則總可以順藤摸瓜找到其他的輸入變量。舉個例子如下:
#include <iostream.h> //獲取參數列表中的所有參數,並打印 void PrintInt(int cnt, ...) { int *temp = &cnt; temp++; for (int i = 0; i < cnt; ++i) { printf("%d\n", *temp); temp++; } } int main(void) { int a = 1; int b = 2; int c = 3; int d = 4; PrintInt(4, a, b, c, d); return 0; }
執行程序后輸出:
1
2
3
4
3. 利用Va_start相關宏獲取省略號指定的參數
typedef char* va_list; void va_start ( va_list ap, prev_param ); /* ANSI version */ type va_arg ( va_list ap, type ); void va_end ( va_list ap );
說明:
1)va_list:一個字符指針,可以理解為指向當前參數的一個指針,取參必須通過這個指針進行。
2)va_start:對ap進行初始化,讓ap指向可變參數表里面的第一個參數。第一個參數是 ap 本身,第二個參數是在變參表前面緊挨着的一個變量,即“...”之前的那個參數;
3)va_arg: 獲取參數。它的第一個參數是ap,第二個參數是要獲取的參數的指定類型。按照指定類型獲取當前參數,返回這個指定類型的值,然后把 ap 的位置指向變參表中下一個變量的位置;
4)va_end:釋放指針,將輸入的參數 ap 置為 NULL。通常va_start和va_end是成對出現。
使用上面的宏獲取參數的步驟如下:
<Step 1> 定義一個 va_list 類型的變量,(假設va_list 類型變量被定義為ap);<Step 2> 調用va_start ,對ap 進行初始化,讓它指向可變參數表里面的第一個參數。
void PrintString(char*, ...); int main(void) { PrintString("Msg","This","is","a","test!",""); return 0; } //ANSI標准形式的聲明方式,括號內的省略號表示可選參數 //利用va_start相關宏,獲取參數列表中的所有參數,並打印 void PrintString (char* prev_param, ...) { //定義保存函數參數的結構 va_list pArgs; int argNo = 0; char* para; //獲取參數用的臨時變量 //對pArgs進行初始化,讓它指向可變參數表里面的第一個參數 //prev_param 是在變參表前面緊挨着的一個變量,即"..."之前的那個參數 va_start(pArgs, prev_param); while (1) { //獲取參數 para = va_arg(pArgs, char*); if ( strcmp( para, "") == 0 ) break; printf("Parameter #%d is: %s\n", argNo, para); argNo++; } //獲取所有的參數之后,將pArgs指針關掉 va_end(pArgs); }