C 为什么va_arg()在x86_64和arm上产生不同的效果?
代码:C 为什么va_arg()在x86_64和arm上产生不同的效果?,c,gcc,x86,arm,x86-64,C,Gcc,X86,Arm,X86 64,代码: #include <stdio.h> #include <stdarg.h> #include <stdlib.h> typedef unsigned int uint32_t; float average(int n_values, ... ) { va_list var_arg; int count; float sum = 0; va_start(var_arg, n_values); for (
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
typedef unsigned int uint32_t;
float average(int n_values, ... )
{
va_list var_arg;
int count;
float sum = 0;
va_start(var_arg, n_values);
for (count = 0; count < n_values; count += 1) {
sum += va_arg(var_arg, signed long long int);
}
va_end(var_arg);
return sum / n_values;
}
int main(int argc, char *argv[])
{
(void)argc;
(void)argv;
printf("hello world!\n");
uint32_t t1 = 1;
uint32_t t2 = 4;
uint32_t t3 = 4;
printf("result:%f\n", average(3, t1, t2, t3));
return 0;
}
但当我交叉编译并在openwrtARM 32位中运行它时,它是错误的
[root@OneCloud_0723:/root/lx]#./helloworld
hello world!
result:13952062464.000000
[root@OneCloud_0723:/root/lx]#uname -a
Linux OneCloud_0723 3.10.33 #1 SMP PREEMPT Thu Nov 2 19:55:17 CST 2017 armv7l GNU/Linux
我知道不要用不正确类型的参数调用va_arg。但为什么我们可以在x86_64中得到正确的结果,而不是在arm中
谢谢。在我的系统x86\u 64上
#include <stdio.h>
int main(void)
{
printf("%zu\n", sizeof(long long int));
return 0;
}
也不要将您的uint32\u t声明为
这是不可移植的,因为int不能保证在整个系统中有4个字节长
所有架构。标准C库实际上在中声明了这种类型
stdint.h,您应该使用thos类型
所以你的程序应该是这样的:
float average(int n_values, ... )
{
va_list var_arg;
int count;
float sum = 0;
va_start(var_arg, n_values);
for (count = 0; count < n_values; count += 1) {
sum += va_arg(var_arg, uint32_t);
}
va_end(var_arg);
return sum / n_values;
}
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
float average(int n_values, ... )
{
va_list var_arg;
int count;
float sum = 0;
va_start(var_arg, n_values);
for (count = 0; count < n_values; count += 1) {
sum += va_arg(var_arg, uint32_t);
}
va_end(var_arg);
return sum / n_values;
}
int main(void)
{
printf("hello world!\n");
uint32_t t1 = 1;
uint32_t t2 = 4;
uint32_t t3 = 4;
printf("result:%f\n", average(3, t1, t2, t3));
return 0;
}
这是可移植的,并且应该在不同的应用程序中产生相同的结果
体系结构。在x86-64 Linux上,每个32位arg在单独的64位寄存器中传递,因为这是x86-64 System V调用约定所要求的
调用方恰好将32位arg扩展到64位寄存器中。这不是必需的;程序中未定义的行为可能会让另一个调用程序在arg传递寄存器中留下高垃圾
被调用者的平均值正在寻找三个64位参数,并在调用者放置它们的相同寄存器中查找,因此它正好起作用
在32位ARM上,long-long不适合单个寄存器,因此寻找long-long参数的被叫方肯定是在不同于调用者放置uint32_t参数的地方寻找
被调用者看到的第一个64位参数可能是long long1,这可能是因为对于arm,long long int的大小与x86_64不同。您的var_arg应该在两个系统va_argvar_arg,uint32_t上,只有这样才能产生正确的结果。未定义的行为可能会导致事情看起来正常,并且可能导致事情崩溃。你很幸运,每个都有一个,所以现在你可以修复它了。@Pablo long-long int在arm上也有8个字节,我测试了它。谢谢。要符合标准,ARM上的大小必须至少为64位。我想这可能需要128位,但这不太可能。@JonathanLeffler我的办公桌上现在没有armv7处理器,我无法测试它。但是基于长的,长的是64位宽。无论如何,OPs代码使用了错误的类型。IDK为什么您认为32位ARM上的long long可能不是8。通过查看ARM编译器输出中返回sizeof:的函数,可以轻松地对其进行测试。我不知道有哪种实现的long-long比表示ISO C所需的值范围更宽,所以它通常是64位类型。这并不能回答问题。问题是到底发生了什么,而不是如何解决。OP已经知道存在类型不匹配。虽然您正确地指出,在uint32中使用您自己的typedef是一个OP似乎没有意识到的错误。@PeterCordes-您可以找到一些罕见的36位计算机,其中long-long使用72位。这是一个很好的答案,我没有处理过调用约定,所以我甚至没有考虑过这些事情。
typedef unsigned int uint32_t;
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
float average(int n_values, ... )
{
va_list var_arg;
int count;
float sum = 0;
va_start(var_arg, n_values);
for (count = 0; count < n_values; count += 1) {
sum += va_arg(var_arg, uint32_t);
}
va_end(var_arg);
return sum / n_values;
}
int main(void)
{
printf("hello world!\n");
uint32_t t1 = 1;
uint32_t t2 = 4;
uint32_t t3 = 4;
printf("result:%f\n", average(3, t1, t2, t3));
return 0;
}