在 C 中打印悬挂指针

Printing Dangling Pointers in C

#include <stdio.h>

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

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

Output: address of x, value of 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;
}

Output: address of temp, 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, %d", p, *p);
    for (int i = 0; i < 10; i++)
        printf("%d ", *ptr);
    *p = 12;
    printf("%d\n", *p);
    printf("%d\n", *p);
    return 0;
}

Output: 16 16 16 16 16 16 16 16 16 16
address of temp, 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?

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

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

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

undefined behaviour 包含它可能执行不正确(崩溃或静默生成不正确的结果),或者它可能偶然地完全按照程序员的意图执行。

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


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

Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit. If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block. ......

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

2 The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,33) and retains its last-stored value throughout its lifetime.34) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to (or just past) reaches the end of its lifetime.

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

'ptr'和'p'是指向这些局部变量地址的指针,但是当局部变量出栈后,这些指针中存放的值就无效了。

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

我回顾VC++2008的时候,局部变量释放后,指针已经没有有效值了。它具有随机值。

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

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

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

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