C知识扫盲------悬挂指针及其处理方法

1.简介:

悬挂指针(Dangling Pointer)指向已经释放或不再有效的内存区域的指针。这种指针虽然仍然持有原来的内存地址,但那块内存已经被释放或重新分配给其他对象。悬挂指针问题是内存管理中的一个常见问题,可能会导致程序崩溃、未定义行为或数据损坏。

2.悬挂指针的产生原因

1.释放内存后指针未更新:

当释放一个内存块后,指针仍然指向那块内存。此时,访问该内存块会导致未定义行为,因为内存可能已经被操作系统或其他程序重新分配。

示例 1: 释放内存后的悬挂指针

#include <stdio.h>
#include <stdlib.h>

void example() {
    int* ptr = (int*)malloc(sizeof(int)); // 分配内存

    if (ptr == NULL)
    {
        printf("内存分配失败!\n");
        return;//退出函数
    }

    *ptr = 10; // 使用分配的内存

    free(ptr); // 释放内存

    // ptr 现在是悬挂指针,因为它指向的内存已经被释放
    printf("ptr 指向的值: %d\n", *ptr); // 未定义行为,可能崩溃或显示垃圾值
}

int main() {
    example();
    return 0;
}

在上面的代码中,ptr 是一个悬挂指针,因为在调用 free(ptr) 后,它仍然指向被释放的内存区域。访问这个内存区域会导致未定义行为。
在这里插入图片描述

2.局部变量的指针:

当指针指向的局部变量离开作用域时,如果该变量所指向的内存被回收,指针就会成为悬挂指针。

示例 2: 局部变量的悬挂指针

#include <stdio.h>

int* create() 
{
    int Value = 20; // 局部变量
    return &Value;  // 返回局部变量的指针
}

void example() 
{
    int* ptr = create();
    // Value 在 create 函数返回后不再有效
    printf("ptr 指向的值: %d\n", *ptr); // 未定义行为,可能崩溃或显示垃圾值
}

int main() 
{
    example();
    return 0;
}

在这里插入图片描述

1.分析:在这个示例中,create 函数返回了指向局部变量 Value 的指针。当 create 函数执行结束后,Value 离开作用域,其内存被回收。指向这个局部变量的指针 ptr 就成了悬挂指针。访问这个指针会导致未定义行为。
2.为什么程序可能会输出值?

情况1:内存未被立即重用:
在某些情况下,局部变量的内存区域在函数返回后不会立即被操作系统回收或重用。 这意味着,尽管该内存区域理论上已不再有效,但它的内容可能在一段时间内保持不变,因此程序仍然可以读取到旧的值。即使这样,读取这些值依然属于未定义行为。

情况2:编译器优化和系统行为(比如博主现在使用的Visual Studio):
编译器和操作系统可能对内存的回收和管理有不同的策略。在某些编译器和系统环境下,局部变量的内存区域可能会在函数返回后保持不变,这也是为什么你可能会看到20 这个值。但是,这种行为是不可靠的,因为它依赖于具体的编译器和系统实现。

3.悬挂指针的影响

1.程序崩溃:

访问已经释放的内存会导致程序崩溃,因为内存可能已经被重新分配或被操作系统回收。

2.数据损坏:

如果被释放的内存区域被重用而你仍然访问它,可能会导致数据被意外修改或读取错误。

3.安全漏洞:

悬挂指针可能被恶意利用,造成安全风险。

4.如何避免悬挂指针

1.将指针设置为 NULL:

在释放内存后,立即将指针设置为 NULL。这样,后续对这个指针的访问会检测到它是 NULL,避免访问非法内存。

free(ptr);
ptr = NULL;

2.避免使用已经释放的指针:

确保在释放内存后不再使用该内存区域。进行适当的检查来确保指针的合法性。

3.避免返回局部变量的指针:

不要返回指向局部变量的指针,确保指针指向的内存在函数返回后仍然有效。

4.使用动态内存分配:

如果需要返回局部变量的指针,使用 malloc 等函数动态分配内存,并在使用完毕后释放它,释放完之后重置为NULL

修改示例二代码如下

#include <stdio.h>
#include <stdlib.h>

int* create()
{
    int* value = (int*)malloc(sizeof(int)); // 动态分配内存
    if (value == NULL)
    {
        printf("内存分配失败!\n");
        return NULL;
    }
    *value = 20; // 设置值
    return value; // 返回动态分配的内存地址,这里的数据存储在堆上
}

void example()
{
    int* ptr = create();
    printf("ptr 指向的值: %d\n", *ptr); // 正确输出值
    free(ptr); // 释放动态分配的内存,防止内存泄漏问题
}

int main() {
    example();
    return 0;
}

在这里插入图片描述
在这个修改后的示例中,create 函数使用 malloc 动态分配内存,这样内存不会在函数返回后被立即回收。使用完动态分配的内存后一定要调用 free 释放它,以免内存泄漏。

5.总结

悬挂指针是指向已经释放或不再有效的内存区域的指针,会导致未定义行为、程序崩溃、数据损坏等问题。 通过在释放内存后将指针设置为 NULL避免使用已经释放的内存,以及避免返回局部变量的指针,可以有效避免悬挂指针的问题。

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值