我可以获取数组的最后一个元素的地址吗? [复制]

Posted

技术标签:

【中文标题】我可以获取数组的最后一个元素的地址吗? [复制]【英文标题】:May I take the address of the one-past-the-end element of an array? [duplicate] 【发布时间】:2011-03-09 20:43:38 【问题描述】:

可能重复: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...

最后一行是否有效?

【问题讨论】:

传奇***.com/questions/988158/…的骗子 @Johannes - 你是对的,投票结束。 【参考方案1】:

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

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

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

【讨论】:

好答案。作为一个有趣的历史点,我实际上看到了一个 C 编译器未能正确取消引用 &array[N]。它在 HP-1000 主机上的 HP RTE 上。它在我的数组上方设置了一个内存“围栏”,因此程序的地址空间中不存在引用地址。这甚至早于 1989 年的 C 标准,所以我认为可以肯定地说这个编译器已经不复存在了。 来自该 DR 的语言不在 N3092 中。虽然我现在找不到它们,但有几个 已关闭 DR 依赖于针对该缺陷的提议解决方案中的“空左值”语言......【参考方案2】:

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

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

因此,&array[10] 没有任何未定义的内容 - 不会发生取消引用操作。

【讨论】:

【参考方案3】:

没有。它是未定义的。 array[10] 等于 *(array + 10)。换句话说,你取消了一个无效的指针。

【讨论】:

但他没有取消引用它... @clmh 关于&*p 在技术上是否构成无操作或取消引用后跟地址存在明确的争论。 &array[10] 是合法的,并且不会取消引用该元素。 @Loadmaster 相当正式 - 是的,它确实取消引用 C++ 和 C89 中的元素,因此导致 C89 中的未定义行为。在 C++ 中,它会导致未定义的行为 if 在该数组之后没有元素(可以肯定的是,您必须将该子数组放入一个多维数组中 - 然后保证有一个元素紧随其后没有填充的数组)。在 C++ 中,如果存在这样的元素,那么 因为 这样的指针本身是 not 无效的 - 指向超出数组末尾的指针是有效的。如上所述,只是取消引用它受到限制。 "dereference" 表示从指针中获取左值。这并不意味着读取一个值。例如,标准在5.3.1/1 处说:“[注意:指向不完整类型(除了 cv void )的指针可以被取消引用。由此获得的左值可以以有限的方式使用(例如,初始化引用);此左值不得转换为右值,参见 4.1。]" 另一个示例参见 3.8/5 "这样的指针可以被取消引用,但生成的左值只能以有限的方式使用,如下所述。"【参考方案4】:

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

【讨论】:

【参考方案5】:

这其实是一个很容易回答的问题。

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

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

【讨论】:

以上是关于我可以获取数组的最后一个元素的地址吗? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

php array_pop()数组函数将数组最后一个单元弹出(出栈)

java中 如何在一个数组中删除里面的元素

JAVA如何把字符串数组是最后一个数据删除?

我可以使用 Jquery 获取元素的 HTML 源代码吗? [复制]

为啥我的 for 循环只读取我创建的数组的最后一个元素? [复制]

我们可以有一个可变长度数组类型的结构元素吗? [复制]