为什么这个可变参数函数在Windows x64上的第4个参数上失败?

Jar*_*aus 3 c windows

下面是包含可变参数函数和调用可变参数函数的代码.我希望它会适当地输出每个数字序列.它在编译为32位可执行文件时执行,但在编译为64位可执行文件时则不会.

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

#ifdef _WIN32
#define SIZE_T_FMT "%Iu"
#else
#define SIZE_T_FMT "%zu"
#endif


static void dumpargs(size_t count, ...) {

    size_t i;
    va_list args;

    printf("dumpargs: argument count: " SIZE_T_FMT "\n", count);

    va_start(args, count);

    for (i = 0; i < count; i++) {

        size_t val = va_arg(args, size_t);
        printf("Value=" SIZE_T_FMT "\n", val);
    }
    va_end(args);
}

int main(int argc, char** argv) {

    (void)argc;
    (void)argv;

    dumpargs(1, 10);
    dumpargs(2, 10, 20);
    dumpargs(3, 10, 20, 30);
    dumpargs(4, 10, 20, 30, 40);
    dumpargs(5, 10, 20, 30, 40, 50);

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

以下是64位编译时的输出:

dumpargs: argument count: 1
Value=10
dumpargs: argument count: 2
Value=10
Value=20
dumpargs: argument count: 3
Value=10
Value=20
Value=30
dumpargs: argument count: 4
Value=10
Value=20
Value=30
Value=14757395255531667496
dumpargs: argument count: 5
Value=10
Value=20
Value=30
Value=14757395255531667496
Value=14757395255531667506
Run Code Online (Sandbox Code Playgroud)

编辑:

请注意,可变参数函数拉出的原因size_t是因为实际使用它是一个接受指针和长度列表的可变参数函数.自然地,长度参数应该是a size_t.在某些情况下,调用者可能会以一个众所周知的长度传递某些东西:

void myfunc(size_t pairs, ...) {
    va_list args;
    va_start(args, count);

    for (i = 0; i < pairs; i++) {
        const void* ptr = va_arg(args, const void*);
        size_t len = va_arg(args, size_t);
        process(ptr, len);
    }
    va_end(args);
}

void user(void) {
    myfunc(2, ptr1, ptr1_len, ptr2, 4);
}
Run Code Online (Sandbox Code Playgroud)

请注意,4传入myfunc可能会遇到上述问题.是的,真正的调用者应该使用sizeof或者结果strlen或者只是简单地将数字4放入size_t某个地方.但重点是编译器没有捕获这个(可变函数的常见危险).

在这里做的正确的事情是消除可变参数功能,并用一个提供类型安全的更好机制替换它.但是,我想记录这个问题,并收集更详细的信息,以确定此平台上存在此问题的确切原因.

Eva*_*ran 6

所以基本上,如果一个函数是可变参数,它必须符合某个调用约定(最重要的是,调用者必须清理args,而不是callie,因为callie不知道会有多少个args).

它在4号开始发生的原因是因为x86-64上使用调用约定.据我所知,visual c ++和gcc都使用寄存器作为前几个参数,然后使用堆栈.

我猜这是即使对于可变函数也是如此(这确实让我感到奇怪,因为它会使va_*宏更复杂).

在x86上,标准的C调用约定是始终使用堆栈.