首页 > 解决方案 > va_list 总是静态的吗?

问题描述

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 无论在哪里声明,它似乎都是静态行为。

你能告诉我这是对的吗,为什么它表现出静态行为?

标签: cargumentsstd

解决方案


va_list 总是静态的吗?

不它不是。它通常是本地的。

您的代码的行为未定义。通话后,va_test2(ap1);唯一可以做的ap1就是打电话va_end(ap1)。我们可以阅读C11 7.16p3

[...] 对象 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 页周围的整个部分)(请参阅此答案)。该定义很可能适用于一些有限的情况。如今,va_arg通常是一些编译器魔法,比如gcc/stdarg.h __builtin_va_arg

va_list如果是数组类型或指向堆栈上数据的指针,则可以解释该行为。x86它是一个结构的数组,如上面的 abi 中所定义。

// 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:——你不能从这样的代码中期待任何东西——通常会期待鼻恶魔产生


推荐阅读