gpt4 book ai didi

c - 为什么我的可变参数函数同时适用于 int 和 long long?

转载 作者:行者123 更新时间:2023-12-02 08:17:50 26 4
gpt4 key购买 nike

根据 this answer传递给可变参数函数的数字常量如果适合一个,则始终被视为 int。这让我想知道为什么以下代码适用于 intlong long。考虑以下函数调用:

testfunc(4, 1000, 1001, 1002, 1003);

testfunc 看起来像这样:

void testfunc(int n, ...)
{
int k;
va_list marker;

va_start(marker, n);
for(k = 0; k < n; k++) {
int x = va_arg(marker, int);
printf("%d\n", x);
}
va_end(marker);
}

这很好用。它打印出 1000、1001、1002、1003。但令我惊讶的是,以下代码也能正常工作:

void testfunc(int n, ...)
{
int k;
va_list marker;

va_start(marker, n);
for(k = 0; k < n; k++) {
long long x = va_arg(marker, long long);
printf("%lld\n", x);
}
va_end(marker);
}

这是为什么呢?为什么它也适用于 long long?我认为数字整数常量如果适合一个,就会作为 int 传递? (参见上面的链接)那么它怎么能与 long long 一起工作呢?

哎呀,它甚至在 intlong long 之间交替工作。这让我很困惑:

void testfunc(int n, ...)
{
int k;
va_list marker;

va_start(marker, n);
for(k = 0; k < n; k++) {

if(k & 1) {
long long x = va_arg(marker, long long);
printf("B: %lld\n", x);
} else {
int x = va_arg(marker, int);
printf("A: %d\n", x);
}
}
va_end(marker);
}

这怎么可能?我以为我所有的参数都是作为 int 传递的...为什么我可以在 intlong long 之间任意来回切换而没有问题全部?我现在真的很困惑......

感谢您对此有所了解!

最佳答案

这与 C 无关。只是您使用的系统 (x86-64) 在 64 位寄存器中传递了前几个参数,即使是可变参数也是如此。

本质上,在您使用的架构上,编译器生成的代码对每个参数(包括可变参数)使用完整的 64 位寄存器。这是架构约定的ABI,与C本身无关;所有程序,无论是如何生成的,都应该在它应该运行的架构上遵循 ABI。

如果您使用 Windows,x86-64 使用 rcxrdxr8r9 作为四个第一个(整数或指针)参数,按此顺序,其余的堆栈。在 Linux、BSD、Mac OS X 和 Solaris 中,x86-64 使用 rdirsirdxrcxr8r9 用于前六个(整数或指针)参数,按此顺序,其余的入栈。

您可以使用一个简单的示例程序来验证这一点:

extern void func(int n, ...);

void test_int(void)
{
func(0, 1, 2);
}

void test_long_long(void)
{
func(0, 1LL, 2LL);
}

如果您在 Linux、BSD、Solaris 或 Mac OS 中将以上编译为 x86-64 程序集(例如 gcc -Wall -O2 -march=x86-64 -mtune=generic -S) (X 或更高版本),你得到大约(AT&T 语法,source,target 操作数顺序)

test_int:
movl $2, %edx
movl $1, %esi
xorl %edi, %edi
xorl %eax, %eax
jmp func

test_long_long:
movl $2, %edx
movl $1, %esi
xorl %edi, %edi
xorl %eax, %eax
jmp func

即函数是相同的,不要将参数压入堆栈。请注意,jmp func 等同于 call func; ret,更简单。

但是,如果您针对 x86 (-m32 -march=i686 -mtune=generic) 进行编译,您将获得大约

test_int:
subl $16, %esp
pushl $2
pushl $1
pushl $0
call func
addl $28, %esp
ret

test_long_long:
subl $24, %esp
pushl $0
pushl $2
pushl $0
pushl $1
pushl $0
call func
addl $44, %esp
ret

这表明 Linux/BSDs/etc 中的 x86 调用约定。涉及在堆栈上传递可变参数,int 变体将 32 位常量压入堆栈(pushl $x 压入 32 位常量 x 到堆栈),long long 变体将 64 位常量压入堆栈。

因此,由于您使用的操作系统和体系结构的底层 ABI,您的可变函数会显示您观察到的“异常”。要仅从 C 标准中查看您期望的行为,您需要解决底层 ABI 怪癖——例如,通过使用至少六个参数启动可变参数函数,以占用 x86-64 体系结构上的寄存器,以便其余的,您真正的可变参数,在堆栈上传递。

关于c - 为什么我的可变参数函数同时适用于 int 和 long long?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40330749/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com