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

时间:2018-07-23 18:40:37

标签: c pointers memory-address

#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

2 个答案:

答案 0 :(得分:4)

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

返回局部变量的地址,然后取消引用该地址将调用undefined behavior

关于可能发生的情况,当您从*p返回后第一次读取foo时,j驻留在堆栈中的地址还没有由于尚未调用其他函数,因此尚未重用。然后,当您调用printf时,该地址会在printf的堆栈框架内重用。然后,下次读取*p时,它将包含放置在此的最后一个函数调用。

但是,重申一下,这是未定义的行为,因此您不能依赖此情况。使用不同的编译器甚至具有不同优化设置的同一编译器进行编译,都可以更改未定义行为的显示方式。

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

11 11 11 

使用-O1:

11 11 0

带有-O2:

11 0 0

带有-O3:

11 0 0

请注意,在一种情况下,我们会以不同的方式获得“预期的”输出,而在其他情况下,则无法获得。

您可以通过将j定义为static或在文件作用域(即函数外部)来避免此问题,在这种情况下,变量的生存期是整个程序的生存期,因此是其地址始终有效。

答案 1 :(得分:1)

尝试一下(不要将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

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

  1. *pj地址
  2. 因此**p将是11,并且foo函数将返回

现在因为int j;是本地的,所以j被调用后foo将会被破坏!

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