va_arg 64位问题

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

我有这样的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;
}
c 64bit variadic-functions
4个回答
4
投票

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

setter(&v, -32L);

3
投票

在x86_64架构上,long的大小为64位。当你将-32传递给setter()时,它的类型是int,只有32位。如果你想要传递long,请明确地投射它。例如:

setter((long *) &v, (long)-32);

1
投票

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


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 calling conventions中,许多主要参数(System V AMD64 ABI中的6个,Microsoft x64中的4个)通过CPU寄存器传递,并且只有后续参数通过堆栈传递。

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

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

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

在任何情况下,堆栈上的每个参数都有64位保留。

© www.soinside.com 2019 - 2024. All rights reserved.