3

我正在研究一个 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 错误,因为我们试图访问没有被编译器特别标记的内存,仍然显示输出三个编译器。请解释一下。

4

2 回答 2

3

因此我们应该得到一个 SIGSEGV 错误

不,我们不应该。如果行为未定义,则任何事情都可能发生。不保证段错误。

PS您的代码的行为是未定义的,但出于问题中所述的其他原因。实际原因是您i在序列点之间进行了多次修改。请参阅C 常见问题解答

于 2013-08-29T09:36:16.197 回答
0

没有 C 标准在任何地方声明“如果您超出数组的范围,则系统必须在运行时以分段错误(也称为内存访问冲突)终止进程”。

从这个意义上说,C 是不安全的。您可以自由地编写错误的代码,这些代码有时可能会起作用,而有时会失败,因为它是错误的。该标准并非旨在定义当您出错时应该发生什么——它旨在定义什么是正确的,当事情正确时会发生什么,以及(通常暗示)什么是错误的,但(再次)不是什么当事情出错时应该发生。这将对实现施加额外的(并且可以说是非常不必要的)限制。

这就是为什么我们有术语未定义的行为,我相信它是为 C 标准创造的。“未定义”是当你做一些未涵盖的事情时发生的事情——有时它可能仍然是一个合乎逻辑的结果,只是你不能保证它会是一致的。

于 2013-08-29T09:48:05.013 回答