1. 程式人生 > >理解va_list、va_start、va_arg、va_end原理及使用方法

理解va_list、va_start、va_arg、va_end原理及使用方法

  • 在程式設計中應該注意的問題和解決辦法
    雖然可以通過在堆疊中遍歷引數列表來讀出所有的可變引數,但是由於不知道可變引數有多少個,什麼時候應該結束遍歷,如果在堆疊中遍歷太多,那麼很可能讀取一些無效的資料.
    解決辦法:a.可以在第一個起始引數中指定引數個數,那麼就可以在迴圈還中讀取所有的可變引數;b.定義一個結束標記,在呼叫函式的時候,在最後一個引數中傳遞這個標記,這樣在遍歷可變引數的時候,可以根據這個標記結束可變引數的遍歷;
    下面是一段示例程式碼:
    //第一個引數定義可選引數個數,用於迴圈取初引數內容
    void arg_cnt(int cnt, ...);
    int main(int argc,char *argv[])
    {
     int int_size = _INTSIZEOF(int);
     printf("int_size=%d/n", int_size);
     arg_cnt(4,1,2,3,4);
     return 0;
    }
    void arg_cnt(int cnt, ...)
    {
     int value=0;
     int i=0;
     int arg_cnt=cnt;
     va_list arg_ptr; 
     va_start(arg_ptr, cnt); 
     for(i = 0; i < cnt; i++)
     {
      value = va_arg(arg_ptr,int);
      printf("value%d=%d/n", i+1, value);
     }
    }

    雖然可以根據上面兩個辦法解決讀取引數個數的問題,但是如果引數型別都是不定的,該怎麼辦,如果不知道引數的型別,即使讀到了引數也沒有辦法進行處理.解決辦法:可以自定義一些可能出現的引數型別,這樣在可變引數列表中,可以可變引數列表中的那型別,然後根據型別,讀取可變引數值,並進行準確地轉換.傳遞引數的時候可以這樣傳遞:引數數目,可變引數型別1,可變引數值1,可變引數型別2,可變引數值2,....
    這裡給出一個完整的例子:
    #include <stdio.h>
    #include <stdarg.h>
    const int INT_TYPE  = 100000;
    const int STR_TYPE  = 100001;
    const int CHAR_TYPE  = 100002;
    const int LONG_TYPE  = 100003;
    const int FLOAT_TYPE = 100004;
    const int DOUBLE_TYPE = 100005;
    //第一個引數定義可選引數個數,用於迴圈取初引數內容
    //可變引數採用arg_type,arg_value...的形式傳遞,以處理不同的可變引數型別
    void arg_type(int cnt, ...);
    //第一個引數定義可選引數個數,用於迴圈取初引數內容
    void arg_cnt(int cnt, ...);
    //測試va_start,va_arg的使用方法,函式引數在堆疊中的地址分佈情況
    void arg_test(int i, ...);
    int main(int argc,char *argv[])
    {
     int int_size = _INTSIZEOF(int);
     printf("int_size=%d/n", int_size);
     arg_test(0, 4);
     
     arg_cnt(4,1,2,3,4);
     arg_type(2, INT_TYPE, 222, STR_TYPE, "ok,hello world!");
     return 0;
    }
    void arg_test(int i, ...)
    {
     int j=0;
     va_list arg_ptr; 
     
     va_start(arg_ptr, i); 
     printf("&i = %p/n", &i);//列印引數i在堆疊中的地址
     printf("arg_ptr = %p/n", arg_ptr);
     //列印va_start之後arg_ptr地址,
     //應該比引數i的地址高sizeof(int)個位元組
     //這時arg_ptr指向下一個引數的地址
     
     j=*((int *)arg_ptr);
     printf("%d %d/n", i, j); 
     j=va_arg(arg_ptr, int);
     printf("arg_ptr = %p/n", arg_ptr);
     //列印va_arg後arg_ptr的地址
     //應該比呼叫va_arg前高sizeof(int)個位元組
     //這時arg_ptr指向下一個引數的地址
     va_end(arg_ptr);
     printf("%d %d/n", i, j);
    }
    void arg_cnt(int cnt, ...)
    {
     int value=0;
     int i=0;
     int arg_cnt=cnt;
     va_list arg_ptr; 
     va_start(arg_ptr, cnt); 
     for(i = 0; i < cnt; i++)
     {
      value = va_arg(arg_ptr,int);
      printf("value%d=%d/n", i+1, value);
     }
    }
    void arg_type(int cnt, ...)
    {
     int arg_type = 0;
     int int_value=0;
     int i=0;
     int arg_cnt=cnt; 
     char *str_value = NULL;
     va_list arg_ptr; 
     va_start(arg_ptr, cnt);
     for(i = 0; i < cnt; i++)
     {
      arg_type = va_arg(arg_ptr,int);
      switch(arg_type)
      {
      case INT_TYPE:
       int_value = va_arg(arg_ptr,int);
       printf("value%d=%d/n", i+1, int_value);
       break;
      case STR_TYPE:
       str_value = va_arg(arg_ptr,char*);
       printf("value%d=%d/n", i+1, str_value);
       break;
      default:
       break;
      }
     }
    }
    以上是我個人的見解,不對的地方希望大家指正,發表看法,我不勝感謝!!!