给定以下代码,参数 'a' 的 printf 语句错误:
#include <stdio.h>
void call(unsigned long long a, int b)
{
printf("%lu,%d\n",a,b);
printf("%llu,%d\n",a,b);
}
void main()
{
call(0,1);
}
当你正常编译时,你会得到:
$ gcc m32.c
m32.c: In function ‘call’:
m32.c:4:12: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long unsigned int’ [-Wformat=]
printf("%lu,%d\n",a,b);
^
$ ./a.out
0,1
0,1
但是当你用 -m32 编译它时,你会得到以下输出:
$ gcc -m32 m32.c
m32.c: In function ‘call’:
m32.c:4:12: warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 2 has type ‘long long unsigned int’ [-Wformat=]
printf("%lu,%d\n",a,b);
^
$ ./a.out
0,0
0,1
显然,第一个 printf 是错误的,但正如您所见,在 printf 之后,打印中的第二个参数是错误的,而我不希望这种情况发生。我无法解释。这怎么可能?
最佳答案
好答案@Attie!另外,因为你触发了我低水平的热情:)我会尝试从另一个角度来回答这个问题。
您可能知道,在x86 架构 中,函数参数是通过stack 发送的。在 x64 架构中,函数参数通过寄存器发送(依次为 RDI、RSI、RDX、RCX、R8、R9)。
因此,与您的问题相关的重要一点是,当您编译 32 位时,printf
调用的参数是通过堆栈发送的。这是两次 printf
调用之前堆栈的样子:
堆栈中的每个矩形 block 都由一个 32 位数字表示(因为您处于 x86 架构中)。您想发送一个 64 位数字作为 printf
的第一个参数!为此,编译器将 unsigned long long 数字拆分为两个 32 位部分,并将它们分别压入堆栈。这就是为什么您在堆栈中得到两个零以及整数中的一个值。
现在我们来分析一下printf
的第一次调用。
0,0
因为它有 "%lu,%d\n" 格式说明符,所以它必须从堆栈中取出一个 unsigned long 和一个 int。 %lu 在 x86 架构中是 32 位的,所以 printf
只从堆栈中取出一个 block 。在此之后,整数又被取了一个 block (因为我们只用 %lu“消耗”了两个零之一,我们将得到另一个零作为 %d)。
第二次调用 printf
输出正常值。
0,1
此调用是使用"%llu,%d\n" 格式说明符完成的。 %llu 在 x86 体系结构中是 64 位的,因此 printf
从堆栈中取出 两个 block ,从而打印出一个零。在此之后,它又从堆栈中取出一个 block 用于整数(这是具有一个值的 block )。
您必须非常小心发送给 printf
函数的字符串格式说明符! format string attack是一种众所周知的攻击类型,它基于您在问题中展示的问题。
关于c - -m32 在参数为 unsigned long long 时给出无法解释的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43325950/