为什么printf()在使用指针时绕过分段失败?

时间:2016-03-06 07:56:11

标签: c pointers segmentation-fault printf scanf

我一直在测试C中点的编码操作,我遇到了一个奇怪的现象,我无法解释。

以下代码只是实例化四个双变量并接收用户输入以填充所有变量。

奇怪的是注释掉的行,当没有注释行时代码按预期工作,但是当我注释掉它时,循环中的分段失败。

我一直在ng-bind='document.Title'

编写此测试代码

代码:

c9.io

我不知所措,为什么打印地址会绕过分段失败?

3 个答案:

答案 0 :(得分:5)

在您的代码中,

 double * cur = &a;

然后做

 scanf("%lf", cur++);

并不像您预期​​的那样按顺序指向abcd个变量。这只是未定义的行为。

FWIW,尝试访问超出范围的内存,调用undefined behavior

如果您希望采用相同的方法,可以将a定义为数组(而不是像abc,{那样的单独变量{1}})然后您可以在数组上使用d 遍历。数组成员始终是连续的。

答案 1 :(得分:3)

This answer已经解释了代码中的问题以及解决方法。我不会重复。

Undefined behaviour并不意味着代码正常或不正确,这意味着无法预测行为。这意味着它可以在不同的机器上不同,甚至可以在同一台机器上连续运行。

当您包含printf()行时,代码仍然具有未定义的行为。看起来它工作正常,但事实并非如此。

在您的程序的特定情况下,&行部队中变量bcd的地址运算符(printf())的使用编译器将它们存储在内存中。没有规则告诉它如何将变量放在内存中;大多数编译器按照定义的顺序将变量放在连续的内存位置。这也可能发生在这里。这就是为什么cur++可能会升级到b然后c然后d的地址,并且程序显然正常运行。

当注释掉printf()行时,编译器可能会优化内存使用情况,并选择将一些变量bcd存储在寄存器中。这种方式cur++无法与他们联系。因为a是局部变量,所以在许多架构上它都存储在堆栈中。 cur++遍历堆栈,scanf("%lf", curr++)通过覆盖函数的返回地址和其他值来破坏它。函数完成后,返回无效的内存地址。 KABOOM!分段错误。

答案 2 :(得分:2)

您正在使用scanf()将值写入*cur。由于cur指向堆栈变量,因此最有可能导致segfault的原因是堆栈没有按照您的思路进行布局,并且当您递增指针时,scanf()会覆盖堆栈上的重要内容,例如返回地址,或者可能是堆栈帧指针。这两个都很容易导致段错误。

编辑:在下面的评论中添加我的另一个想法:

另一种可能性是,递增cur实际上导致它不是从&a转到&b,而是另一个方向,从&a&cur。如上所述,无法保证堆栈的顺序。这会导致scanf()损坏cur本身。添加printf()可能会以某种方式更改堆栈分配,因为a,b,c,d之前需要printf(),但直到之后才会使用cur