所以给出以下代码:
#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
指向的值?
答案 0 :(得分:3)
p是指向int对象的指针。 &amp; 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
- &gt;编译器获取数组p的“第二个元素”的地址
(int*)(&p)
- &gt; &p
是int**
,但是被转换为int*
,在此特定实例中,恰好与存储在p + 4字节中的值相同
行
中发生了什么std::cout << "*(p + 1): " << *(p + 1) << std::endl;
是*(p+1)
- &gt;编译器访问数组p的“第二个元素”,因为你可能使用x86_64系统,这是小端,存储在那里的十六进制值是0x38D8A884,指针的下半部分存储在p(十进制转换为953723012) ,
答案 1 :(得分:2)
虽然标准不保证((p + 1) == (int*)(&p))
你在这里似乎很幸运。
然而,因为你在取消引用(p+1)
时使用的是64位机器,所以你只得到p的低32位。
0x38D8A884 == 953723012
等式的右边是您收到的输出。左侧是p的低32位,如程序输出所示。
答案 2 :(得分:2)
没有
指针算法虽然未经检查,但非常受标准限制。通常,它只应在数组中使用,并且您可以使用它指向数组元素或超出数组末尾的数组元素。此外,虽然允许指向数组末尾的一个,但是如此获得的指针是 sentinel值,不应该取消引用。
那么,你观察到了什么?简单地说,&p
,p + 1
等等是临时表达式,其结果必须在某处实现。通过优化,所述结果可能会在CPU寄存器中实现,但如果它们没有在功能框架内实现(通常)。
当然,标准没有规定此位置,因此尝试获取它会产生未定义的行为;即使它似乎在你的编译器上运行这个编译选项集对任何其他编译器都没有任何意义,甚至对于任何其他选项集也没有任何意义。< / p>
这是未定义行为的真正含义:它并不意味着程序崩溃,它只是意味着任何事情都可能发生,这包含似乎工作的情况。 / p>
答案 3 :(得分:1)
随机情况是p + 1等于&amp; p。它只发生在你的代码中,其中指针p跟随它指向的对象。这就是p本身的地址是sizeof(int)大于它指向的对象的地址。例如,如果您在i和p之间插入一个更多的定义,则等式p + 1 ==&amp; p将无效。例如
int i = 42;
int j = 62;
int* p = &i;