new、delete、动态数组初始化、野指针、解引用、内存池、重载new和delete、内存泄漏等,释放崩溃

一、野指针

为什么会出现野指针

  • 指针变量没有被初始化。任何指针变量刚被创建时不会自动成为nullptr指针,它的缺省值是随机的,所以指针变量在创建的同时应当被初始化,要么将指针设置为nullptr,要么让它指向合法的内存。

  • 指针p被free或者delete之后,没有置为nullptr,让人误以为p是个合法的指针。

  • 指针操作超越了变量的作用范围。这种情况让人防不胜防,示例程序如下:

点击查看代码
class A
{
public:
    void Func(void) { std::cout << " test" << std::endl; }
};

void Test()
{
    A* p;
    {
        A  a;
        p = &a; // 注意a的生命期
    }
    p->Func();  // p是野指针
}

为什么对指针delete以后还需要置为nullptr

delete只是把指针所指的内存给释放掉,但并没有把指针本身干掉。计算机上删除数据的方式:删除数据(视频,音乐,文件等),计算机并不会把存储数据的硬盘区域(或者是内存)的内容擦除消去,而是将内容标记为可覆盖,这时候我们就不能再访问到数据,而这些可覆盖据数据会留在原来的硬盘区域直到被新的数据逐渐覆盖。delete p 的含义不是删除消灭p。而是让p所指向的地址的内容标记为可覆盖。而p=null 则是将指针指向空。delete p 之后,p指向地址可覆盖,但是p仍然指向原来的地址,随着程序的运行,原本内存可能被使用,p指向可能内容改变。所以为了安全起见,将p指向null。避免p不小心再次被使用。如果直接p=null,而不先 delete p。那么p的地址不可被覆盖,造成内存泄漏(内存不可以再使用)。

二、解引用

使用*解引用

解引用再取地址和指针有什么区别???本质上是一样的,可以去实现一下stl容器的迭代器。

三、动态数组初始化

四、重载new和delete

五、内存池

转载:内存池技术

转载:内存池设计与实现

六、内存泄漏

如何判断是否有内存泄漏

使用Purify检测内存是否泄漏

不用工具判断内存是否有泄漏:new和delete是否匹配

怎么解决内存泄漏

七、new失败、内存耗尽如何处理

  • 判断指针是否为NULL,如果是则马上用return语句终止本函数

  • 判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。

  • 为new和malloc设置异常处理函数。

八、free()崩溃

free或delete异常

  • 释放的指针地址是否和new或malloc出来的一致

  • 释放的指针大小是否被改变

转载:free()崩溃原因总结

  • 越界、漏写sizeof()、realloc()第二个参数写错

  • 改变指针的指向

  • 重复释放内存

  • 释放不是动态申请的内存

八、获取new或malloc出来的指针指向的实际内存大小

_msize()

C或C++在申请空间时,实际上会申请比预期更多的空间,一部分是因为对齐需要(也许刚好有那么一块内存可以满足条件,不需要内存对齐时多申请空间),还有另一个原因是在所申请内存首部存了额外的信息占据了一点空间,该首部其中一部分信息便是该指针对应申请空间的大小。

九、注意

  • ❓ 程序崩溃或退出后系统会自动释放内存,为什么在程序退出前还要释放内存?

十、示例代码:

点击查看代码
#include <iostream>
#include <string>

int main()
{
    int* p0 = new int[3]();  //为一个大小为三的数组申请内存空间,p0保存的是首地址,数组所有元素的值为0,内置基本数据类型最好使用()初始化
    int* p1 = new int[3];    //为一个大小为三的数组申请内存空间,p1保存的是首地址,数组所有元素的值未知
    int* p2 = new int(3);    //申请一个内存空间,里面的值为3
    std::string* str1 = new std::string[3];        //调用默认构造函数,str1为空,
    std::string* str2 = new std::string[3]();      //调用默认构造函数,str2为空
    std::string* str3 = new std::string("Hello");  //初始化为Hello
    for (int i = 0; i < 3; i++)
    {
        p1[i] = 3 * i;
    }
    for (int i = 0; i < 3; i++)
    {
        std::cout << *p1 + i << std::endl;      //0,1,2  *p1 值的基础上 +i  先解引用后++
        std::cout << *(p1 + i) << std::endl;  //0,3,6  将地址 +i,再解引用得到下一个地址对应的值
    }
    std::cout << *p2 << std::endl;

    delete[] p0;
    delete[] p1;    //基本数据类型,new的时候使用了[],delete也最好使用[],
    delete p2;

    delete[] str1;  //类类型(自定义数据类型),new的时候使用了[],delete必须使用[],不然会存在潜在风险
    delete[] str2;
    delete str3;

    p0 = nullptr;
    p1 = nullptr;  //如果不指向nullptr,delete之后,p1将是一个随机值,成为一个野指针
    p2 = nullptr;  //我们在删除一个指针之后,编译器只会释放该指针所指向的内存空间,而不会删除这个指针本身
    str1 = nullptr;
    str2 = nullptr;
    str3 = nullptr;

    return 0;
}

转载,还没看完

原文链接: https://www.cnblogs.com/mmmmmmmmm/p/14020059.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    new、delete、动态数组初始化、野指针、解引用、内存池、重载new和delete、内存泄漏等,释放崩溃

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/404256

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月24日 下午6:46
下一篇 2023年4月24日 下午6:46

相关推荐