提问人:ILoveC 提问时间:1/20/2021 最后编辑:ILoveC 更新时间:1/23/2021 访问量:282
va_list总是静态的吗?
Is va_list always static?
问:
void va_test2(va_list ap2)
{
printf("va_test 2 : %d\n", va_arg(ap2, int));
}
void va_test1(const char* str, ...)
{
va_list ap1;
printf("%s\n", str);
va_start(ap1, str);
va_test2(ap1);
printf("va_test 1 : %d\n", va_arg(ap1, int));
va_test2(ap1);
}
int main(void)
{
va_test1("this is a test", 1, 2, 3);
}
result :
this is a test
va_test 2 : 1
va_test 1 : 2
va_test 2 : 3
result I expected:
this is a test
va_test 2 : 1
va_test 1 : 1
va_test 2 : 2
在我看来,在“va_test1”中初始化va_list“ap1”后,它被复制到“va_test2”中的局部变量“ap2”。
因此,在 va_arg(ap, int) 在 'va_test2' 中增加va_list 'ap2' 后,应该不会影响原来的va_list 'ap1'。
但该行为表明,增加的参数实际上影响了“ap1”。
据我所知,va_arg是定义的
#define va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(T)) - _INTSIZEOF(T)))
这直接增加了发送的指针。
在我的结论中,va_list似乎在任何地方都是静态行为。
你能告诉我这是对的吗,为什么它显示静态行为?
答:
这里没有证据表明有静态存储持续时间。只有证据可以访问 中的数据或引用的数据,这可能是因为是数组或指向数据的指针或包含此类指针的结构。ap1
va_test2
ap1
ap1
评论
va_list总是静态的吗?
不,不是。它通常是本地的。
未定义代码的行为。通话结束后,您唯一能做的就是拨打 .我们可以阅读 C11 7.16p3:va_test2(ap1);
ap1
va_end(ap1)
[...]对象 ap 可以作为参数传递给另一个函数;如果该函数使用参数 ap 调用 va_arg 宏,则调用函数中 AP 的值是不确定的,应在进一步引用 AP 之前传递给 va_end 宏。
不过,可以通过拒绝定义来解释代码的行为。您得到的行为与宏的显示定义不匹配,因为宏确实修改了变量的值(除非存在隐藏:),因此拒绝该定义将是前进的方式。va_arg
#define ap *ap
据我所知,va_arg是定义的
#define va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(T)) - _INTSIZEOF(T)))
我会说,很可能不是。如果您使用的是 x86 架构,则使用 x86 abi 指定的不同寄存器传递不同的数据类型(例如,请参阅第 21 页和第 52 页周围的整个部分)(请参阅此答案)。这一定义很可能适用于少数有限的情况。如今,通常是一些编译器的魔术,例如 gcc/stdarg.h __builtin_va_arg。va_arg
如果是数组类型或指向堆栈上数据的指针,则可以解释该行为。它是一个结构的数组,如上面的 abi 中所定义。va_list
x86
// cross my fingers these are right
typedef int va_list[1];
#define va_start(ap, a) (*ap = (int*)&a);
#define va_arg(ap, t) (*(t*)((*ap += _INTSIZEOF(T)) - _INTSIZEOF(T)))
由于代码的行为未定义,因此编译器实现者并不关心此类代码的行为方式 - 它可以以任何方式运行。因此,你不能 - 你不能期望从这样的代码中得到任何东西 - 通常期望鼻恶魔生成。result I expected:
评论
va_arg
va_list
不,它不能是静态的。原因是静态将不允许函数可重入,因此您应该不能在不同的线程中使用可调参数函数。
va_list
是普通类型,如指针。唯一的区别是,这是一个非常特殊的指针,指向参数列表中的变量,事实上,根据ABI,它可能非常特殊(因为例如,如果您的ABI允许在寄存器中传递参数,它应该引用寄存器)
在古代 C 编译器中,是一个简单的指针,它被强制转换为指向传递给宏的类型的指针,以便能够使用它进行指针运算,并将其推进以指向列表中的下一个参数。这些是与过程相关的语义。但这种指针运算必须很特殊,因为指向堆栈的不同 cpus 更新指针通常以与普通数据指针不同的方式对齐数据。这意味着,例如,如果在 64 位体系结构中传递一个短数字或一个(32 位整数),出于效率原因,可能会将一个完整的 64 位字推送到堆栈以保存单个字符(或整数)。无论如何,它是一个架构最依赖的部分,这就是大多数编译器特别对待它的原因(在 gcc 中,它被映射到 )。但从本质上讲,它是一个指针(可能是通过引用传递的,你不知道)。va_list
va_arg
int
__gnuc_va_list
在 C 语言中,有一种通过引用传递变量的方法,包括将变量声明为该变量的一个元素的数组。如果你在实际的参数列表中写下数组名称,你确实是通过引用传递一个变量,因为变量名是对只有一个元素的数组的第一个元素的引用:
typedef void *va_list[1];
#define va_start(_l, _first) do{_l[0] = &(_first)+1;}while(0)
#define va_arg(_l, _typ) (*(_typ *)(_l[0] = (_typ *)_l + 1));
#define va_end(_l) /* just nothing */
上面的代码将为您提供指针的语义,当您将指针作为参数传递给函数时,该指针将通过引用传递(因为您已经定义了类型,当您按名称传递它时,您使用的是引用)
#include <stdio.h>
/* these are implemented in a library and the user doesn't
* see the details on how my_type is defined. */
typedef int va_list_fake[1];
void va_start_fake(va_list_fake a, int val)
{
a[0] = val;
}
int va_arg_fake(va_list_fake a)
{
return a[0];
}
void exchange(va_list_fake a, va_list_fake b)
{ /* this is not seen by the function user */
int temp = a[0];
a[0] = b[0];
b[0] = temp;
}
void va_end_fake(va_list_fake a)
{
/* empty */
}
/* now it comes the code in main that doesn't know how is
* implemented the type va_list_fake */
int main()
{
va_list_fake a, b; /* they look as normal variables */
va_start_fake(a, 3); /* compare this with va_start */
va_start_fake(b, 2); /* idem. */
/* print the contents of variables a and b with a simil
* of function/macro va_arg() that differs from va_arg only
* in the lack of a second type parameter. */
printf("a = %d, b = %d\n", va_arg_fake(a), va_arg_fake(b));
/* exchange, but pass the variables by value, so it should
* not be updated. 8-. (but there's some hidden trick) */
exchange(a, b); /* this will exchange the values */
/* now a contains the value 2 and b contains the value 3 */
printf("a = %d, b = %d\n", va_arg_fake(a), va_arg_fake(b));
va_end_fake(a);
va_end_fake(b);
}
评论
c
c#
va_test2(ap1);
后跟其他任何内容 (on ) 则为未定义的行为。对 after 的调用是未定义的行为。ap1
va_end(ap1)
va_arg(ap1
va_test2(ap1)
As far as I know, va_arg is defined
你从哪里得到这个定义?你怎么知道这是真的?