C/C++ 有意超出范围的索引
C/C++ intentional out of range indexing
假设我有一个这样的数组:
int val[10];
我故意用从负值到高于 9 的所有值来索引它,但没有以任何方式使用结果值。这是出于性能原因(也许在进行数组访问后检查输入索引更有效)。
我的问题是:
- 这样做是否安全,或者我会遇到某种内存保护屏障,冒着损坏内存或某些索引类似风险的风险?
- 如果我像这样访问超出范围的数据,是否效率不高?(假设数组没有内置的范围检查)。
- 会不会算是不好的做法?(假设写了一条注释来表明我们知道使用超出范围的索引)。
这是未定义的行为。根据定义,未定义意味着"任何事情都可能发生"。你的代码可能会崩溃,它可以完美地工作,它可以带来所有人之间的和平与和谐。我不会赌第二个或最后一个。
这是未定义的行为,您实际上可能会与优化器发生冲突。
想象一下这个简单的代码示例:
int select(int i) {
int values[10] = { .... };
int const result = values[i];
if (i < 0 or i > 9) throw std::out_of_range("out!");
return result;
}
现在从优化器的角度来看:
int values[10] = { ... };
:有效索引在[0, 9]
中。values[i]
:i
是一个索引,因此i
在[0, 9]
中。if (i < 0 or i > 9) throw std::out_of_range("out!");
:i
在[0, 9]
,从未服用
因此,优化器重写了函数:
int select(int i) {
int values[10] = { ... };
return values[i];
}
有关基于开发人员没有做任何被禁止的事情这一事实的假设向前和向后传播的更有趣的故事,请参阅每个 C 程序员都应该知道的关于未定义行为的知识:第 2 部分。
编辑:
可能的解决方法:如果您知道将从-M
访问到+N
则可以:
- 使用适当的缓冲区声明数组:
int values[M + 10 + N]
- 偏移任何访问:
values[M + i]
正如 verbose 所说,这会产生未定义的行为。接下来是更精确的一点。
5.2.1/1 说
[...]表达式 E1[E2] 与 *((E1)+(E2))相同(根据定义)
因此,val[i]
等同于*((val)+i))
。由于val
是一个数组,因此数组到指针的转换 (4.2/1) 在执行加法之前发生。因此,val[i]
等效于*(ptr + i)
其中 ptr
是设置为 &val[0]
的int*
。
然后,5.7/2 解释了ptr + i
所指的内容。它还说(强调是我的):
[...]如果指针操作数和结果都指向同一数组对象的元素,或者指向数组对象的最后一个元素,则计算不应产生溢出;否则,行为是未定义的。
在 ptr + i
的情况下,ptr 是指针操作数,结果为 ptr + i
。根据上面的引用,两者都应该指向数组中的一个元素或指向最后一个元素之后的一个元素。也就是说,在OP的情况下,ptr + i
是所有i = 0, ..., 10
的明确定义的表达式。最后,*(ptr + i)
为0 <= i < 10
定义得很好,但对i = 10
没有定义。
编辑:
我对val[10]
(或等效地*(ptr + 10)
)是否产生未定义的行为感到困惑(我正在考虑C++而不是 C)。在某些情况下,这是正确的(例如 int x = val[10];
是未定义的行为),但在其他人中,这并不那么清楚。例如
int* p = &val[10];
正如我们所看到的,这等同于int* p = &*(ptr + 10);
可能是未定义的行为(因为它取消引用指向val
最后一个元素的指针)或与定义良好的int* p = ptr + 10;
相同。
我找到了这两个参考资料,表明这个问题是多么模糊:
我可以获取数组的"一端"元素的地址吗?
通过下标获取一个过去结束的数组元素的地址:C++标准是否合法?
如果你把它放在一个带有一些填充整数的结构中,它应该是安全的(因为指针实际上指向"已知"目的地)。但最好避免它。
struct SafeOutOfBoundsAccess
{
int paddingBefore[6];
int val[10];
int paddingAfter[6];
};
void foo()
{
SafeOutOfBoundsAccess a;
bool maybeTrue1 = a.val[-1] == a.paddingBefore[5];
bool maybeTrue2 = a.val[10] == a.paddingAfter[0];
}
- 并行用于C++17中数组索引范围内的循环
- C++ - 成功打印超出范围的字符串索引
- 张量不会在超出范围索引时引发异常
- 如何在一定范围内将无锁更新索引设置为最大值
- 为什么 std::string 在索引超出范围时不自动追加(或崩溃)?
- 向量下标出的范围错误.即使向量的索引大于访问数据的索引,也会发生误差
- 打包元组及其索引范围
- 如何在 C++ 的字符串中打印从一个索引到另一个索引的字符范围
- 绑定或色谱柱索引超出范围Sqlite C
- 具有多维数组的索引范围之外的索引是不确定的
- 如何在向量的给定索引范围内查找最小元素
- 如何跳过基于"索引"的基于范围的 for 循环中的元素?
- 阵列索引(转换为整数)用范围枚举
- 为什么在 std::vector 中使用索引超出范围的运算符 [] 时没有出现异常?
- 范围循环中的访问索引
- 使用迭代器的矢量擦除特定索引(不基于范围或条件)
- 在向量的排序向量中查找特定值的索引范围
- 请解释 for 循环索引的范围
- 如何在庞大的二进制数据中快速识别 1(索引)的连续范围
- 字符串下标超出范围.我不知道如何使用字符索引数组,所以我使用了(无符号整数),但它不起作用