C语言 带有 lld、ld 和 d 类型标识符的 size_t 变量的 printf

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2424528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 04:49:20  来源:igfitidea点击:

printf of a size_t variable with lld, ld and d type identifiers

cprintf

提问by Shrey

I wrote this tiny code:

我写了这个小代码:

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

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

    return 0;
}

I am running this on a i386 GNU/Linuxmachine with gcc version 4.1.1 20070105 (Red Hat 4.1.1-52). This is the output that I got:

我在带有gcc 版本 4.1.1 20070105 (Red Hat 4.1.1-52)i386 GNU/Linux机器上运行它。这是我得到的输出:

lld=429496729700, ld=100, u=7993461

I can understand that the first (lld) was printed as garbage because the printftries to print 8 bytes (for signed long longas signified by lld) when only 4 bytes are available from variable temp. But, I fail to understand why the last identifier, uis getting printed as garbage - whereas, in my understanding this is the closest applicable identifier for size_t.

我可以理解第一个 ( lld) 被打印为垃圾,因为当变量中只有 4 个字节可用时,它会printf尝试打印 8 个字节(signed long long表示由lldtemp。但是,我不明白为什么最后一个标识符u会被打印为垃圾——而在我看来,这是最接近适用于size_t.

Here I have assumed that size_tis unsigned int(which is signed 4 bytes for my i386).

在这里我假设size_tunsigned int(我的 i386 有 4 个字节的符号)。

Now, I did a little tweaking with the printfline:

现在,我对这printf条线做了一些调整:

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

and I have a perfectly fine answer (except the lldpart).

我有一个非常好的答案(除了lld部分)。

ld=100, u=100, lld=34331653576851556

Can someone please help me in understanding what exactly am I missing here?

有人可以帮助我了解我在这里到底错过了什么吗?

Thanks a lot for any help!

非常感谢您的帮助!

[side note: I tried switching optimization using gcc -O[0,2]tag on/off without any difference in the observation.]

[旁注:我尝试使用gcc -O[0,2]标签开/关切换优化,观察结果没有任何差异。]

回答by paxdiablo

That's because what you've pushed on the stack is three 32-bit values and your format string tries to use four of them or, more accurately, one 64-bit value and two 32-bit values.

那是因为您压入堆栈的是三个 32 位值,而您的格式字符串尝试使用其中的四个,或者更准确地说,一个 64 位值和两个 32 位值。

In the first case, the lldsucks up two 32-bit values, the ldsucks up the third one and the ugets whatever happens to be on the stack after that, which could really be anything.

在第一种情况下,lld吸收了两个 32 位值,ld吸收了第三个值,然后u获取堆栈中发生的任何内容,这实际上可能是任何内容。

When you change the order of the format specifiers in the string, it works differently because the ldsucks up the first 32-bit value, the usucks up the second and the lldsucks up the third pluswhatever happens to be on the stack after that. That's why you're getting different values, it's a data alignment/availability issue.

当您更改字符串中格式说明符的顺序时,它的工作方式有所不同,因为它ld吸收了第一个 32 位值,u吸收了第二个值,lld吸收了第三个以及之后堆栈中发生的任何内容。这就是为什么你会得到不同的值,这是一个数据对齐/可用性问题。

You can see this in action with the first value. 429496729700 is equal to (4294967296 + 1) * 100, i.e., (232+1)*100. Your code snippet

您可以使用第一个值看到这一点。429496729700 等于(4294967296 + 1) * 100,即 (2 32+1)*100。你的代码片段

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

actually has the following effect:

实际上有以下作用:

What you pass     Stack     What printf() uses
-------------     -----     ------------------
                 +-----+
100              | 100 | \
                 +-----+  = 64-bit value for %lld.
100              | 100 | /
                 +-----+
100              | 100 |    32-bit value for %ld.
                 +-----+
                 | ?   |    32-bit value for %u (could be anything).
                 +-----+

In the second case

在第二种情况下

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

the following occurs:

发生以下情况:

What you pass     Stack     What printf() uses
-------------     -----     ------------------
                 +-----+
100              | 100 |    32-bit value for %ld.
                 +-----+
100              | 100 |    32-bit value for %u.
                 +-----+
100              | 100 | \
                 +-----+  = 64-bit value for %lld (could be anything).
                 | ?   | /
                 +-----+

回答by dirkgently

Your code aptly demonstrates Undefined Behavior. Note that in case of variadic arguments no type checking is done for parameters. This is when an explicit cast becomes necessary. In fact the following should therefore be used:

您的代码恰当地展示了未定义行为。请注意,在可变参数的情况下,不会对参数进行类型检查。这是需要显式转换的时候。实际上,因此应使用以下内容:

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

As an aside remember the specifier for size_tis z. So:

顺便说一下,请记住size_tis的说明符z。所以:

 printf("zd=%zd\n", temp);

回答by Wey Dong

paxdiablo's answer is reasonable in the author's environment. However, on my machine(x86_64 Linux), I got three "100" outputed in each type of the two printf line. So, type mismatch in printf actually invokes undefined behavior. In a specific running environment, maybe you can find an owesome explanation for the weird output results, but it's not universal.

paxdiablo 的回答在作者的环境中是合理的。但是,在我的机器(x86_64 Linux)上,我在两个 printf 行的每种类型中输出了三个“100”。因此, printf 中的类型不匹配实际上会调用未定义的行为。在特定的运行环境中,对于奇怪的输出结果,也许你可以找到一个欠缺的解释,但它并不具有普遍性。

The stack-based model is not always applicable, many modern calling conventions pass the first few ordinary values in registers, not on the stack. See also the bizarre situation asked in this post.

基于堆栈的模型并不总是适用,许多现代调用约定在寄存器中传递前几个普通值,而不是在堆栈上。另请参阅此帖子中提出的奇怪情况。

In conclusion, we should avoid the mismatch type identifiers usage in printf. And when we meet weird output of printf, don't forget to check if we have used right type identifiers for all of the variables.

总之,我们应该避免在 printf 中使用不匹配的类型标识符。当我们遇到奇怪的 printf 输出时,不要忘记检查我们是否为所有变量使用了正确的类型标识符。

回答by Giuseppe Guerrini

You are passing to printf the wrong number of bytes. %lld requires a larger integer, in your case the way %lld taked its argument is completely messed up, since it would expect a 64-bit value.

您向 printf 传递了错误的字节数。%lld 需要一个更大的整数,在您的情况下, %lld 接受其参数的方式完全混乱,因为它需要一个 64 位值。