繁体   English   中英

具有 lld、ld 和 d 类型标识符的 int 变量的 printf

[英]printf of a int variable with lld, ld and d type identifiers

我认为,printf 中的 %d 将从堆栈中读取 sizeof(int),而 %ld 将从堆栈中读取 sizeof(long),等等。 我写了这个代码片段:

##############printf1.c
#include <stdio.h>

int main()
{
    int a = 1, b = 2;
    long la = 1, lb = 2;
    long long lla = 1, llb = 2;

    printf("a=%d, b=%d\n", a, b); 
    printf("a=%ld, b=%ld\n", a, b); 
    printf("a=%lld, b=%lld\n", a, b); 

    printf("la=%d, lb=%d\n", la, lb);
    printf("la=%ld, lb=%ld\n", la, lb);
    printf("la=%lld, lb=%lld\n", la, lb);

    printf("lla=%d, llb=%d\n", lla, llb);
    printf("lla=%ld, llb=%ld\n", lla, llb);
    printf("lla=%lld, llb=%lld\n", lla, llb);

    return 0;
}

而我机器中的 output 是:

$ ./printf1
a=1, b=2
a=1, b=2
a=1, b=2
la=1, lb=2
la=1, lb=2
la=1, lb=2
lla=1, llb=2
lla=1, llb=2
lla=1, llb=2

这是怎么发生的? 如果变量类型和类型标识符不匹配,我认为应该有一些垃圾输出。 我的机器:

$ uname -a
Linux cu01 3.10.0-693.el7.x86_64 #1 SMP Tue Aug 22 21:09:27 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

我的 gcc:

$ gcc --version
gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)

ps:我读过这篇文章: printf of a size_t variable with lld, ld and d type identifiers 。但是作者的问题在我的测试中没有发生。 至于作者的代码:

##############printf2.c
#include <stdio.h>
int main()
{
    size_t temp;
    temp = 100;

    printf("lld=%lld, ld=%ld, u=%u\n", temp, temp, temp);
    printf("ld=%ld, u=%u, lld=%lld\n", temp, temp, temp);

    return 0;
}

我机器上的 output 结果始终是“正确的”:

$ ./printf2
lld=100, ld=100, u=100
ld=100, u=100, lld=100

有人可以对这些做出解释吗? 非常感谢你的帮助!

之所以会发生这种情况,是因为这些值是如何传递给 printf 的,以及它们是如何被读取的。 因此,首先使用d说明符将其传递给 printf 时得到正确结果的原因是它只读取 8 字节 integer 中的前 4 个字节,恰好是数字 1 只占用一个byte 所以无论它读取多少字节,它都会打印 1。这是一种简单的可视化方法。

这是你的长(和长)数字在二进制中的样子

00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001
                                   |-----------------------------------|

上面显示了当您使用d作为说明符传递它时读取的位,如果您要传递ldlld作为说明符,它将读取剩余的 4 个字节,但由于它们都是前导零,这显然不是影响打印效果。 同样的概念也适用于数字 2。尝试用LONG_MAX替换lalla的值,您会注意到它与d说明符一起使用时会打印-1 请注意,此行为是未定义的,因为它取决于系统的字节顺序,并且不会在所有系统上发生,这正是您的系统上最有可能发生的情况。

现在,为什么这不会破坏堆栈? 如果我们对传递给堆栈的东西的大小不诚实的话,显然应该这样做,对吗? 好吧,既然你在 Linux 系统上,你的机器正在使用 System V abi,它在它的调用约定中指定前几个 arguments 将使用寄存器传递,所以在这种情况下实际上没有任何东西被压入堆栈,并且这一切都是从寄存器中读取的。 如果您想了解更多信息,这里有更多信息https://wiki.osdev.org/Calling_Conventions

重要的是要注意这会在不同的机器上导致不同的结果,这显然是为什么它被称为未定义的行为,以及为什么你不应该依赖它或在你的代码中使用它,特别是当它很容易在这样的情况下不使用时这个。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM