1

我有这样的C代码。在 64 位 linux 系统上,结果是:4294967264 而不是 -32。clang 和 gcc 都生成具有相同错误结果的二进制文件。行中的问题:

*v = va_arg(args, long);
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

void setter(long *v, ...)
{
        va_list args;
        va_start(args, v);
        *v = va_arg(args, long);
        va_end(args);
}

int main()
{
        long v = 0;
        setter((long *) &v, -32);
        printf("%ld\n", v);
        return 0;
}
4

4 回答 4

4

您实际上需要将 a 传递long给您的函数。你正在通过一个int.

setter(&v, -32L);
于 2013-03-18T15:51:20.013 回答
3

在 x86_64 架构上,大小long为 64 位。当您传递-32到 时setter(),它的类型是int并且只有 32 位。如果您想long通过,请显式转换。例如:

setter((long *) &v, (long)-32);
于 2013-03-18T15:52:00.757 回答
1

一点澄清:
如前所述,在 64 位架构上, along是 64 位的。然而,这还不是全部,因为 C/C++ 会进行一些自动转换。在这里,setter()函数接受一个指定的参数和零个或多个未指定的参数。-32 参数是那些未指定的参数之一,因此编译器不知道实际上 along是有意的并保留了一个int(32 位)。此外,额外的零被压入堆栈以保持 64 位对齐。这将产生如上所述的打印结果。因此,您必须明确指定您需要一个longhere(或-32L(long) -32)。但是如果函数确实已经被声明void setter (long *v, long arg),那么编译器就会知道第二个参数是long, 并自动转换int参数。

于 2015-04-30T10:21:41.103 回答
0

更奇怪的是,但仍然不是真正的编译器错误:如果将-32值作为第 7 个或更高的可变参数传递,它可能会扩展为 64 位:

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

/* long long int is a 64bit datatype on both Unix and Windows */
void setter(long long int arg, ...)
{
    va_list args;
    va_start(args, arg);
    while(arg != 0) {
        printf("0x%016llx  %lld\n", arg, arg);
        arg = va_arg(args, long long int);
    }
    va_end(args);
}

int main()
{
    setter(-32, -32, -32, -32, -32, -32, -32, -32, 0);
    return 0;
}

x64 Linux 上的输出是:

0xffffffffffffffe0  -32
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0xffffffffffffffe0  -32
0xffffffffffffffe0  -32

但是,x64 Windows 上的输出类似于:

0xffffffffffffffe0  -32
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00000000ffffffe0  4294967264
0x00040800ffffffe0  1134700294832096
0x178bfbffffffffe0  1696726761565323232
0x00007ff6ffffffe0  140698833649632
0x00007ff6ffffffe0  140698833649632

这里的原因是,在 64 位x86 调用约定中,许多前导参数(System V AMD64 ABI 中为6 ,Microsoft x64 中为4)通过 CPU 寄存器传递,只有后续参数通过堆栈传递。

由于 64 位寄存器提供对其低 32 位的单独访问,编译器确实只设置了它们的低 32 位,因此没有值扩展到 64 位。

对于后续参数,它取决于编译器:

  • gcc 确实将扩展的 64 位值推入堆栈
  • MSVC cl.exe 确实将每个低 32 位写入堆栈,使每个高 32 位未初始化
  • 不确定其他编译器

无论如何,堆栈上的每个参数都保留了 64 位。

于 2019-03-05T18:04:56.727 回答