如果索引超过数组末尾有效,为什么我需要指定数组长度

Why do I need to specify array length if indexing past the end of the array works?

本文关键字:数组 为什么 索引 有效 如果      更新时间:2023-10-16

定义一个有 2 个元素的数组

char a[2];

然后分配值并打印第 4 个元素。

a[0]='a';
a[1]='b';
a[2]='c';
a[3]='d';
cout<<a[3]<<endl;

为什么我可以得到答案"d"而不是运行时错误?声明数组a时,值 2 在 [] 中代表什么?如果 2 在这里没有意义,为什么我不能把它写成:

char a[];
C 和

C++ 中的索引运算符a[b]可以被认为是定义为 *(&a + sizeof(TA) * b) ,(有趣的是,a[b]b[a] 也是等价的,但这是另一种解释)。

让我们演练一下代码:

char a[2]; // statically-allocates 2 bytes on the stack, e.g. at `0xFFFF`, and `0xFFFE` (as the stack grows downwards)
a[0] = 'a'; // sets `0xFFFF`
a[1] = 'b'; // sets `0xFFFE`
a[2] = 'c'; // sets `0xFFFD` <-- danger!

这就是危险所在:C/C++ 不强制要求数组代码具有边界检查,因此您的代码可以被视为等效于以下内容:

char a0; // 0xFFFF
char a1; // 0xFFFE
*0xFFFF = 'a';
*0xFFFE = 'b';
*0xFFFD = 'c'; <-- danger! writing to unallocated memory
*0xFFFC = 'd'; <-- uncharted territory! here be dragons!

您的代码之所以"有效"0xFFFD是因为内存存在有两个原因:1:堆栈向下增长,操作系统会自动保留,因此您不会出现段错误(Windows 上的"访问冲突"),以及 2:您没有接近堆栈溢出错误情况。

但是,如果您要向函数添加更多局部变量,那么您会看到a[2]将覆盖这些值,您还冒着覆盖当前堆栈帧的返回地址的风险,从而损坏堆栈并使程序处于不确定状态,应立即终止)。

考虑:

char[2] a;
int     b = 0;
int     c = 0;
a[2] = 'a';
assert( b == 0 ); // this assertion will fail (at least on systems that don't word-align locals)