c - -m32 在参数为 unsigned long long 时给出无法解释的问题

标签 c printf format-specifiers

给定以下代码,参数 '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 调用之前堆栈的样子:

stack before printf call

堆栈中的每个矩形 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/

相关文章:

debugging - 我的 CUDA 内核是真的在设备上运行还是在仿真中被主机错误地执行?

c - 为什么 printf 函数在数字顺序与格式不对应的情况下按顺序打印结果?

c++ - C/C++ 中的 (nil) 指针

ios - 格式指定类型 "unsigned long"但参数的类型为 "int"

c - Printf 打印的字符比我的字符串中包含的字符多

c - printf %i 在这种情况下如何工作?

c - 一个 Void 指针的内存

c++ - 计算范围内的值时出现舍入误差

c++ - 跨文件 #if 和 #endif - 应该合法吗?

更改C中的日期格式