为什么在取消引用指针后会打印不同的值?

Why does after derefrencing the pointer prints different values?

#include <stdio.h>

int main()
{
    int i = 10;
    int *p = &i;
    foo(&p);
    printf("%d ", *p);
    printf("%d ", *p);
}

void foo(int **const p)
{
    int j = 11;
    *p = &j;
    printf("%d ", **p);
}

上面的 code 输出:

11 11 Undefined-value

为什么第三次打印未定义的值? 为什么最后不打印 11

foo中,你将*p(与main中的p相同)设置为局部变量的地址。当 foo returns 时,该变量超出范围,因此该地址不会指向任何有意义的地方。

返回局部变量的地址并随后取消引用该地址调用 undefined behavior

至于可能发生了什么,当你从foo回来后第一次阅读*p时,j所在的地址由于没有调用其他函数,因此堆栈上的函数尚未被重用。当您随后调用 printf 时,该地址确实会在 printf 的堆栈帧中重新使用。然后下次读取 *p 时,它包含最后一个函数调用放在那里的内容。

不过要重申一下,这是未定义的行为,因此您不能依赖于这种情况的发生。使用不同的编译器甚至具有不同优化设置的相同编译器进行编译可以改变未定义行为的表现方式。

作为未定义行为的示例,我在使用带 -O0 的 gcc 进行编译时得到以下输出:

11 11 11 

与-O1:

11 11 0

使用-O2:

11 0 0

与-O3:

11 0 0

请注意,在一种情况下我们会得到 "expected" 输出,而在其他情况下我们不会,而且方式不同。

您可以通过将 j 定义为 static 或在文件范围内(即函数外部)来避免此问题,在这种情况下,变量的生命周期是整个程序的生命周期,因此它的地址总是有效的。

试试这个(不要将 int j 定义为局部变量):

#include <stdio.h>

void foo(int **const p);
int j = 11;          // define int j outside of the function as public

int main()
{
    int i = 10;
    int *p = &i;
    foo(&p);

    printf("%d ", *p);
    printf("%d ", *p);
}

void foo(int **const p)
{
    *p = &j;
    printf("%d ", **p);
}

输出将是:

11 11 11

当您在 void foo(int **const p) 中定义 int j = 11; 时,这些步骤会发生:

  1. *pj地址
  2. 所以 **p 将是 11foo 函数 return

现在因为int j;是本地的,所以j内存会在foo调用后被销毁!

**p指向未定义的内容,在这种状态下你得到Undefined-value输出。