我可以拿一个数组的一个接一个元素的地址吗?

时间:2010-06-29 21:29:48

标签: c++ arrays pointers undefined-behavior

  

可能重复:
  Take the address of a one-past-the-end array element via subscript: legal by the C++ Standard or not?

int array[10];

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

最后一行是否有效?

5 个答案:

答案 0 :(得分:22)

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

编辑:因为我的记忆一半是正确的 - 这是(委员会)官员Defect Report的一部分,至少有一些委员会成员(例如Tom Plum)认为措辞已经改变所以它会导致未定义的行为。 OTOH,DR的历史可以追溯到2000年,状态仍然是“起草”,因此可以肯定它是否真的是固定的,或者可能是(我没有通过N3090 / 3092来解决)。

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

答案 1 :(得分:9)

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

  

一元&运算符返回其操作数的地址。如果操作数具有类型''type'',则结果具有类型''指向类型''的指针。如果操作数是一元*运算符的结果,则该运算符和&运算符都不是。运算符被计算,结果就像两个都被省略一样,除了对运算符的约束仍然适用且结果不是左值。同样,如果操作数是[]运算符的结果,则&运算符和*隐含的一元[]都不会被计算,结果如下:如果删除&运算符并将[]运算符更改为+运算符。

所以,&array[10]没有任何未定义的内容 - 没有解除引用操作。

答案 2 :(得分:2)

array[10]相当于*(array + 10)(也相当于10[array]),因此&array[10]就像从*移除*(array+10)一样。任何体面的编译器都应该发出相同的代码(和相同的警告)。

答案 3 :(得分:1)

没有。这是未定义的。 array[10]相当于*(array + 10)。换句话说,您已取消引用无效指针。

答案 4 :(得分:-2)

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

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

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

应该产生NO警告,因为int * b =&amp; 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