va_arg - Linux 和 Windows 上的不同行为

问题描述 投票:0回答:1

我做了一些测试代码来演示 Windows 和 Linux 上的不同输出

#include <stdarg.h>
#include <stdio.h>

void print_vint(va_list args)
{
    int i = va_arg(args, int);
    printf("%d\n", i);
}

void variadic_f(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    print_vint(args);
    print_vint(args);
    va_end(args);
}

int main(int argc, char* argv[])
{
    variadic_f("dummy ", 1, 2);
    return 0;
}

print_vint
函数具有通过值传递的
va_list
类型的参数。在 Windows 机器上,它是指向最后一个非变量参数之后的堆栈的指针。参数按值传递给函数,函数在调用
va_arg
时更改它。第二次调用
print_vint
获得相同的指针值(因为函数内的本地副本已更改)并打印与第一次调用相同的输出。
以下是程序调用的 Windows 输出:

1
1

我在Linux上编译相同的源代码并运行。输出不同:

1
2

我的结论是Linux使用指向不透明结构的指针,该结构具有指向堆栈的指针,当通过值传递va_list时,解引用指针和更改结构内部的指针将在连续使用

va_list
中可见。

当我将

print_vint
更改为接受指针时,它在 Linux 和 Windows 上的工作方式相同。

void print_vint(va_list *args)
{
    int i = va_arg(*args, int);
    printf("%d\n", i);
}

C 标准是否指定

va_arg
应如何表现以及执行如何影响
va_list
参数?

c variadic-functions
1个回答
1
投票

这里的错误是您将

va_list
传递给另一个函数并在该函数中调用
va_arg
。一旦执行此操作,就不允许在调用者中再次使用
va_list

C 标准有关变量参数的第 7.16p3 节对此进行了详细说明:

声明的类型是

va_list

这是一个适合保存信息的完整对象类型 宏

va_start
va_arg
va_end
va_copy
需要。如果访问 对于需要变化的参数,被调用函数应声明 具有类型的对象(在本子条款中通常称为 ap)
va_list
。对象
ap
可以作为参数传递给另一个对象 功能; 如果该函数使用参数
va_arg
调用
ap
宏, 调用函数中
ap
的值是不确定的,应为 在进一步引用
va_end
.
253)
之前传递给 ap

  1. 允许创建一个指向
    va_list
    的指针并将该指针传递给另一个函数,其中 如果其他函数返回后,原始函数可能会进一步使用原始列表。

请注意,上面的脚注 253 指出您可以将指针传递给

va_list
来执行您想要的操作:

void print_vint(va_list *args)
{
    int i = va_arg(*args, int);
    printf("%d\n", i);
}

void variadic_f(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    print_vint(&args);
    print_vint(&args);
    va_end(args);
}
© www.soinside.com 2019 - 2024. All rights reserved.