【C/C++】空指针、野指针和悬空指针(nullptr NULL 0)

空指针

C/C++中,用 0 或 NULL 都可以表示空指针。

使用空指针的后果

  • 如果对空指针解引用,程序会崩溃。
  • 如果对空指针使用delete运算符,系统将忽略该操作,不会出现异常。

为什么空指针访问会出现异常?

NULL 指针分配的分区:其范围是从 0x00000000 到 0x0000FFFF。这段空间是空闲的,对于空闲的空间而言,没有相应的物理存储器与之相对应,所以对这段空间来说,任何读写操作都是会引起异常的。空指针是程序无论在何时都没有物理存储器与之对应的地址。为了保障“无论何时”这个条件,需要人为划分一个空指针的区域,故有上面NULL指针分区。

nullptr

nullptr 在 C++11 引入,使用 nullptr 代替 NULL 可以增加代码的清晰性和可读性。

  • nullptr 明确地表示了一个空指针(类型安全),而 NULL 则可能表示一个空指针或一个整数类型的零。在阅读和理解代码时,nullptr 的语义更加明确,可以减少误解和错误。
void func(int);         // (1) 接受一个整数的函数  
void func(void* ptr);   // (2) 接受一个指针的函数

// 尝试使用NULL来调用func函数
func(NULL);
// 编译器将不得不决定是调用(1)版本的func(将NULL解释为整数0)还是调用(2)版本的func(将NULL解释为空指针)。在不同的编译器或不同的编译设置下,这种行为可能是不确定的,有时甚至会导致编译错误。

// 如果我们使用nullptr来调用func函数
func(nullptr);
// 编译器将明确调用(2)版本的func,因为nullptr只能被解释为空指针,与整数类型的参数不兼容。

野指针

野指针是指一个没有被初始化的指针(或者赋值了一个无效的地址)。

野指针的产生通常是因为指针变量声明后未被初始化就直接使用,导致它指向一个未知的内存地址。

使用野指针会导致程序尝试访问一个不合法或不可用的内存区域,可能引发崩溃、数据损坏或者不可预知的行为。

int *ptr; // ptr 未初始化,指向一个随机的内存地址
*ptr = 10; // 使用野指针,可能导致程序崩溃

悬空指针

悬空指针是指一个原本指向有效内存的指针,在该内存被释放或删除后,该指针仍然指向这块内存区域。

悬空指针的产生通常是在使用freedelete等操作释放了指针所指向的内存,但未将指针置空,导致它仍然指向已经释放的内存。

使用悬空指针会导致程序访问已经被释放的内存区域,这块内存可能已经被重新分配给其他对象,导致数据错误、崩溃或其他不可预料的行为。

// 1. 释放后没有置空指针或赋予新的有效地址
int *ptr = (int*)malloc(sizeof(int)); // 分配内存
*ptr = 10;
free(ptr); // 释放内存
*ptr = 20; // 使用悬空指针,可能导致不可预测的行为

// 2. 返回局部变量的指针
int* createInt() {
    int x = 10;
    return &x; // x 是局部变量,函数结束后 x 被销毁,返回的指针成为悬空指针
    // x的内存在函数结束时就不再有效。这样,返回的指针实际上指向了一块已经被释放的内存区域。
}
// 如何避免: 在堆上分配内存
int* createInt() {
    int* p = new int(10); // 在堆上分配内存
    return p;
}

// 3. 函数参数指针被释放
void foo(int* ptr) {
	// 操作 ptr
 	delete ptr;
}
int main() {
 	int* ptr = new int;
 	foo(ptr);
 	// 在 foo 函数中 ptr 被释放,但在 main 函数中仍然可用,成为悬空指针
    // 后续可能会使用 ptr 导致未定义行为
}
// 避免在 foo 中删除传入的指针
// 或者
void foo(int*& ptr) {
    // 操作 ptr
    delete ptr;
    ptr = nullptr; // 将 ptr 置为 nullptr,避免悬空指针
}
// 注意,int*& ptr 表示一个指向 int* 类型的引用。也就是说,ptr 是一个指向 int 类型指针的引用。通过这种方式,函数可以修改调用者传入的指针本身(否则是指针的拷贝),而不仅仅是修改指针指向的值。
// 在 C++ 中,参数传递默认是按值传递的,即使你在函数中修改了指针的值,这些修改也只作用于函数的局部副本,原始指针在函数外部并不会改变。

如何避免

释放内存后将指针置为 nullptr

避免返回局部变量的指针

使用智能指针

注意函数参数的生命周期, 避免在函数内释放调用方传递的指针,或者通过引用传递指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shayudiandian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值