自引用指针算术



因此给定以下代码:

#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)->&pint**,但在该特定实例中被强制转换为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位。

否。

指针算术虽然未检查,但受到标准的限制。通常,它只能在数组中使用,您可以使用它来指向数组元素或数组末尾之后的元素。此外,尽管允许将一个指针指向数组末尾,但如此获得的指针是一个哨兵值,不应取消引用。


那么,你观察到的是什么?简单地说,&pp + 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

最新更新