在以下代码中
tt=5;
for(i=0;i<tt;i++)
{
int c,d,l;
scanf("%lld%lld%lld",&c,&d,&l);
printf("%d %d %d %d",c,d,l,tt);
}
在第一次迭代中,&#39; tt&#39;正在自动更改为0。 我知道我已经将c,d,l声明为int并且输入的时间很长,所以它使c,d = 0。但是,我仍然无法理解tt如何变为0。
答案 0 :(得分:3)
小而强制性的公告。正如评论中所说,你面临未定义的行为,所以
您不能指望此代码以某种方式工作,因此不会在实际程序中使用它。
然而,你似乎对此很好,问题是&#34; tt&#34;实际发生了什么。恕我直言这个问题真的很棒,它揭示了深入理解编程的热情,它有助于深入挖掘下层。让我们开始吧。
我无法在VS2015上重现行为,但情况非常清楚。实际数据对齐,可变大小,字节顺序,堆栈增长方向和其他细节可能在您的PC上有所不同,但总体思路应该是相同的。
变量i, tt, c, d, l
是本地的,因此它们存储在stack上。假设sizeof(int)
为4,sizeof(long long)
为8,这很常见。然后在图片上显示一个可能的数据对齐(地址从左到右增长,每个单元代表一个字节):
执行scanf
时,您会传递c
的地址(下一张图中的蓝色箭头)以填充数据。但是数据的大小是8个字节,因此c
和tt
的数据都被覆盖(图中的蓝色单元格)。对于little-endian表示,除非用户输入非常大的数字,否则始终将零写入tt
,而c
实际上获取小数字的有效数据。
但是,c
中的有效数据在填充d
期间将以相同的方式重写,填充d
时l
也会发生同样的情况。因此,在描述的情况下,只有l
将获得非零值。简单测试:输入c, d, l
的大号并检查tt
是否仍为零。
您可以从汇编代码中获取所有答案。启用反汇编列表(具体步骤取决于工具链:gcc具有-S
选项,visual studio具有&#34; goto反汇编&#34;在断点处的上下文菜单中的项目)并分析列表。查看CPU将要执行的确切指令非常有用。一些调试器允许逐个执行指令。因此,您需要了解变量在堆栈上的分配方式以及它们何时被覆盖。分析scanf
对于初学者来说很难,因此您可以从简化版的程序开始:用以下内容替换scanf
(无法测试,但应该有效):
*((long long *)(&c)) = 1; //or any other user specified value
*((long long *)(&d)) = 2;
*((long long *)(&l)) = 3;