一种独特的数据转换方式

时间:2017-01-06 16:33:03

标签: c++

在以下代码中

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。

1 个答案:

答案 0 :(得分:3)

小而强制性的公告。正如评论中所说,你面临未定义的行为,所以

  • 不要因为分配为零而感到惊讶
  • 不要惊讶于在无效的代码更改后没有分配给零(例如从&#34; int i,tt;&#34;到&#34; int tt,i;&#重新排序初始化34;反之亦然)
  • 在使用不同的标志或不同的编译器版本编译或用于不同的平台或使用不同的输入进行测试后,不会被分配为零感到惊讶
  • 不要对任何事感到惊讶。任何行为都是可能的。

您不能指望此代码以某种方式工作,因此不会在实际程序中使用它

然而,你似乎对此很好,问题是&#34; tt&#34;实际发生了什么。恕我直言这个问题真的很棒,它揭示了深入理解编程的热情,它有助于深入挖掘下层。让我们开始吧。

可能的解释

我无法在VS2015上重现行为,但情况非常清楚。实际数据对齐,可变大小,字节顺序,堆栈增长方向和其他细节可能在您的PC上有所不同,但总体思路应该是相同的。

变量i, tt, c, d, l是本地的,因此它们存储在stack上。假设sizeof(int)为4,sizeof(long long)为8,这很常见。然后在图片上显示一个可能的数据对齐(地址从左到右增长,每个单元代表一个字节):

enter image description here

执行scanf时,您会传递c的地址(下一张图中的蓝色箭头)以填充数据。但是数据的大小是8个字节,因此ctt的数据都被覆盖(图中的蓝色单元格)。对于little-endian表示,除非用户输入非常大的数字,否则始终将零写入tt,而c实际上获取小数字的有效数据。

enter image description here

但是,c中的有效数据在填充d期间将以相同的方式重写,填充dl也会发生同样的情况。因此,在描述的情况下,只有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;