这是合法的:
int array[5];
int *array_begin = &array[0];
int *array_end = &array[5];
第 5.2.1 节下标表达式 E1[E2] 与 *((E1)+(E2)) 相同(根据定义)
因此我们可以说 array_end 也是等价的:
int *array_end = &(*((array) + 5)); // or &(*(array + 5))
第 5.3.1.1 节一元运算符'*':一元 * 运算符执行间接:应用它的表达式应是指向对象类型的指针,或
一个指向函数类型的指针,结果是一个左值,指向表达式所指向的对象或函数。
如果表达式的类型是“指向 T 的指针”,则结果的类型是“T”。 [注意:指向不完整类型的指针(其他
比 cv void) 可以取消引用。这样获得的左值可以以有限的方式使用(初始化引用,例如
例子);此左值不得转换为右值,请参见 4.1。 ——尾注]
以上重要部分:
'结果是一个引用对象或函数的左值'。
一元运算符 '*' 返回一个引用 int 的左值(没有取消引用)。一元运算符 '&' 然后获取左值的地址。
只要没有解除对越界指针的引用,那么该操作就完全被标准覆盖并且所有的行为都被定义了。所以通过我的阅读,以上是完全合法的。
许多 STL 算法依赖于明确定义的行为这一事实暗示标准委员会已经考虑到这一点,我确信有一些东西明确地涵盖了这一点。
下面的评论部分提出了两个论点:
(请阅读:但它很长,我们俩最终都变成了巨魔)
论据 1
这是非法的,因为第 5.7 节第 5 段
当一个整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标之差等于积分表达式。换句话说,如果表达式 P 指向数组对象的第 i 个元素,则表达式 (P)+N(等效于 N+(P))和 (P)-N(其中 N 的值为 n)指向分别指向数组对象的第 i + n 个和第 i - n 个元素,前提是它们存在。此外,如果表达式 P 指向数组对象的最后一个元素,则表达式 (P)+1 指向数组对象的最后一个元素,如果表达式 Q 指向数组对象的最后一个元素,则表达式 (Q)-1 指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者过去
数组对象的最后一个元素,评估不应产生溢出;否则,行为未定义。
虽然该部分是相关的;它不显示未定义的行为。我们所说的数组中的所有元素要么在数组内,要么在数组的末尾(这在上面的段落中有很好的定义)。
论据 2:
下面显示的第二个参数是:* 是取消引用运算符。
尽管这是用于描述“*”运算符的常用术语;标准中故意避免使用该术语,因为术语“取消引用”在语言及其对底层硬件的意义方面没有得到很好的定义。
尽管访问超出数组末尾的内存绝对是未定义的行为。我不相信unary * operator 访问内存(读取/写入内存)在这种情况下(不是以标准定义的方式)。在这种情况下(由标准定义(见 5.3.1.1))unary * operator 返回一个lvalue referring to the object。在我对语言的理解中,这不是对底层记忆的访问。然后,unary & operator 运算符立即使用此表达式的结果,该运算符返回 lvalue referring to the object 引用的对象的地址。
还提供了许多其他对 Wikipedia 和非规范来源的引用。所有这些我都觉得无关紧要。 C++ 是由标准定义的。
结论:
我愿意承认该标准的许多部分我可能没有考虑过,并且可能证明我的上述论点是错误的。 NON 在下面提供。如果你给我看一个显示这是 UB 的标准参考。我会的
- 留下答案。
- 全部大写,这是愚蠢的,我错了所有人都可以阅读。
这不是论据:
并非世界上的所有事物都是由 C++ 标准定义的。敞开心扉。