我写了这个小代码:
#include <stdio.h>
int main() {
size_t temp;
temp = 100;
printf("lld=%lld, ld=%ld, u=%u\n", temp, temp, temp);
return 0;
}
我使用 gcc版本4.1.1 20070105(Red Hat 4.1.1-52)在 i386 GNU / Linux 计算机上运行此功能。这是我得到的输出:
lld=429496729700, ld=100, u=7993461
我可以理解第一个(lld
)被打印为垃圾,因为printf
尝试打印8个字节(仅signed long long
表示lld
)变量temp
提供4个字节。
但是,我无法理解为什么最后一个标识符u
被打印为垃圾 - 而在我的理解中,这是size_t
最接近的适用标识符。
这里我假设size_t
是unsigned int
(我的i386签名为4个字节)。
现在,我使用printf
行进行了一些调整:
...
printf("ld=%ld, u=%u, lld=%lld\n", temp, temp, temp);
...
我有一个非常好的答案(lld
部分除外)。
ld=100, u=100, lld=34331653576851556
有人可以帮助我理解我在这里错过了什么吗?
非常感谢您的帮助!
[旁注:我尝试使用gcc -O[0,2]
标签打开/关闭切换优化,但观察结果没有任何差异。]
答案 0 :(得分:23)
那是因为你在堆栈上推送的是三个32位值,你的格式字符串试图使用其中的四个,或者更准确地说,是一个64位值和两个32位值。
在第一种情况下,lld
吸收了两个32位值,ld
吸收了第三个值,u
得到了堆栈之后发生的任何事情。 ,这可能真的是什么。
当您更改字符串中格式说明符的顺序时,它的工作方式不同,因为ld
吸收了第一个32位值,u
吸收了第二个和{{1吸收第三个 plus 之后发生在堆栈上的任何事情。这就是为什么你得到不同的价值,这是数据对齐/可用性问题。
您可以使用第一个值查看此操作。 429496729700等于lld
,即(2 32 +1)* 100。您的代码段
(4294967296 + 1) * 100
实际上有以下效果:
printf("lld=%lld, ld=%ld, u=%u\n", temp, temp, temp);
在第二种情况下
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).
+-----+
发生以下情况:
printf("ld=%ld, u=%u, lld=%lld\n", temp, temp, temp);
答案 1 :(得分:9)
您的代码恰当地展示了未定义的行为。请注意,在可变参数的情况下,不会对参数进行类型检查。这是一个必要的明确演员。实际上应该使用以下内容:
printf("lld=%lld, ld=%ld, u=%u\n",
(unsigned long long)temp,
(unsigned long)temp,
(unsigned int)temp);
另外请记住size_t
的说明符是z
。所以:
printf("zd=%zd\n", temp);
答案 2 :(得分:0)
您向printf传递错误的字节数。 %lld需要一个更大的整数,在你的情况下,%lld删除它的参数的方式完全搞砸了,因为它会期望一个64位的值。
答案 3 :(得分:0)
基于堆栈的模型并不总是适用,许多现代的调用约定在寄存器中而不是在堆栈中传递前几个普通值。另请参阅in this post提出的奇怪情况。
最后,我们应该避免在printf中使用不匹配类型标识符。当我们遇到printf的奇怪输出时,请不要忘记检查我们是否对所有变量都使用了正确的类型标识符。