我一直在测试C中点的编码操作,我遇到了一个奇怪的现象,我无法解释。
以下代码只是实例化四个双变量并接收用户输入以填充所有变量。
奇怪的是注释掉的行,当没有注释行时代码按预期工作,但是当我注释掉它时,循环中的分段失败。
我一直在ng-bind='document.Title'
代码:
c9.io
我不知所措,为什么打印地址会绕过分段失败?
答案 0 :(得分:5)
在您的代码中,
double * cur = &a;
然后做
scanf("%lf", cur++);
并不像您预期的那样按顺序指向a
,b
,c
,d
个变量。这只是未定义的行为。
FWIW,尝试访问超出范围的内存,调用undefined behavior。
如果您希望采用相同的方法,可以将a
定义为数组(而不是像a
,b
,c
,{那样的单独变量{1}})然后您可以在数组上使用d
遍历。数组成员始终是连续的。
答案 1 :(得分:3)
This answer已经解释了代码中的问题以及解决方法。我不会重复。
Undefined behaviour并不意味着代码正常或不正确,这意味着无法预测行为。这意味着它可以在不同的机器上不同,甚至可以在同一台机器上连续运行。
当您包含printf()
行时,代码仍然具有未定义的行为。看起来它工作正常,但事实并非如此。
在您的程序的特定情况下,&
行部队中变量b
,c
和d
的地址运算符(printf()
)的使用编译器将它们存储在内存中。没有规则告诉它如何将变量放在内存中;大多数编译器按照定义的顺序将变量放在连续的内存位置。这也可能发生在这里。这就是为什么cur++
可能会升级到b
然后c
然后d
的地址,并且程序显然正常运行。
当注释掉printf()
行时,编译器可能会优化内存使用情况,并选择将一些变量b
,c
和d
存储在寄存器中。这种方式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
。