在C++ Primer的第2章“变量和基本类型”中, 它说:
“指向一个对象的指针和指向另一个对象结尾后面的指针可能会持有相同的地址。”
我不是母语为英语的人,所以对于“指向一个对象结尾后面的指针”这句话感到有些困惑。请问有谁可以告诉我它的意思?
“指向一个对象的指针和指向另一个对象结尾后面的指针可能会持有相同的地址。”
我不是母语为英语的人,所以对于“指向一个对象结尾后面的指针”这句话感到有些困惑。请问有谁可以告诉我它的意思?
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]
的物理位置。
int* pi = foo+6;
或者 int* a = &foo[5], *pi = a+1;
关于 int* pi = &foo[6];
是否合法/定义良好存在争议。 - Lightness Races in Orbit这意味着在之后
int a[] = { 1, 2 };
float b;
可能会出现 (void *) &a[2] == (void *) &b
的比较结果为真。
&a[2]
(或等价地,a+2
)是指向数组 a
结尾后的一个指针,因为该数组仅包含索引为 0 和 1 的元素。
通常情况下,超出范围的数组索引是完全无效的(访问 a[2]
是不允许的,甚至计算 &a[3]
也是不允许的),但是对于计算数组结尾后的地址有一个特殊的例外,因为事实证明这非常有用,例如在迭代数组时需要一个结束值来知道何时停止循环。
&a[2]
是未定义行为。唯一合法的方法是使用 a + 2
来获取指向结尾后面一个元素的指针。 - James Kanzevoid *
,那么你就会得到一个int *
和float *
之间的比较。我本可以使用int b;
作为示例,在这种情况下转换是不必要的,但我想强调它可能发生在任何类型上。 - user743382int a[]
替换为std::vector<int> a
,并且在向量中有2个元素和调试版本的std::vector
,那么他将会得到一个异常,如果他执行&a[2]
。这就是为什么C++委员会没有采用C标准的措辞,并故意保持其未定义行为的原因。 - James Kanze0x7ffffcf6a848
0x7ffffcf6a84c
0x7ffffcf6a84c
&a + 1
的地址与&b
的地址相同。 int a = 2;
int b = 3;
cout<< &a << endl << &a + 1 << endl << &b <<endl;
int ar[3] = {}; int* ptr = ar; ar+1; // NOT "one past"!
- Lightness Races in Orbit
char c[3]; char* p = c + 3;
中的 p 是指向数组 c 结尾后一个位置的指针。 - Alex F