指针超出对象末尾是什么意思?

9
在C++ Primer的第2章“变量和基本类型”中, 它说:
“指向一个对象的指针和指向另一个对象结尾后面的指针可能会持有相同的地址。”
我不是母语为英语的人,所以对于“指向一个对象结尾后面的指针”这句话感到有些困惑。请问有谁可以告诉我它的意思?

3
char c[3]; char* p = c + 3; 中的 p 是指向数组 c 结尾后一个位置的指针。 - Alex F
1
对于任何数组,获取指向其最后一个元素的指针,并加上1。这将是一个超出末尾的指针。 - Alex F
3个回答

14
假设您有一个数组,int foo[5] = {1,2,3,4,5};。它在内存中的布局如下:
-----------
|1|2|3|4|5|
-----------

指向数组中的任何成员都是合法的,但指针也可以合法地指向数组的末尾之后(通常在使用迭代器进行STL算法时,用于表示已经到达数组的结尾) - 尽管不允许对该指针进行解引用操作。例如:

-------------
|1|2|3|4|5|?|
-------------
 ^         ^
 |         |
 p         q

p是指向数组的指针,q是一个指向末尾后一个位置的指针。

现在,假设你还有一个数组const char bar[3] = "Hi";。这两个数组可能已经被分配到相邻的内存中了,就像这样:

<--foo---> <-bar->
-----------------
|1|2|3|4|5|H|i|0|
-----------------
 ^         ^
 |         |
 p         q

那么q既是foo的结尾位置,也指向bar[0]的物理位置。


非常感谢,这正是我想知道的。 - libra
还有一个问题,我如何将指针赋给数组的结尾?就像这样“int *pi = foo[6]”吗? - libra
2
@libra: int* pi = foo+6; 或者 int* a = &foo[5], *pi = a+1; 关于 int* pi = &foo[6]; 是否合法/定义良好存在争议。 - Lightness Races in Orbit

13

这意味着在之后

int a[] = { 1, 2 };
float b;

可能会出现 (void *) &a[2] == (void *) &b 的比较结果为真。

&a[2](或等价地,a+2)是指向数组 a 结尾后的一个指针,因为该数组仅包含索引为 0 和 1 的元素。

通常情况下,超出范围的数组索引是完全无效的(访问 a[2] 是不允许的,甚至计算 &a[3] 也是不允许的),但是对于计算数组结尾后的地址有一个特殊的例外,因为事实证明这非常有用,例如在迭代数组时需要一个结束值来知道何时停止循环。


5
正式地说,&a[2] 是未定义行为。唯一合法的方法是使用 a + 2 来获取指向结尾后面一个元素的指针。 - James Kanze
@libra 如果没有将其转换为void *,那么你就会得到一个int *float *之间的比较。我本可以使用int b;作为示例,在这种情况下转换是不必要的,但我想强调它可能发生在任何类型上。 - user743382
@hvd:没错。因此,总的来说,目前情况就是“正如James所说”。 - Lightness Races in Orbit
@hvd 请提供参考资料。我知道C++委员会曾经明确决定不按照C的方式进行(因为他们不想支持C风格数组的语法,这些语法不能被std :: vector模拟)。他们可能后来改变了主意,但我没有听说过。 - James Kanze
@hvd 这只是“某种程度上”的语言律师行为,因为如果用户稍后将int a[]替换为std::vector<int> a,并且在向量中有2个元素和调试版本的std::vector,那么他将会得到一个异常,如果他执行&a[2]。这就是为什么C++委员会没有采用C标准的措辞,并故意保持其未定义行为的原因。 - James Kanze
显示剩余8条评论

2
"one past" 表示对指针执行 "+1 操作"。下面的代码会产生:
0x7ffffcf6a848
0x7ffffcf6a84c
0x7ffffcf6a84c

正如您所看到的,"one past a" 的地址,即&a + 1的地址与&b的地址相同。
    int a = 2; 
    int b = 3;
    cout<< &a << endl << &a + 1 << endl << &b <<endl;

不,它的意思是“在指针上执行+1操作,使得它不再指向数据边界内,而是超出一个元素”。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit 在问题中的整个句子,即“one past the end of ...”是您在评论中所说的。我只回答了“one past”的含义。它字面上意味着“指针+1操作”。 :-) - Peng Zhang
不,再说一遍,它确实不是。 "One past" 的意思是你超出了底层对象一个单位。任何任意的 +1 操作都是一个 _增量_;你可以称之为 "one up",但其他人不这么做! int ar[3] = {}; int* ptr = ar; ar+1; // NOT "one past"! - Lightness Races in Orbit
哈哈,我刚刚在听《Let It Go》。很奇怪。 - Lightness Races in Orbit
1
@LightnessRacesinOrbit 这完全取决于你如何理解英语中的“past”。一天过去了,是2/12/13。因此,在我的看法中,你只是在谈论英语而不是C++编程。 - Peng Zhang
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接