因此给定以下代码:
#include <iostream>
#include <vector>
int main(int argc, char* argv[]) {
int i = 42;
int* p = &i;
std::cout << "*p: " << *p << std::endl;
std::cout << "&p: " << &p << std::endl;
std::cout << "p: " << p << std::endl;
std::cout << "p + 1: " << (p + 1) << std::endl;
std::cout << "p + 1: " << ((p + 1) == (int*)(&p)) << std::endl;
std::cout << "*(p + 1): " << *(p + 1) << std::endl;
return 0;
}
它可能会产生以下输出:
*p: 42
&p: 0x7fff38d8a888
p: 0x7fff38d8a884
p + 1: 0x7fff38d8a888
p + 1: 1
*(p + 1): 953723012
(p + 1)
是指向存储p
的内存位置的指针吗?用这种方法可以得到p
所指向的值吗?
p是指向int对象的指针。&p是p的地址。
示例中的堆栈看起来像:
Address Type Name Value
0x7fff38d8a884 int i 42
0x7fff38d8a888 int* p 0x7fff38d8a884
堆栈的设置方式是,p的地址正好在i的地址之后。在这种特殊情况下,当你向p添加1时,它向下移动了4个字节,并在那里找到了值,这恰好是i的地址。
线路发生了什么
std::cout << "p + 1: " << ((p + 1) == (int*)(&p)) << std::endl;
是p+1
-->编译器获取数组p的"第二个元素"的地址(int*)(&p)
->&p
是int**
,但在该特定实例中被强制转换为int*
,恰好与p+4字节中存储的值相同
线路发生了什么
std::cout << "*(p + 1): " << *(p + 1) << std::endl;
是*(p+1)
-->编译器访问数组p的"第二个元素",因为您可能使用的是x86_64系统,它是小端序,存储在那里的十六进制值是0x38D8A884,存储在p中的指针的下半部分(转换为十进制的953723012),。
在您的示例中,(p + 1)
不指向您分配的任何存储,因此取消引用它会产生未定义的行为,应该避免。
EDIT:另外,(p + 1)
本身的第二个输出是不可靠的,因为只有当指针是指向数组的指针时才应该使用指针算术。因此,在我的机器上,表达式的求值结果为false
。
如果你还记得指针和数组可以互换使用,你可能会发现,例如
p[1]
与相同
*(p + 1)
这意味着表达式(p + 1)
是指向p
之后的int
值的指针。由于p
不指向数组,这意味着正n
的(p + n)
是指向您尚未分配的东西的指针(它超出了界限),读取该值会导致未定义的行为。分配给它也是未定义的行为,甚至可以覆盖其他变量的数据。
要获取存储p
的地址,请使用运算符的地址:&p
。它返回指向指针的指针(即类型为int **
)。
虽然标准不能保证((p + 1) == (int*)(&p))
,但您在这里似乎很幸运。
然而,由于你在一台64位机器上解引用(p+1)
,你只能得到p的低32位。
0x38D8A884 == 953723012
等式的右边是您收到的输出。左手边是程序输出所见证的p的低32位。
否。
指针算术虽然未检查,但受到标准的限制。通常,它只能在数组中使用,您可以使用它来指向数组元素或数组末尾之后的元素。此外,尽管允许将一个指针指向数组末尾,但如此获得的指针是一个哨兵值,不应取消引用。
那么,你观察到的是什么?简单地说,&p
、p + 1
等是临时表达式,其结果必须在某个地方具体化。启用优化后,所述结果可能会在CPU寄存器中具体化,但如果没有,则会在函数框架内的堆栈上具体化(通常)。
当然,这个位置不是标准规定的,所以试图获得它会产生未定义的行为;即使它似乎在上运行,您的编译器也可以使用,但这套编译选项对任何其他编译器,甚至是这个具有任何其他选项集的编译器来说都毫无意义。
这就是未定义行为的真正含义:这并不意味着程序崩溃,它只是意味着任何事情都可能发生,这包括似乎有效的情况。
p+1等于&p。它只发生在像你这样的代码中,指针p跟在它所指向的对象后面。也就是说,p的地址本身的sizeof(int)大于它所指向对象的地址。例如,如果你要在i和p之间再插入一个定义,那么方程p+1==&p将无效。例如
int i = 42;
int j = 62;
int* p = &i;
p恰好被分配到堆栈上整数i的地址之后的地址(4字节之后)。some_ptr+1(实际上是some_ptr+1*sizeof(int))不是获取some_ptr地址的一致方法,在这种情况下只是巧合。
所以回答你的问题some_ptr+1!=&some_ptr