代码之家  ›  专栏  ›  技术社区  ›  zdenko.s

va_arg-Linux和Windows上的不同行为

  •  3
  • zdenko.s  · 技术社区  · 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 。的第二次调用 打印_ int 获取相同的指针值(因为函数内部的本地副本已更改),并打印与第一次调用相同的输出。
    以下是程序调用的Windows输出:

    1
    1
    

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

    1
    2
    

    我的结论是,Linux使用指向不透明结构的指针,该结构具有指向堆栈的指针,当值取消引用指针传递给va_list时,在连续使用中可以看到结构内部的指针变化 va_list .

    当我改变 打印_ int 为了接受指针,它在Linux和Windows上的工作方式相同。

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

    C标准是否规定了如何 va_arg 应该如何表现以及执行如何影响 va_list 论点

    1 回复  |  直到 1 周前
        1
  •  9
  •   dbush    1 周前

    这里的错误是您通过了 va_list 转换为另一个函数并调用 va_arg 在该功能中。一旦您这样做,就不允许使用 va_list 再次在呼叫者中。

    这一点在 C standard 关于变量自变量:

    声明的类型为

    va_list
    

    它是一种适合保存信息的完整对象类型 宏所需 va_start , va_arg , va_end va_copy .如果访问 对于所需的可变参数,被调用的函数应声明 具有类型的对象(在本款中通常称为ap) va_list 。对象 ap 可以作为参数传递给另一个 作用 如果该函数调用 va_arg 带参数的宏 ap , 的价值 ap 在调用函数中是不确定的,应为 传递给 va_end 在进一步引用之前的宏 ap . 253)

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

    所以你的代码触发 undefined behavior 通过访问 args 在其值变得不确定之后。

    请注意,上面的脚注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);
    }