我正在研究C输出问题:
#include<stdio.h>
int main()
{
int a[][2][3]={0,1,2,3,4,5,6,7,8,9,10,11,12};
int i=-1;
int d;
d=a[i++][++i][++i];
printf("%d\n",d);
return 0;
}
指向Ideone的链接:http://ideone.com/1oS9Un
并且期待运行时错误,但令人惊讶的是代码在CodeBlocks,Dev C ++和Ideone上运行良好。
据我所知,编译器在运行时通过以下等式解析每个内存地址:a [i] [j] [k] = ((*(a + i)+ j)+ k),因此每个编译器应首先解析内括号,然后是下一个内括号,依此类推。
因此给定的行
d=a[i++][++i][++i];
应解决为:
d=*(*(*(a+i++)+ ++i)+ ++i)
也是,http://www.difranco.net/compsci/C_Operator_Precedence_Table.htm (请参阅注释2)
应该首先解析最里面的括号,并且它的值应该是a-1,其中i变为0.因此我们应该得到一个SIGSEGV错误,因为我们试图访问未经编译器特别标记的内存,仍然输出是显示在所有三个编译器中。请解释一下。
答案 0 :(得分:3)
因此我们应该得到一个SIGSEGV错误
不,我们不应该。如果行为是undefined,则任何事情都可能发生。不保证会出现段错误。
P.S。代码的行为是未定义的,但原因不在于问题中所述的原因。实际原因是您在序列点之间多次修改i
。请参阅C FAQ。
答案 1 :(得分:0)
没有C标准在任何地方声明“如果你越过数组的边界,那么系统必须在运行时以分段错误(也就是内存访问冲突)终止进程”。
从这个意义上说,不安全。您可以自由编写可能有时起作用的错误代码,而在其他代码中则失败,因为它是错误的。该标准并非旨在定义出错时应该发生的事情 - 它旨在定义什么是正确的,什么会在事情正确时发生,以及(通常暗示)什么是错误但是(再次)不是在出现问题时会发生什么。这将对实现产生额外的(并且可以说是非常不必要的)约束。
这就是为什么我们有术语未定义的行为,我认为这是为C标准创造的。 “未定义”是指当你做一些未被覆盖的事情时会发生的事情 - 它有时可能仍然是一个逻辑结果,只是你无法保证它是一致的。