CSDN 博客:CC++ 内存管理详解

CSDN 博客:C/C++ 内存管理详解

在软件开发过程中,内存管理是一个非常重要的环节。对于 C 和 C++ 这两种编程语言,它们都拥有独特的内存管理机制,理解这些机制对于编写高效、健壮的程序至关重要。本文将详细讲解 C/C++ 内存管理相关的内容,并重点分析不同内存分配方式的区别和使用场景。

1. C/C++ 内存分布

在 C 和 C++ 中,内存可以分为多个区域,包括栈、堆、数据段、代码段等。这些区域分别用来存储不同类型的数据。通过以下示例代码,我们可以直观地理解这些区域的作用:

int globalVar = 1;           // 全局变量
static int staticGlobalVar = 1; // 静态全局变量void Test() {static int staticVar = 1; // 静态局部变量int localVar = 1;         // 局部变量int num1[10] = {1, 2, 3, 4}; // 局部数组char char2[] = "abcd";    // 字符数组const char* pChar3 = "abcd"; // 字符指针常量int* ptr1 = (int*)malloc(sizeof(int) * 4);  // 动态分配内存int* ptr2 = (int*)calloc(4, sizeof(int));  // 动态分配并初始化int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); // 重新分配内存free(ptr1); // 释放内存free(ptr3);
}

以下是对应变量在内存中的分布情况:

变量名存储位置存储段
globalVar全局变量数据段(静态区)
staticGlobalVar静态全局变量数据段(静态区)
staticVar静态局部变量数据段(静态区)
localVar局部变量
num1局部数组
char2字符数组
*char2数组元素存储位置
pChar3指针变量
*pChar3常量字符串 “abcd”代码段(常量区)
ptr1指针变量
*ptr1动态分配内存
ptr2指针变量
*ptr2动态分配内存
ptr3指针变量
*ptr3动态分配内存
内存区域分类:
  • 栈(Stack):存储局部变量(如 localVar),以及函数调用时的参数和返回值。
  • 堆(Heap):存储动态分配的内存(如通过 malloccallocrealloc 分配的内存)。
  • 数据段(Data Segment):存储全局变量和静态变量(如 globalVarstaticGlobalVar)。
  • 代码段(Code Segment):存储程序的可执行代码以及只读常量(如 pChar3 所指向的字符串)。
2. C语言中的动态内存管理

C 语言提供了几种用于动态分配内存的函数:malloccallocreallocfree。这些函数用于在程序运行时动态地分配和释放内存。

2.1 malloc、calloc 和 realloc 的区别
  • malloc:用于分配指定大小的内存块,内存中的内容未初始化。
  • calloc:类似于 malloc,但会将内存初始化为零。它的参数为元素的数量和每个元素的大小。
  • realloc:用于调整之前分配的内存块的大小,如果新大小大于原大小,可能会移动内存块的位置。
示例代码:
int* ptr1 = (int*)malloc(sizeof(int) * 4);  // 分配4个int类型大小的内存块
int* ptr2 = (int*)calloc(4, sizeof(int));   // 分配并初始化4个int类型大小的内存块
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4); // 重新分配内存
free(ptr1);
free(ptr3);
2.2 malloc 实现原理

malloc 底层通常通过操作系统的 brkmmap 系统调用分配内存。具体实现可能因平台和 C 标准库的不同而有所区别。在 GNU C 库(glibc)中,malloc 通过维护一个自由链表来跟踪已分配和未分配的内存块,并根据请求的大小寻找合适的内存块进行分配。

3. C++ 内存管理

C++ 继承了 C 语言的内存管理方式,并在此基础上引入了 newdelete 操作符,提供更方便的动态内存管理机制。与 mallocfree 不同,newdelete 适用于对象的动态内存分配,并且会自动调用构造函数和析构函数。

3.1 new 和 delete 操作符
  • new:用于动态分配内存并调用对象的构造函数。
  • delete:用于释放动态分配的内存并调用对象的析构函数。
示例代码:
int* ptr = new int;        // 动态分配一个int类型的空间
delete ptr;                // 释放内存int* arr = new int[10];    // 动态分配10个int类型的空间
delete[] arr;              // 释放连续内存
3.2 new 和 delete 的实现原理

在 C++ 中,new 操作符会首先调用 operator new 函数来分配内存,然后在该内存上调用构造函数。而 delete 操作符则会先调用析构函数清理对象资源,再调用 operator delete 函数释放内存。

void* operator new(size_t size) {void* p = malloc(size);if (!p) throw std::bad_alloc();  // 如果分配失败,抛出异常return p;
}void operator delete(void* p) {free(p);  // 释放内存
}
4. new 和 malloc 的区别

虽然 newmalloc 都可以用于动态分配内存,但它们有以下几点不同:

  • new 是操作符,malloc 是函数new 是 C++ 内置的操作符,而 malloc 是 C 语言中的标准库函数。
  • 初始化malloc 只分配内存,不会对其进行初始化;而 new 不仅分配内存,还会调用构造函数初始化对象。
  • 异常处理malloc 分配失败时返回 NULL,而 new 分配失败时会抛出 std::bad_alloc 异常。
  • 自定义类型malloc 不能调用构造函数,因此不适合分配自定义类型的对象,而 new 则可以调用构造函数来初始化对象。
4.1 示例代码:
class A {
public:A(int a = 0) : _a(a) {std::cout << "A() called" << std::endl;}~A() {std::cout << "~A() called" << std::endl;}
private:int _a;
};A* obj1 = (A*)malloc(sizeof(A));  // 使用 malloc 分配内存,但不会调用构造函数
free(obj1);  // 释放内存A* obj2 = new A(10);  // 使用 new 分配内存并调用构造函数
delete obj2;  // 释放内存并调用析构函数
5. operator new 与 operator delete

operator newoperator delete 是全局函数,用于实现 newdelete 的底层操作。它们通常会调用 mallocfree 来完成内存的分配与释放。


**### CSDN 博客:C/C++ 内存管理详解(续)

接着上篇,我们已经介绍了 C/C++ 的内存分布和基本的动态内存管理方式。本篇将继续详细讲解内存管理中的一些高级内容,包括 operator newoperator delete 的实现原理、内置类型和自定义类型的内存管理、placement new、以及 malloc/freenew/delete 的更深入对比。

5. operator new 与 operator delete

operator newoperator delete 是系统提供的全局函数,分别用于动态分配和释放内存。它们实际上是 newdelete 操作符的底层实现。在 C++ 中,new 操作符首先调用 operator new 分配内存,然后调用构造函数初始化对象;而 delete 操作符首先调用析构函数清理对象,然后调用 operator delete 释放内存。

5.1 operator new 的实现原理

operator new 的实现原理可以用如下代码描述:

void* operator new(size_t size) {void* p;// 尝试分配 size 字节的内存while ((p = malloc(size)) == nullptr) {// 如果 malloc 分配失败,尝试执行内存不足的应对措施if (_callnewh(size) == 0) {// 如果没有用户设置的处理措施,抛出 std::bad_alloc 异常throw std::bad_alloc();}}return p;
}

可以看到,operator new 本质上是通过 malloc 来分配内存的。不同的是,如果内存分配失败,operator new 会尝试调用用户设置的内存不足处理程序(_callnewh()),而 malloc 只是简单返回 NULL

5.2 operator delete 的实现原理

operator delete 的实现则相对简单,它直接调用 free 来释放内存:

void operator delete(void* p) {free(p);
}
6. new 和 delete 的实现原理
6.1 内置类型的内存管理

对于内置类型(如 intfloat 等),newmalloc 在内存分配上是类似的。它们都分配指定大小的内存并返回指向该内存的指针。然而,newmalloc 的不同之处在于:

  • 单个元素的分配new 可以分配单个内置类型的内存,而 malloc 只能分配一块指定大小的内存。
  • 异常处理:当内存分配失败时,new 会抛出异常,而 malloc 则返回 NULL
示例代码:
int* p1 = new int;   // 分配单个int类型空间
delete p1;           // 释放内存int* p2 = (int*)malloc(sizeof(int)); // 使用malloc分配内存
free(p2);            // 释放内存
6.2 自定义类型的内存管理

对于自定义类型,newdelete 的作用更加明显,因为它们除了分配和释放内存之外,还会自动调用构造函数和析构函数。这一特性使得 newdelete 成为管理复杂对象的首选。

6.2.1 new 的工作过程:
  1. 调用 operator new 分配内存:为对象分配所需的内存。
  2. 在已分配的内存上调用构造函数:通过构造函数来初始化对象。
6.2.2 delete 的工作过程:
  1. 调用析构函数:析构函数会清理对象占用的资源(如释放动态分配的内存等)。
  2. 调用 operator delete 释放内存:通过 free 或类似的机制将内存归还给操作系统。
示例代码:
class A {
public:A(int a) : _a(a) {std::cout << "Constructor called" << std::endl;}~A() {std::cout << "Destructor called" << std::endl;}
private:int _a;
};int main() {A* obj = new A(10);  // 动态分配并调用构造函数delete obj;          // 调用析构函数并释放内存
}
7. malloc/free 和 new/delete 的区别

malloc/freenew/delete 都是从堆上分配内存,并且都需要用户手动释放,但它们之间存在一些关键区别:

7.1 语法上的区别
  • malloc/free 是函数mallocfree 是 C 标准库中的函数,用于动态内存管理。
  • new/delete 是操作符newdelete 是 C++ 的内置操作符,主要用于对象的动态内存管理。
7.2 初始化的区别
  • malloc 不会初始化内存malloc 只是分配一块内存,而不负责初始化内容。
  • new 可以调用构造函数初始化对象new 不仅分配内存,还会调用构造函数来初始化对象。
7.3 内存分配失败的处理方式
  • malloc 分配失败返回 NULL:如果 malloc 无法分配内存,它会返回 NULL,程序员需要手动检查返回值。
  • new 分配失败抛出 std::bad_alloc 异常:当 new 失败时,它会抛出异常,而不是返回 NULL
7.4 自定义类型的对象分配
  • malloc/free 不会调用构造函数和析构函数malloc 仅仅分配内存,无法初始化对象,也不会调用析构函数来清理对象的资源。
  • new/delete 会调用构造函数和析构函数new 在分配内存后会调用构造函数,delete 在释放内存前会调用析构函数。
示例代码对比:
// 使用 malloc/free
A* obj1 = (A*)malloc(sizeof(A));  // 仅仅分配内存,不调用构造函数
free(obj1);                       // 仅仅释放内存,不调用析构函数// 使用 new/delete
A* obj2 = new A(10);  // 分配内存并调用构造函数
delete obj2;          // 调用析构函数并释放内存
8. 定位 new 表达式 (Placement-new)

定位 new 表达式是一种高级用法,它允许在已分配的内存上构造对象,而不需要重新分配内存。典型的使用场景是内存池或者需要精细控制内存分配的地方。

8.1 定位 new 的使用方式

定位 new 表达式的语法如下:

new (place_address) type;

其中 place_address 是内存的地址,而 type 是需要构造的对象类型。

示例代码:
class A {
public:A(int a = 0) : _a(a) {std::cout << "A() called" << std::endl;}~A() {std::cout << "~A() called" << std::endl;}
private:int _a;
};int main() {void* buffer = malloc(sizeof(A)); // 分配一块内存A* obj = new(buffer) A(10);       // 在指定的内存上构造对象obj->~A();                        // 手动调用析构函数free(buffer);                     // 释放内存
}
8.2 定位 new 的应用场景

定位 new 常用于内存池管理。内存池是一种预先分配大量内存的技术,通过在这块内存上手动管理对象的分配和释放,能够极大提高程序的性能。特别是对于实时系统或嵌入式系统,使用内存池可以避免频繁调用操作系统的内存管理函数,减少系统开销。


结语

通过以上两部分的详细讲解,我们全面介绍了 C/C++ 的内存管理机制。从基本的 malloc/freenew/delete,再到定位 new 表达式,内存管理的不同方式各有适用场景。理解并合理使用这些机制,能够帮助开发者编写高效且健壮的代码,特别是在需要精确控制内存分配的场景中,正确的内存管理能够极大提升程序的性能。

在实践中,建议程序员根据具体需求选择适当的内存管理方式,避免内存泄漏和资源浪费,确保程序的健壮性和可维护性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/66331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++编程进阶:标准库中的算法库解析

文章目录 概述1. 非修改性序列操作2. 修改性序列操作3. 排序相关算法4. 二分查找算法5. 合并与集合操作6. 堆操作7. 最小/最大操作8. 数值算法(`<numeric>`头文件)概述 算法库总览:介绍了C++ 标准库提供的海量算法,这些算法作用于各类容器(如vector、list、set等)和…

Express 加 sqlite3 写一个简单博客

例图&#xff1a; 搭建 命令&#xff1a; 前提已装好node.js 开始创建项目结构 npm init -y package.json:{"name": "ex01","version": "1.0.0","main": "index.js","scripts": {"test": &q…

Linux双端口服务器:端口1的文件系统目录挂载到端口2

目录 一、服务器安装NFS服务并配置二、文件挂载三、持久化挂载总结为什么服务器配置多个端口 目前有一台服务器&#xff0c;不过他设置了两个SSH的端口&#xff0c;通过下面方法可以让这两个端口连接的主机能够共享同一个文件系统&#xff0c;原本这两个端口的文件系统是隔离的…

Objective-C语言的循环实现

Objective-C语言中的循环实现 在程序设计中&#xff0c;循环是一个非常重要的概念&#xff0c;它允许我们重复执行一段代码&#xff0c;直到满足特定条件为止。在Objective-C语言中&#xff0c;我们有多种方式实现循环&#xff0c;包括for循环、while循环和do-while循环。本文…

osg运行时关于gl.h错误的问题解决

osg测试的时候&#xff0c;运行生成的代码&#xff0c;出现了如下的一堆错误问题&#xff1a; 14:09:17:921 生成开始于 14:09... 14:09:18:208 1>------ 已启动生成: 项目: Project3, 配置: Debug x64 ------ 14:09:18:596 1>osgt1.cpp 14:09:18:932 1>C…

nginx-灰度发布策略(split_clients)

一. 简述&#xff1a; 基于客户端的灰度发布&#xff08;也称为蓝绿部署或金丝雀发布&#xff09;是一种逐步将新版本的服务或应用暴露给部分用户&#xff0c;以确保在出现问题时可以快速回滚并最小化影响的技术。对于 Nginx&#xff0c;可以通过配置和使用不同的模块来实现基于…

Meta 发布 Llama 3.3:一个性能和效率均有所提升的多语言模型

Meta 发布 Llama 3.3:一个性能和效率均有所提升的多语言模型 Meta 发布了 Llama 3.3,这是一款多语言大语言模型,旨在支持研究和行业中的一系列人工智能应用。该模型具有 128k 个 token 上下文窗口,并对架构进行了改进以提高效率,在推理、编码和多语言任务的基准测试中表现…

【NLP自然语言处理】Transformer模型的几大核心优势与应用前景

目录 &#x1f354; Transformer的并行计算 &#x1f354; Transformer架构的并行化过程 2.1 Transformer架构中Encoder的并行化 2.2 Transformer架构中Decoder的并行化 &#x1f354; Transformer的特征抽取能力 &#x1f354; 为什么说Transformer可以代替seq2seq? 4…

Maven中管理SNAPSHOT版本含义及作用

在开发过程中突然产生了一个疑问&#xff1a;IDEA中 maven deploy的依赖包的版本号,比如 1.0.0-SNAPSHOT是在哪配置的&#xff1f;在远程仓库中的版本和这个有关系吗 &#xff1f; 在 Maven 中&#xff0c;-SNAPSHOT 后缀是用于标识项目版本为快照&#xff08;Snapshot&#xf…

数据结构与算法之排序

9.1 排序的概念 1. 排序的定义 定义&#xff1a;排序是将表中的记录按关键字递增&#xff08;或递减&#xff09;有序排列的过程。说明&#xff1a;数据中可以存在相同关键字的记录。本章主要考虑递增排序。扩展&#xff1a;排序是数据处理中的基本操作之一&#xff0c;广泛应用…

《C++11》各种初始化方式的详细列举与对比

在 C 中&#xff0c;初始化对象的方式多种多样。随着 C 标准的演进&#xff0c;特别是 C11 的引入&#xff0c;初始化方式得到了显著的扩展和改进。本文将详细列举 C 中的各种初始化方式&#xff0c;并对它们进行对比&#xff0c;帮助开发者更好地理解和应用这些特性。 1. C98…

算法:两个升序单链表的合并

将两个按值排序的带头结点的单链表La和Lb排列成一个升序的 单链表&#xff0c;并返回一个新的单链表的表头指针 &#xff08;两个升序合并成升序&#xff0c;用尾插法&#xff09; LinkList Merge_LinkList(LNode* La, LNode* Lb) {//准备工作LNode* Lc;//新链表的头结点LNode…

基于 Python Django 的西西家居全屋定制系统(源码+部署+文档)

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

25考研|重邮软件工程复试攻略!

与计算机一样&#xff0c;重邮复试不合格也很有可能被淘汰&#xff01;快快认真准备起来&#xff01; 一、复试内容 1、笔试&#xff1a;分值100 2、综合面试&#xff1a;满分100 主要考核考生的综合素质和业务能力&#xff0c;由各招生学院具体组织实施&#xff0c;综合面试…

如何制作重识别数据集及如何解决all query identities do not appear in gallery的问题

如何制作重识别数据集 数据集制作链接 注意点&#xff1a; 按照上述方式制作完成数据集之后&#xff0c;分别建立3个文件夹&#xff0c;分别为train&#xff0c;test&#xff0c;query&#xff0c; 值得注意的是&#xff0c;query文件里的相机编号要进行修改&#xff0c;修改…

链地址法(哈希桶)

链地址法&#xff08;哈希桶&#xff09; 解决冲突的思路 开放定址法中所有的元素都放到哈希表⾥&#xff0c;链地址法中所有的数据不再直接存储在哈希表中&#xff0c;哈希表 中存储⼀个指针&#xff0c;没有数据映射这个位置时&#xff0c;这个指针为空&#xff0c;有多个数…

【C语言】可移植性陷阱与缺陷(七): 除法运算时发生的截断

在C语言编程中&#xff0c;除法运算可能会引发一些与可移植性相关的问题&#xff0c;特别是当涉及到整数除法时发生的截断&#xff08;truncation&#xff09;。不同平台对于整数除法的行为和处理方式可能会有所不同&#xff0c;这可能导致代码在不同编译器或硬件平台上的行为不…

2. 模型和算法

1. 模型&#xff08;Model&#xff09; 模型指的是通过机器学习或其他方法从数据中提取出的一个数学结构或表示&#xff0c;它可以用于做出预测、分类、回归或其他决策任务。模型是机器学习的核心&#xff0c;它在一定程度上是数据的“抽象化”&#xff0c;用于表达输入与输出…

C++ 原子变量

C 原子变量 文章目录 C 原子变量1. 原子变量是什么&#xff1f;2. 原子操作的特点3. 原子变量的作用1. 多线程安全的共享数据访问2. 替代锁机制3. 实现低级同步算法 4. 原子变量的常见操作5. 内存顺序&#xff08;Memory Ordering&#xff09;内存顺序控制在原子变量中的作用如…

前后端分离架构设计与实现:构建现代Web应用的基石

前后端分离架构设计与实现&#xff1a;构建现代Web应用的基石 引言 随着互联网技术的发展&#xff0c;Web应用变得越来越复杂和多样化。传统的单体式架构难以满足快速迭代、团队协作以及性能优化的需求。前后端分离架构应运而生&#xff0c;它不仅提高了开发效率&#xff0c;…