19

可能重复:
通过下标获取一个过去的数组元素的地址:C++ 标准是否合法?

int array[10];

int* a = array + 10;   // well-defined
int* b = &array[10];   // not sure...

最后一行是否有效?

4

5 回答 5

24

是的,您可以将地址放在数组末尾之外,但不能取消引用它。对于您的 10 项数组,array+10可以使用。有几次争论(委员会等)是否&array[10]真的会导致未定义的行为(如果确实如此,是否真的应该)。它的底线是,至少根据当前标准(C 和 C++),它正式导致未定义的行为,但如果有一个编译器实际上不起作用,那么任何论点中的任何人都无法找到或引用它。

编辑:有一次我的记忆是正确的——这是(部分)向委员会提交的官方缺陷报告,至少一些委员会成员(例如,Tom Plum)认为措辞已经改变,因此不会导致未定义的行为. OTOH,DR 的日期是 2000 年,状态仍然是“正在起草”,因此它是否真的已修复或是否可能会存在疑问(我还没有查看 N3090/3092 来弄清楚)。

然而,在 C99 中,这显然不是未定义的行为。

于 2010-06-29T21:32:32.030 回答
10

正如其他答案所表明的那样,该表达式array[10]等效于*(array + 10),这似乎会导致对刚刚超过数组末尾的元素进行未定义的取消引用。但是,表达式&array[10]等价于&*(array + 10),并且 C99 标准明确(6.5.3.2 地址和间接运算符):

一元 & 运算符返回其操作数的地址。如果操作数的类型为“type”,则结果的类型为“pointer to type”。如果操作数是一元运算符的结果,则该运算*符和 & 运算符都不会被计算,结果就像两者都被省略了,除了对运算符的约束仍然适用并且结果不是左值。类似地,如果操作数是[]运算符的结果,则不会计算&运算符或*隐含的一元运算符,[]结果就像&删除了运算符并将[]运算符更改为 + 运算符一样。

因此,没有什么未定义的&array[10]- 没有发生取消引用操作。

于 2010-06-29T21:45:43.220 回答
2

不,它是未定义的。 array[10]相当于*(array + 10)。换句话说,您取消了对无效指针的引用。

于 2010-06-29T21:32:24.200 回答
1

array[10]等价于*(array + 10)(也等价于10[array]),所以&array[10]就像删除*from一样*(array+10)。任何体面的编译器都应该发出相同的代码(和相同的警告)。

于 2010-06-29T21:32:50.487 回答
-3

这实际上是一个相当简单的回答。

你所做的只是指针数学,没有什么是无效的。如果您尝试以某种方式使用结果指针,那么取决于您的进程是否可以访问该指针指向的内存,如果可以,它的权限是什么?

foo.cc:

#include <iostream>

using namespace std;
int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        cerr << "Pointers values are: " << a << " " << b << " " << c << endl;
        return 0;
}

编译:

g++ -Werror -Wall foo.cc -o foo

应该不会产生警告,因为 int *b = &array[10] 是有效的

实际上,我添加的后续行也是如此,只是为了指出有关指针数学的内容。

foo 不仅可以编译,而且可以顺利运行:

 ./foo
  Pointers are: 0x7fff1d356e68 0x7fff1d356e68 0x7fff1d359d20

基本上数组语法 foo[idx] 是指针数学的捷径和干净的表示。

如果对 c99 有一些混淆,该标准无论如何都不会影响这个数学并且定义明确。

C99 中的相同内容:

#include <stdio.h>

int main() {
        int array[10];
        int *a = array + 10;
        int *b = &array[10];
        int *c = &array[3000];

        fprintf(stderr, "Pointers are: %p, %p, %p\n" , a , b , c );
        return 0;
}

然后:

gcc -std=c99 -Wall -Werror -o foo foo.c

./foo

输出:

Pointers are: 0x7fff2c7d22c8, 0x7fff2c7d22c8, 0x7fff2c7d5180
于 2010-06-29T22:31:22.517 回答