深入理解C/C++的内存管理

在C和C++中,高效的内存管理是编写性能优化和资源高效利用程序的关键。本文将深入探讨C/C++内存管理的各个方面,包括内存的分布、C语言和C++中的动态内存管理方式,以及newdelete操作符的使用

C/C++内存分布

C和C++程序的内存可以分为以下几个区域:

  • 栈(Stack):自动存储局部变量。当函数被调用时,局部变量在栈上创建,函数返回时,这些变量被销毁。
  • 堆(Heap):用于动态内存分配。与栈不同,堆上的内存分配和释放需要手动管理。
  • 全局/静态存储区:存放全局变量和静态变量。这部分内存在程序启动时分配,在程序结束时释放。
  • 文字常量区:常量字符串等常量数据存放的地方。它们在程序生命周期内不变。
  • 程序代码区:存放程序执行代码的内存区域。

理解这些区域对于避免内存泄漏和优化程序性能至关重要。

C语言中动态内存管理方式

C语言提供了几种动态内存管理的方法,包括:

  • malloc:分配指定大小的内存块。分配的内存未初始化,可能包含垃圾数据。
  • calloc:分配并初始化指定数量的内存块。初始化为零。
  • realloc:重新分配之前分配的内存块的大小。
  • free:释放之前分配的内存块。

这些函数允许程序在运行时根据需要分配和释放内存,但也需要开发者负责管理这些内存,以避免内存泄漏。

cpp 复制代码
#include <stdlib.h>

void dynamicMemoryExample() {
    int* ptr = (int*)malloc(sizeof(int) * 4); // 分配内存
    if (ptr != NULL) {
        for (int i = 0; i < 4; i++) {
            ptr[i] = i; // 使用内存
        }
    }
    free(ptr); // 释放内存
}

C++中动态内存管理

C++提高了动态内存管理的抽象层次,通过newdelete操作符提供更为直观和安全的方式来处理内存。

  • 使用new分配内存不仅会分配内存,还会调用对象的构造函数,这提供了初始化对象的机会。
  • 使用delete释放内存时,会调用对象的析构函数,然后释放内存,这提供了清理资源的机会
cpp 复制代码
#include <iostream>

void dynamicMemoryExampleInCpp() {
    int* ptr = new int(10); // 动态分配内存并初始化
    std::cout << *ptr << std::endl; // 使用内存
    delete ptr; // 释放内存,并调用析构函数
}
cpp 复制代码
void Test()
{
  // 动态申请一个int类型的空间
  int* ptr4 = new int;
  
  // 动态申请一个int类型的空间并初始化为10
  int* ptr5 = new int(10);
  
  // 动态申请10个int类型的空间
  int* ptr6 = new int[3];
  delete ptr4;
  delete ptr5;
  delete[] ptr6;
}

注意:申请和释放单个元素的空间,使用 new delete 操作符,申请和释放连续的空间,使用
new[] delete[] ,注意:匹配起来使用。

newdelete****操作自定义类型

cpp 复制代码
class A
{
public:
 A(int a = 0)
 : _a(a)
 {
 cout << "A():" << this << endl;}
 ~A()
 {
 cout << "~A():" << this << endl;
 }
private:
 int _a;
};
int main()
{
 // new/delete 和 malloc/free最大区别是 new/delete对于【自定义类型】除了开空间
还会调用构造函数和析构函数
 A* p1 = (A*)malloc(sizeof(A));
 A* p2 = new A(1);
 free(p1);
 delete p2;
 // 内置类型是几乎是一样的
 int* p3 = (int*)malloc(sizeof(int)); // C
 int* p4 = new int;
free(p3);
delete p4;
 A* p5 = (A*)malloc(sizeof(A)*10);
 A* p6 = new A[10];
 free(p5);
 delete[] p6;
 return 0;
}

注意:在申请自定义类型的空间时,new会调用构造函数,delete会调用析构函数,而malloc与
free不会。

operator new与operator delete函数

在C++中,newdelete不仅仅是操作符,它们背后实际上是调用了operator newoperator delete函数。这两个函数负责分配和释放动态内存。重要的是,C++允许开发者重载这些函数来提供自定义的内存管理策略。

自定义内存管理

自定义内存管理通常用于优化性能,例如通过实现特定的内存池来避免频繁地向操作系统请求小块内存。这样可以显著减少内存碎片以及提高内存分配和释放的效率。

cpp 复制代码
#include <iostream>
#include <cstdlib>

void* operator new(size_t size) {
    std::cout << "Custom new for size: " << size << std::endl;
    return malloc(size);
}

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

通过这个简单的例子,我们看到了如何拦截所有的newdelete调用,以便在分配和释放内存时执行自定义逻辑

new和delete的实现原理

new操作符在底层调用operator new函数来分配内存,并在成功分配内存后调用对象的构造函数。相似地,delete操作符首先调用对象的析构函数,然后调用operator delete函数来释放内存。

这种分离使得newdelete不仅仅关注内存的分配和释放,还能确保对象生命周期的正确管理。
如果申请的是内置类型的空间, new 和 malloc , delete 和 free 基本类似,不同的地方是:
new/delete 申请和释放的是单个元素的空间, new[] 和 delete[] 申请的是连续空间,而且 new 在申
请空间失败时会抛异常, malloc 会返回 NULL

自定义类型
new 的原理

  1. 调用 operator new 函数申请空间
  1. 在申请的空间上执行构造函数,完成对象的构造
    delete 的原理
  1. 在空间上执行析构函数,完成对象中资源的清理工作
  1. 调用 operator delete 函数释放对象的空间
    new T[N] 的原理
  1. 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对
    象空间的申请
  1. 在申请的空间上执行 N 次构造函数
    delete[] 的原理
  1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理
  2. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释
    放空间

也就是说当调用delete[]的时候,地址的第一个字节处存放的是n,也就是需要调用n次析构,如果我们使用的是delete,默认从当前位置开始析构,那么位置就会出错。

定位new表达式(placement-new)

定位new表达式是C++中的一个高级特性,允许在已分配的内存上构造对象。这对于内存池、缓存管理等场景非常有用,因为它允许重复使用已分配的内存来创建新对象,避免了频繁的内存分配和释放操作。

cpp 复制代码
#include <new> // 必须包含这个头文件

char buffer[1024]; // 预分配内存

void placementNewExample() {
    int* p = new (buffer) int(10); // 在buffer上构造int对象
    // 使用p...
}

使用定位new表达式时,需要确保操作的内存足够大且正确对齐,以容纳指定类型的对象。

常见面试题

内存区域划分

问:请描述C/C++程序中的内存区域划分。

答:C/C++程序的内存分为栈、堆、全局/静态存储区、文字常量区和程序代码区。

动态内存管理

问:mallocnew的区别是什么?

答:malloc仅分配内存,不调用构造函数;new分配内存的同时调用构造函数初始化对象。相应地,freedelete的区别在于delete会先调用析构函数再释放内存。

定位new

问:什么是定位new表达式,它有什么用途?

答:定位new表达式允许在已分配的内存上构造对象。这在需要在特定位置创建对象,或者在内存池、缓存管理等场景下重复使用内存时非常有用。


通过本文,我们深入探讨了C/C++中的内存管理,从基本的内存区域划分到高级特性如定位new表达式,以及如何通过重载operator newoperator delete来实现自定义内存管理策略。理解这些概念不仅对于写出高性能的C/C++代码至关重要,也是面试中常见的问题。希望这篇博客能帮助你在C/C++内存管理方面达到新的高度!

相关推荐
Yhame.2 小时前
深入理解 Java 中的 ArrayList 和 List:泛型与动态数组
java·开发语言
编程之路,妙趣横生2 小时前
list模拟实现
c++
mazo_command4 小时前
【MATLAB课设五子棋教程】(附源码)
开发语言·matlab
IT猿手4 小时前
多目标应用(一):多目标麋鹿优化算法(MOEHO)求解10个工程应用,提供完整MATLAB代码
开发语言·人工智能·算法·机器学习·matlab
青春男大4 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
88号技师4 小时前
几款性能优秀的差分进化算法DE(SaDE、JADE,SHADE,LSHADE、LSHADE_SPACMA、LSHADE_EpSin)-附Matlab免费代码
开发语言·人工智能·算法·matlab·优化算法
Zer0_on4 小时前
数据结构栈和队列
c语言·开发语言·数据结构
一只小bit4 小时前
数据结构之栈,队列,树
c语言·开发语言·数据结构·c++
马浩同学5 小时前
【GD32】从零开始学GD32单片机 | DAC数模转换器 + 三角波输出例程
c语言·单片机·嵌入式硬件·mcu
一个没有本领的人5 小时前
win11+matlab2021a配置C-COT
c语言·开发语言·matlab·目标跟踪