typedef struct __va_list { void *__ap; } va_list什么意思 C语言中可变参数宏的va_start(ap, v)

作者&投稿:琦若 (若有异议请与网页底部的电邮联系)
定义va_list为结构体struct __va_list { void *__ap; }的名字。
因此,可以用
va_list astruct;
来定义,代替
struct __va_list astruct;

vaarg,vastart,vaend,valist是c语言实现可变参数使用到的一系列宏

keil4 编译不能通过__va_end()~

你右键函数,点Go to definiton看start的定义文件里有没有end的定义,我觉得是真的没有这个函数

我把你的提问分为3个问题:
1、为什么printf("%s", ap);输出不了?
2、va_start(ap, v)的定义中为什么使用二级指针?
3、va_arg(ap,t) 的定义中为什么用*(t *),它的作用是?
在解释之前,先确认一个小问题:
在C语言中,指针这种类型的大小实际上一样的,我的意思是说无论是char *a,还是int *a,或者是char **a,a这个指针变量所占用的内存空间是一样的(都是sizeof(a),究竟是等于4,还是8取决于CPU的位数)
先回答第一个问题:
你应该知道va_list的定义:typedef char * va_list;
也就是说ap可以理解为一个char *类型的变量,va_start(ap,c)这个执行之后,ap确实指向了可变参数列表中的第一个参数,注意【是ap这个指针指向了第一个参数】,而如果你的第一个参数是一个字符串(C语言中也就意味着是一个char*的变量),这样的话,ap这个指针就指向了一个char*类型的指针变量,【指向指针的指针变量是二级指针变量】这个我就不用多说了吧,所以printf("%s", ap);是无法输出的,而修改为printf("%s", *(char **)ap);应该就可以输出了!

然后是第二个问题:
这里先说一下函数调用过程中参数传递的问题:
【 函数参数是以数据结构:栈的形式存取,从右至左入栈。
  首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址,举个例子如下:
void func(int x, char *y, char z);
  那么,调用函数的时候,实参 char z 先进栈,然后是 char *y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。】
注意,x,y,z这几个变量是存放到堆栈中的,所以我们需要获得的不是y这个变量本身,而是它在堆栈中的地址,而ap这个指针变量就是保存着堆栈中函数入参的地址的,所以在va_start(ap, v)的定义中要使用&v,而不管v变量本身是什么类型的(哪怕v是一个指针变量,甚至是二级指针)&v都表示一个地址,所以可以强制转换为va_list类型(也就是char *)。

第三个问题:
要睡觉了,先自己想吧,如果还不明白,就留言追问吧。