C++学习笔记之内存1

电子说

1.3w人已加入

描述

整体来说,这些内容都相当基础,对于大多数初学者来讲还是能接受的,在面试日常实习、暑期实习、校招过程中还是相当有帮助的,对在校生来说还是相当友好滴~

1、内存泄漏?怎么解决?

内存泄漏是指程序在动态分配内存后,未释放或者未能完全释放该内存空间的情况。这样会导致内存不断被占用,进而导致程序性能下降、甚至崩溃等问题。

解决内存泄漏问题需要先确定内存泄漏的原因,可以通过以下几个步骤来解决内存泄漏问题:

  1. 排查代码:查看代码中是否有明显的内存泄漏的情况,例如忘记释放内存等。
  2. 使用工具检查:可以使用一些内存泄漏检测工具,例如Valgrind、Purify、AddressSanitizer等,来检测程序中的内存泄漏情况。
  3. 检查资源的使用情况:程序中除了内存泄漏还可能存在其他资源泄漏,例如文件句柄、网络连接等,需要逐一检查并进行相应的释放。
  4. 使用智能指针:在C++中,可以使用智能指针(shared_ptr、unique_ptr、weak_ptr)等RAII技术来管理动态内存,自动释放资源,避免忘记释放内存的问题。
  5. 重构代码:如果程序中的内存泄漏问题比较严重,无法通过以上方法解决,可以考虑对代码进行重构,优化内存使用情况,避免内存泄漏的问题。

2、说说常见的内存泄漏都有哪些?

  • 对象被无意识地持续引用:在使用完对象后,程序没有将其引用置为NULL,导致这些对象一直占用内存。
  • 内存分配未释放:程序中使用了动态分配内存的函数(如malloc、calloc、realloc等)分配内存,但没有调用free函数进行释放。
  • 大对象未分配内存池:如果需要频繁地分配、释放大对象(如数组、矩阵等),直接调用系统函数分配内存可能会导致内存碎片化,进而导致系统内存泄漏。此时,可以使用内存池技术来解决这个问题。
  • 循环引用:当两个或多个对象之间互相引用时,它们会互相持有对方的引用,当这些对象中有一个引用没有被释放时,将导致内存泄漏。
  • 持续增长的缓存:当一个缓存区在使用后没有被清空或者不定期的清理,会导致缓存中的数据越来越多,最终导致内存泄漏。

为了解决内存泄漏问题,需要进行内存泄漏检测和内存泄漏排查。一些编程语言和开发工具可以提供内存泄漏检测的功能,可以通过这些工具来查找内存泄漏的代码位置,并及时修复。同时,在编写代码时,也应该遵循良好的编程习惯,及时释放已经不再使用的内存,以避免内存泄漏问题的出现。

3、如何避免内存泄漏?

  • 确保在程序中每次使用完内存后及时释放,特别是对于动态分配的内存,要在不需要时及时释放。
  • 确保内存释放的正确性,例如使用free函数时,需要确保传递给它的指针是指向动态分配的内存空间。
  • 对于需要长时间占用内存的程序,可以考虑采用内存池技术,动态分配一定数量的内存空间,在使用完成后放回内存池中,避免频繁申请和释放内存造成的性能影响。
  • 对于需要频繁申请和释放内存的程序,可以考虑采用内存缓存技术,将频繁使用的内存缓存起来,避免频繁申请和释放内存造成的性能影响。
  • 使用内存泄漏检测工具,例如Valgrind等,来帮助检测和解决内存泄漏问题。

4、你知道常见的内存错误吗?再说说解决的对策?

  1. 内存泄漏:指已经不再需要使用的内存没有被释放,导致内存浪费。解决方案可以采用以下方法:
    1. 手动管理内存并调用free()释放不再使用的内存;
    2. 使用智能指针等自动内存管理机制;
    3. 使用内存泄漏检测工具定位和修复内存泄漏问题。
  2. 内存溢出:指分配的内存空间不足以满足当前需要,导致程序崩溃。解决方案可以采用以下方法:
    1. 程序设计时充分考虑内存使用情况,合理地规划内存分配;
    2. 使用内存监控工具或者操作系统提供的性能工具,监测和分析程序内存使用情况;
    3. 优化算法或数据结构,减少内存占用。
  3. 野指针:指指针指向了已经被释放的内存空间,或者指针未被初始化就被使用。解决方案可以采用以下方法:
    1. 对指针变量进行初始化;
    2. 在指针使用之前,检查其是否为空或者指向的内存是否被释放;
    3. 使用nullptr代替NULL,避免空指针问题;
    4. 使用智能指针等自动内存管理机制,避免手动释放内存的错误。

5、详细说说内存的分配方式?

内存的分配方式有两种:静态内存分配和动态内存分配。

  1. 静态内存分配:在程序编译时就已经分配好内存,运行时不能改变分配的内存大小,程序执行速度快,但是空间利用率低,不能灵活分配内存空间。常见的静态内存分配有:
    1. 全局变量:在程序编译时分配内存,整个程序执行期间内存不释放。
    2. 静态局部变量:在函数执行时分配内存,但是该内存空间在函数执行完毕后不释放,可以用 static 修饰符来声明。
    3. 静态数组:在编译时就分配内存,执行期间内存不释放。
    4. 静态结构体:在编译时就分配内存,执行期间内存不释放。
  2. 动态内存分配:在程序运行时才分配内存,可以根据需要灵活地分配和释放内存空间。常见的动态内存分配有:
    1. malloc():在堆上分配指定大小的内存空间,返回一个指向这段内存的指针。
    2. calloc():在堆上分配指定数量和大小的内存空间,返回一个指向这段内存的指针。
    3. realloc():调整已分配内存的大小,返回一个指向这段内存的指针。
    4. free():释放已经分配的内存。

动态内存分配的优点是空间利用率高、可以根据需要灵活地分配和释放内存空间,但是容易引起内存泄漏和内存碎片问题。因此在使用动态内存分配时要注意及时释放已经不再需要的内存空间,避免内存泄漏问题的发生。

6、堆和栈的区别?

栈(stack)是一种先进后出(Last In First Out,LIFO)的数据结构,由编译器自动管理,存放程序的局部变量、函数参数和返回地址等信息。栈的内存空间由操作系统自动分配和释放,其空间大小是固定的,一般为几MB至几十MB。

堆(heap)则是一种动态内存分配方式,程序员需要手动申请和释放堆内存。堆的内存空间由操作系统管理,其大小可以动态增加或减少,一般情况下堆的空间远远大于栈。

在程序运行过程中,栈的分配和释放速度较快,但栈的容量有限;堆的分配和释放速度较慢,但堆的容量较大。因此,对于较小的数据结构,优先使用栈来分配内存;对于较大的数据结构,需要动态管理内存时,可以使用堆来分配内存。

7、如何控制C++的内存分配?

在 C++ 中,可以通过重载 new 和 delete 运算符来控制内存分配。

重载 new 和 delete 运算符的方式如下:

void* operator new(size_t size);
void operator delete(void* p) noexcept;

其中,operator new 用于分配内存,operator delete 用于释放内存。

默认情况下,operator new 调用 malloc 函数分配内存,operator delete 调用 free 函数释放内存。但是,我们可以重载这些运算符,自定义内存分配和释放方式。

例如,下面是一个简单的例子,演示如何重载 operator newoperator delete 运算符:

#include 


void* operator new(size_t size)
{
    std::cout << "Allocating " << size << " bytes of memory" << std::endl;
    void* p = malloc(size);
    return p;
}


void operator delete(void* p) noexcept
{
    std::cout << "Deallocating memory" << std::endl;
    free(p);
}


int main()
{
    int* ptr = new int;
    delete ptr;
    return 0;
}

运行上面的代码,输出如下:

Allocating 4 bytes of memory
Deallocating memory

可以看到,重载后的 operator newoperator delete 运算符被调用,并输出了相关信息。

通过重载 operator newoperator delete 运算符,我们可以实现自定义的内存分配和释放方式,从而更好地控制 C++ 的内存分配。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分