C++难点(一)——指针

目录

1. 指针(Pointers):

2.空指针(Null Pointer):

1).未初始化指针:

2).指针被显式赋值为nullptr或NULL:

3).指针被释放后没有设为nullptr:

4)函数返回空指针:

5).指针被置为无效值:

3.智能指针

1)std::unique_ptr:

2)std::shared_ptr:

3)std::weak_ptr:

4.为什么使用智能指针?

5.野指针


指针和内存管理是C++编程中非常重要的概念,也是初学者容易遇到困惑和错误的地方。让我们详细讲解指针和内存管理的相关知识:

1. 指针(Pointers)

指针是一个变量,它存储的是另一个变量的内存地址。指针可以让我们直接访问和操作内存中的数据,是C++中强大但也容易出错的特性。指针使用*符号来声明和访问。

int main() {
    int x = 10;
    int* ptr; // 声明一个指向整数的指针

    ptr = &x; // 将ptr指向变量x的内存地址

    std::cout << "Value of x: " << x << std::endl; // 输出:Value of x: 10
    std::cout << "Address of x: " << &x << std::endl; // 输出:Address of x: 0x7ffefefffffc (可能会有不同的地址)

    std::cout << "Value of ptr: " << ptr << std::endl; // 输出:Value of ptr: 0x7ffefefffffc (ptr存储了x的地址)
    std::cout << "Value at ptr: " << *ptr << std::endl; // 输出:Value at ptr: 10 (*ptr表示指针ptr指向的值)

    return 0;
}

需要注意的是,指针在声明时应该进行初始化,否则它将指向一个未知的内存地址(野指针),访问该指针可能导致程序崩溃。另外,指针也可以为空指针,即指向空地址(nullptr),表示不指向任何有效的内存。

2.空指针(Null Pointer)

空指针是指指针不指向任何有效的内存地址,它可以用特殊的值nullptr表示(C++11引入)或使用C++老标准中的NULL(通常被定义为0)来表示。

在定义指针时,如果没有为其赋初值,它可能是一个空指针,或者在释放了动态分配内存后没有及时将指针设为nullptrNULL,也可能导致空指针。

int* ptr = nullptr; // 或 int* ptr = NULL; 或 int* ptr = 0;

对于空指针,应该避免在其上解引用,否则可能导致程序崩溃。

以下是可能会发生空指针的情况:

1).未初始化指针

在定义指针时没有为其赋初值,它的值是不确定的,可能是一个随机值,这样的指针可能是空指针。

int* ptr; // 未初始化指针,可能是空指针

2).指针被显式赋值为nullptrNULL

如果将指针显式赋值为nullptrNULL,它将成为一个空指针。

int* ptr = nullptr; // ptr是空指针

3).指针被释放后没有设为nullptr

在使用delete释放动态分配的内存后,没有将指针设为nullptr,导致指针仍然指向原来的内存地址,成为了空指针。

int* ptr = new int; // 动态分配内存
// 使用内存
delete ptr; // 释放内存,但ptr仍然指向原来的内存地址
// 这时ptr成为了空指针,但它指向的内存是已释放的,解引用将导致未定义行为

4)函数返回空指针

在函数中返回一个空指针,如果在调用函数时没有进行有效性检查,可能导致后续对空指针的解引用操作。

int* createIntPtr() {
    return nullptr; // 返回空指针
}

int* ptr = createIntPtr(); // ptr是空指针

5).指针被置为无效值

将指针设置为一个无效的地址,例如强制类型转换为一个不相关的类型或超出合法范围的地址,这样的指针可能成为空指针。

int x = 10;
int* ptr = (int*)&x; // 强制类型转换为int指针,但不相关的类型可能会导致空指针

为了避免空指针带来的问题,应该在使用指针前进行有效性检查,确保指针不是空指针。同时,建议始终将指针设为nullptr,在释放动态分配的内存后,以防止成为野指针。使用智能指针可以更好地管理指针,避免空指针和野指针的问题。

3.智能指针

智能指针是C++中用于管理动态内存的智能工具,它能够自动管理资源的释放,避免了手动释放内存带来的潜在问题,如内存泄漏和悬挂指针。使用智能指针可以让我们更加安全和方便地管理动态分配的内存。C++11引入了三种主要的智能指针:std::unique_ptrstd::shared_ptrstd::weak_ptr。下面让我们来详细讲解每一种智能指针的使用和原因:

1)std::unique_ptr

std::unique_ptr是一种独占式智能指针,表示它所指向的内存资源只有一个所有者。当这个所有者被销毁时(比如离开作用域或被显式删除),它所拥有的资源会被自动释放。因为独占式的特性,std::unique_ptr不能与其他指针共享同一个资源。

使用std::unique_ptr的方法如下:

#include <memory>

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(10); // 创建一个动态分配的int,使用std::make_unique函数

    // 使用ptr指向的内存
    std::cout << "Value of ptr: " << *ptr << std::endl; // 输出:Value of ptr: 10

    // 不需要手动释放内存,当ptr离开作用域时,内存会自动释放

    return 0;
}

2)std::shared_ptr

std::shared_ptr是一种共享式智能指针,表示它所指向的内存资源可以有多个所有者。每当创建一个新的std::shared_ptr指向同一块内存时,内部的引用计数会增加。当没有任何std::shared_ptr指向这块内存时,内存资源会自动释放。

使用std::shared_ptr的方法如下:

#include <memory>

int main() {
    std::shared_ptr<int> ptr1 = std::make_shared<int>(10); // 创建一个动态分配的int,使用std::make_shared函数
    std::shared_ptr<int> ptr2 = ptr1; // 复制ptr1,共享同一块内存

    // 使用ptr1和ptr2指向的内存
    std::cout << "Value of ptr1: " << *ptr1 << std::endl; // 输出:Value of ptr1: 10
    std::cout << "Value of ptr2: " << *ptr2 << std::endl; // 输出:Value of ptr2: 10

    // 不需要手动释放内存,当ptr1和ptr2离开作用域时,内存会自动释放

    return 0;
}

3)std::weak_ptr

std::weak_ptr是一种弱引用指针,它不会增加引用计数,也不会影响资源的生命周期。std::weak_ptr用于解决std::shared_ptr的循环引用问题,当std::shared_ptr之间存在循环引用时,会导致资源无法释放。std::weak_ptr允许我们创建一个观察者,而不影响资源的释放。

使用std::weak_ptr的方法如下:

#include <memory>

int main() {
    std::shared_ptr<int> sharedPtr = std::make_shared<int>(10);
    std::weak_ptr<int> weakPtr = sharedPtr; // 创建一个观察者weakPtr,不增加引用计数

    // 使用sharedPtr和weakPtr指向的内存
    std::cout << "Value of sharedPtr: " << *sharedPtr << std::endl; // 输出:Value of sharedPtr: 10
    if (auto ptr = weakPtr.lock()) {
        // weakPtr.lock()会尝试将weakPtr转换为shared_ptr,如果成功则表示资源仍然存在
        std::cout << "Value of weakPtr: " << *ptr << std::endl; // 输出:Value of weakPtr: 10
    } else {
        std::cout << "Resource has been released." << std::endl;
    }

    // 不需要手动释放内存,当sharedPtr离开作用域时,内存会自动释放

    return 0;
}

4.为什么使用智能指针?

使用智能指针的主要目的是自动管理动态分配的内存,避免手动释放内存可能带来的错误。手动管理内存容易导致内存泄漏、悬挂指针和二次释放等问题,而使用智能指针可以大大减少这些错误的出现。同时,智能指针的使用也能提高代码的可读性和可维护性,使得资源管理更加安全和方便。因此,在C++编程中,尽可能使用智能指针来管理动态内存,特别是在涉及到动态分配内存的情况下,使用智能指针是一种非常好的编程实践。

5.野指针

野指针是指指针在其指向的内存被释放后,未及时将指针设为nullptr或有效的内存地址,导致指针悬挂在空中,可能指向已释放的内存区域。使用野指针解引用或对其进行操作是一种未定义行为,可能导致程序崩溃或出现奇怪的结果。

int* ptr = new int; // 动态分配内存
// ... 一些代码 ...
delete ptr; // 释放内存
ptr = nullptr; // 重要!将指针设为nullptr,防止成为野指针

为了避免空指针和野指针带来的问题,应该始终养成良好的编程习惯:

  • 定义指针时初始化为nullptr或赋予有效的内存地址。
  • 使用指针前进行有效性检查,确保指针不是空指针。
  • 在释放动态分配的内存后,将指针设为nullptr,避免成为野指针。
  • 尽量使用智能指针来管理动态内存,可以更好地避免空指针和野指针的问题。

使用空指针和野指针是C++中常见的编程错误,但通过小心谨慎地使用指针,并遵循良好的编程实践,可以有效地避免这些问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值