在我写的算法中,我可以得到以下内容(当然简化)
int a[3] = {1,2,3};
int b = a[3];
当用于填充b
的索引溢出时,我从不使用b的值。代码仍然不正确吗?我是否必须进行明确的边界检查?
答案 0 :(得分:5)
此代码具有未定义的行为,无论您是否使用b
。为什么?因为a[3]
根据定义等同于*(a+3)
。这里引用了标准,证明*(a+3)
本身是未定义的,无论该值是存储,使用还是单独保留。
当一个表达式有积分时 类型被添加到或从中减去 指针,结果有类型 指针操作数。如果指针 操作数指向一个元素 数组对象,数组很大 够了,结果指向了 元素偏离原始 元素这样的差异 结果和的下标 原始数组元素等于 积分表达。换一种说法, 如果表达式P指向第i个 数组对象的元素, 表达式(P)+ N(等效地, N +(P))和(P)-N(其中N具有 值n)分别指向 第i + n和第i-n个元素 数组对象,只要它们存在。 而且,如果表达P点 到数组的最后一个元素 对象,表达式(P)+1分 一个超过数组的最后一个元素 对象,如果表达式Q指向 一个超过数组的最后一个元素 对象,表达式(Q)-1指向 数组对象的最后一个元素。 如果指针操作数和 结果指向相同的元素 数组对象,或者是最后一个对象 数组对象的元素, 评估不得产生 溢流;否则,行为是 理解过程科幻奈德。
答案 1 :(得分:3)
仍然不正确,仍然是未定义的行为。边界检查。
int b = *(a + 3); // dereferencing beyond the array bound.
答案 2 :(得分:3)
阅读a[3]
已导致未定义的行为。由于未定义的行为永远不会受到本地限制,这可能导致您的硬盘驱动器被格式化,或者您的计算机出现在一个巨大的肉食僵尸身上。
实际上,它通常会起作用。但是很容易构成一个数组末尾标记映射内存区域结束的情况,因此访问一个元素会导致分段错误。对于堆栈上的int
数组肯定不是这种情况,大多数堆实现都不是这样,但是你不应该依赖它。
(是否取&a[3]
的地址也是未定义的行为is heavily disputed。)
答案 3 :(得分:2)
仍然不正确是的,因为您访问越界内存位置以获取值a[3]
并将其存储在变量b
中。
你从不使用b
的事实可能意味着编译器优化了那行代码,所以你可能从来没有看到那条线存在任何不利影响。
但是,编译器不需要这样做,并且代码本身仍然具有未定义的行为。
答案 4 :(得分:1)
是
使用该值,将其复制到b
。
更具体地说,不允许解除引用(a+3)
,因为表达式(a+3)
不是有效指针...而表达式 a[3]
等同于{ {1}}(其中*(a+3)
已经衰减为指针表达式。)
答案 5 :(得分:0)
是的,阅读不会退出的a[3]
是错误的。
使用b
也是错误的,但已经太晚了。