對於可變參數的函數可以,使用下面的宏來,獲取輸入的每一個參數
這些宏定義在stdarg.h中
-
typedef char *va_list;
-
va_start宏,獲取可變參數列表的第一個參數的地址(list是類型爲va_list的指針,param1是可變參數最左邊的參數):
#define va_start(list,param1) ( list = (va_list)¶m1+ sizeof(param1) )
-
va_arg宏,獲取可變參數的當前參數,返回指定類型並將指針指向下一參數(mode參數描述了當前參數的類型):
#define va_arg(list,mode) ( (mode *) ( list += sizeof(mode) ) )[-1]
-
va_end宏,清空va_list可變參數列表:
#define va_end(list) ( list = (va_list)0 )
由於調用函數之前,會把參數從右到左依次壓入棧中,所以只要確定一個參數的位置,其他的位置就可以找到
例如定義:fun(int n, …) 通過 fun(3,a, b, c)調用
如下圖會被壓入棧,確定3的位置,每次調用va_arg, ap指針上移,並把棧裏的值取出
#include<stdio.h>
#include<stdarg.h>
int fun(int num_args, ...)
{
int val = 0;
va_list ap;
int i;
va_start(ap,num_args);
for(i = 0; i < num_args; i++)
{
val = va_arg(ap, unsigned);
printf("%d\n", val);
}
va_end(ap);
return val;
}
int main()
{
fun(3, 10, 20, 30);
}
結果
kayshi@ubuntu:~/code/Test$ ./a.out
10
20
30
一:直接利用地址指針的變化把每個參數打印出來
#include<stdio.h>
struct person{
char *name;
int age;
char socre;
};
int put_test(const char* format, ...)
{
char *p = (char *)&format;//讓p指向字符串format
int i;
struct person per;
char c;
double d;
p = p + sizeof(char *);//p加一個指針的大小指向下一個元素的位置
i = *((int *)p);//取出整形數據
printf("arg1: %s\n", format);
printf("arg1: %d\n", i);
p = p + sizeof(int);//p加上整形數據的大小,指向下一個元素
per = *((struct person *)p);//取出結構體,給per
printf("arg3: .name = %s, age = %d, socre = %c\n", per.name, per.age, per.socre);
p = p + sizeof(struct person);//p加上結構體的大小,指向下一個元素
c = *((char *)p);//取出p指向的字符
printf("arg4: %c\n", c);
p = p + sizeof(char) + 3;//讓p加上字符的大小,在加上3,爲了字節對齊
d = *((double *)p);//取出double類型的數據
printf("arg5: %f\n", d);
return 0;
}
int main()
{
struct person per = {"kayshi", 29, 'A'};
put_test("abcd", 123, per,'k', 2.79);
}
-m32 表示:在64位上的ubantu上以32位進行編譯
kayshi@ubuntu:~/code/put_test$ gcc -m32 put_test.c
kayshi@ubuntu:~/code/put_test$ ./a.out
arg1: abcd
arg1: 123
arg3: .name = kayshi, age = 29, socre = A
arg4: k
arg5: 2.790000
二:使用頭文件<stdarg.h>提供的宏來進行打印參數
#include<stdio.h>
#include<stdarg.h>
struct person{
char *name;
int age;
char socre;
};
int put_test(const char* format, ...)
{
int i;
va_list p;
struct person per;
char c;
double d;
va_start(p, format);//p會執行format的下一元素
i = va_arg(p, int);//取出p指向位置的值(整形),p加一個整數據的大小
printf("arg1: %s\n", format);
printf("arg1: %d\n", i);
per = va_arg(p, struct person);//取出p指向位置的值(結構體),p加一個結構體的大小
printf("arg3: .name = %s, age = %d, socre = %c\n", per.name, per.age, per.socre);
c = va_arg(p, int);//取出p指向位置的值(char),p加一整形的大小(字節對齊)
printf("arg4: %c\n", c);
d = va_arg(p, double);//取出p指向位置的值(double),p加一double類型的大小
printf("arg5: %f\n", d);
va_end(p);
return 0;
}
int main()
{
struct person per = {"kayshi", 29, 'A'};
put_test("abcd", 123, per,'k', 2.79);
}
kayshi@ubuntu:~/code/put_test$ gcc -m32 put_test1.c
kayshi@ubuntu:~/code/put_test$ ./a.out
arg1: abcd
arg1: 123
arg3: .name = kayshi, age = 29, socre = A
arg4: k
arg5: 2.790000
kayshi@ubuntu:~/code/put_test$
三:把頭文件中的宏定義提取到文件中,並把頭文件註釋
這是在stdarg.h中定義的宏
typedef char* va_list; va_list是char *
下面這句是爲了字節對齊,當sizeof(n)=1/2/4時,那麼_INTSIZEOF(n) = 4
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) -1) & ~(sizeof(int) -1))
下面這句表示,指針ap指向了字符串v的下一個位置
#define va_start(ap, v) (ap = (va_list)&v + _INTSIZEOF(v))
下面3句:取出指針ap指向的內容,並把指針ap在加本次佔的大小,就會指向下一個元素
第一句時頭文件本身的語句,不好理解。 後面兩句也可以實現同樣的功能
#define va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
#define va_arg(ap, t) (ap = ap + _INTSIZEOF(t), *(t*)(ap - _INTSIZEOF(t)))
#define va_arg(ap, t) (*(t*)(ap = ap + _INTSIZEOF(t), ap - _INTSIZEOF(t)))
讓指針ap指向null,避免野指針
#define va_end(ap) (ap = (va_list)0)
#include<stdio.h>
//#include<stdarg.h>
typedef char* va_list;
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) -1) & ~(sizeof(int) -1))
#define va_start(ap, v) (ap = (va_list)&v + _INTSIZEOF(v))
//#define va_arg(ap, t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))
//#define va_arg(ap, t) (ap = ap + _INTSIZEOF(t), *(t*)(ap - _INTSIZEOF(t)))
#define va_arg(ap, t) (*(t*)(ap = ap + _INTSIZEOF(t), ap - _INTSIZEOF(t)))
#define va_end(ap) (ap = (va_list)0)
struct person{
char *name;
int age;
char socre;
};
int put_test(const char* format, ...)
{
int i;
va_list p;
struct person per;
char c;
double d;
va_start(p, format);
i = va_arg(p, int);
printf("arg1: %s\n", format);
printf("arg1: %d\n", i);
per = va_arg(p, struct person);
printf("arg3: .name = %s, age = %d, socre = %c\n", per.name, per.age, per.socre);
c = va_arg(p, int);
printf("arg4: %c\n", c);
d = va_arg(p, double);
printf("arg5: %f\n", d);
va_end(p);
return 0;
}
int main()
{
struct person per = {"kayshi", 29, 'A'};
put_test("abcd", 123, per,'k', 2.79);
}
kayshi@ubuntu:~/code/put_test$ gcc -m32 put_test1.c
kayshi@ubuntu:~/code/put_test$ ./a.out
arg1: abcd
arg1: 123
arg3: .name = kayshi, age = 29, socre = A
arg4: k
arg5: 2.790000