对于可变参数的函数可以,使用下面的宏来,获取输入的每一个参数
这些宏定义在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指针上移,并把栈里的值取出
在32位系统中,可以这样。但是64位系统中这样使用不行
#include<stdio.h>
long sum(int i, ...)
{
int *p, j;
long s = 0;
p = &i + 1;
for(j = 0; j < i; j++)
s += p[j];
return s;
}
void main(void)
{
long Sum=0;
Sum = sum(3, 1, 2, 3);
printf("%ld\n", Sum);
}
加上-m32 编译32位的执行程序。
kayshi@ubuntu:~/code/Test$ gcc -m32 variable_parameter.c
kayshi@ubuntu:~/code/Test$ ./a.out
6
在64位系统中可以使用标准库中的函数来实现
#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