在 C 中打印悬空指针

问题描述

#include <stdio.h>

int main()
{
    int *ptr;
    {
        int x = 2;
        ptr = &x;
    }

    printf("%x %d",ptr,*ptr);
    return 0;
}

输出:x的地址,x的值。

这里,ptr 应该是一个悬空指针,对吧?然而,它仍然存储 x 的地址。 它如何仍然指向 x 的值,即使在该块被删除之后?

#include <stdio.h>

int * func (int n)
{
    int temp;
    int *ptr = &temp;
    temp = n * n;
    return ptr;
}

int main()
{
    int n = 4;
    int *p = func(4);
    printf("%x,%d",p,*p);
    return 0;
}

输出:临时地址,16

在这个程序中,数据变量temp和它的指针变量ptr是在单独的函数中创建的。 为什么它会产生正确的结果?

#include <stdio.h>

int * func (int n)
{
    int temp;
    int *ptr = &temp;
    temp = n * n;

    for (int i = 0; i < 10; i++)
        printf("%d ",*ptr);
    return ptr;
}
    
int main()
{
    int n = 4;
    int *p = func(4);
    printf("\n%x,*p);
    for (int i = 0; i < 10; i++)
        printf("%d ",*ptr);
    *p = 12;
    printf("%d\n",*p);
    printf("%d\n",*p);
    return 0;
}

输出:16 16 16 16 16 16 16 16 16 16
临时地址,1
16 16 16 16 16 16 16 16 16 16
12
12

除了for循环之外,上面的程序与第二个程序类似。在 main() 函数,它每次都给出正确的输出。即使我尝试将其更改为 *p = 10,无论我打印多少次,它仍然会给出正确的输出

但在第二个程序中,由于未定义的行为,它只给出一次正确的输出。它在第一个 printf 之后给出垃圾值。

但是在第三个程序中,它如何仍然每次都给出正确的输出

我的问题是:

  1. 指针变量指向一个超出范围的局部变量,但仍然打印正确的输出,并且可以通过更改指针变量的值来访问它。为什么?
  2. 和 increment() 中创建的 temp 一样,ptr 也是在本地创建的。为什么它始终正确打印值而没有任何警告或错误?如果 for 循环不存在,打印一次后也会报错。为什么会这样?
    当我通过 temp 时,我收到警告和分段错误错误。但是为什么 ptr 这个局部变量可以正确打印值?
  3. 在第一个程序中,多次打印 *ptr 后,它给出了正确的输出,并且我能够更改 *ptr = 1;在第一个 printf 之后。为什么即使变量超出范围,我也可以访问 ptr?

谢谢大家的回答。我现在从你所有的答案中理解。非常感谢。

解决方法

您的两个程序行为都未定义。

在第一个代码中,您的程序通过其地址访问 x,它位于声明它的块之外。 x 是一个本地(自动)非静态变量,其生命周期仅限于其作用域1),即声明它的块。任何在其生命周期之外访问它的尝试都将导致未定义的行为2)。第二个代码中的 temp 变量也是如此。

undefined behaviour 包括它可能会错误地执行(崩溃或静默地生成错误的结果),或者它可能偶然地完全按照程序员的意图去做。

此外,打印指针的正确格式说明符是 %p


1)。来自 C11 标准#6.2.1p4 [强调我的]

每个其他标识符的范围由其声明的位置决定(在声明符或类型说明符中)。如果声明标识符的声明符或类型说明符出现在任何块或参数列表之外,则标识符具有文件范围,该范围在翻译单元的末尾终止。 如果声明标识符的声明符或类型说明符出现在块内或函数定义中的参数声明列表内,则标识符具有块作用域,在相关块的末尾终止。 ......

2)。来自 C11 标准#6.2.4p2 [强调我的]

2 对象的生命周期是程序执行期间保证为其保留存储空间的部分。一个对象存在,有一个常量地址,33) 并在其整个生命周期中保留其最后存储的值。34) 如果对象在其生命周期之外被引用,则行为未定义。当指针指向(或刚刚过去)的对象到达其生命周期结束时,指针变得不确定。

,

第一个代码中的'x'和第二个代码中的'temp'是一个局部变量,因此当变量超出定义的块时它会从堆栈中释放。

'ptr' 和 'p' 是指向这些局部变量地址的指针,但是在局部变量从堆栈中释放后,这些指针中存储的值将无效。

局部变量释放后,值是否留在内存中,是开发工具和环境的问题。即释放栈,然后清空占用局部变量的指针的内存,在OS或编译器中被处理,关键是你不能再使用那个有效地址的值。

当我回顾VC++ 2008时,局部变量释放后,指针不再有有效值。它具有随机值。

,

我已经通过 IDA 反汇编了你的第三个程序。
func() 函数作为 main() 函数的一部分进行编译,而不是作为独立函数进行编译。

enter image description here 因此,保留了正确的值。
我猜这是编译时的优化结果。

但是,当我在 func() 中添加一行时,程序的结果是不同的。
enter image description here

在这种情况下,编译器将 'func()' 识别为函数。
出现了预期的结果,程序在 '*p = 12' 处崩溃。

enter image description here