理解va_list、va_start、va_arg、va_end原理及使用方法
阿新 • • 發佈:2019-02-03
雖然可以通過在堆疊中遍歷引數列表來讀出所有的可變引數,但是由於不知道可變引數有多少個,什麼時候應該結束遍歷,如果在堆疊中遍歷太多,那麼很可能讀取一些無效的資料.
解決辦法: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;
}
}
}
以上是我個人的見解,不對的地方希望大家指正,發表看法,我不勝感謝!!!