1. 程式人生 > >va_start和va_end詳解

va_start和va_end詳解

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 <stdio.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;
}

執行程式後輸出: 




4


3. 利用Va_start相關巨集獲取省略號指定的引數

 下面是 <stdarg.h> 裡面重要的幾個巨集定義如下:
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 進行初始化,讓它指向可變引數表裡面的第一個引數。      <Step 3> 獲取引數,並使用引數。     <Step 4> 獲取所有的引數之後,將 ap 指標關掉。     給出一個例子如下:
#include 〈stdio.h〉 
#include 〈string.h〉 
#include 〈stdarg.h〉 
/*ANSI標準形式的宣告方式,括號內的省略號表示可選引數*/ 
int demo( char msg, ... ) 
       /*定義儲存函式引數的結構*/
   va_list argp; 
   int argno = 0; 
   char para; 
     /*argp指向傳入的第一個可選引數,msg是最後一個確定的引數*/ 
   va_start( argp, msg ); 
   while (1) 
       { 
        para = va_arg( argp, char); 
           if ( strcmp( para, "") == 0 ) 
               break; 
           printf("Parameter #%d is: %s\n", argno, para); 
           argno++; 
va_end( argp ); 
/*將argp置為NULL*/
return 0; 
}
void main( void ) 
   demo("DEMO", "This", "is", "a", "demo!", "");