C++ 数据内存分布揭秘:从栈到堆的探索之旅

目录

1. 栈(Stack)

2. 堆(Heap)

malloc和new的区别

堆与栈在C++中的异同点详解

3. 数据段(Data Segment)

4. 代码段(Code Segment)

5. 动态内存分配的陷阱


当我们谈论C++编程时,对内存布局的理解至关重要。本文将深入探讨C++中各种变量和数据结构在内存中的分布情况,包括栈(stack)、堆(heap)以及其他内存区域,并通过多个代码示例直观呈现这些概念的实际应用。

1. 栈(Stack)

栈是一种快速高效的内存区域,由编译器自动管理。每当函数被调用时,函数的局部变量和临时变量都会在栈上分配空间,函数执行完毕后,这些空间将自动释放。

void functionExample() {
    int localVar = 10; // 局部变量存储在栈上
    char str[] = "Hello, Stack!"; // 本地数组也存储在栈上
}

int main() {
    functionExample(); // 调用函数时,局部变量在栈上分配和释放
}

2. 堆(Heap)

堆则是用于动态内存分配的区域,程序员可以通过`new`或`malloc`函数申请内存,通过`delete`或`free`函数释放内存。堆内存的分配和释放由程序员手动控制,相较于栈更灵活但也更容易产生内存泄漏。

int main() {
    int* heapVar = new int(20); // 动态分配的整型变量存储在堆上
    char* heapArray = new char[100]; // 动态分配的字符数组存储在堆上

    delete heapVar;
    delete[] heapArray; // 必须手动释放堆上的内存
}
malloc和new的区别

mallocnew 都是用来在程序运行时动态分配内存的机制,但它们之间存在一些关键性的区别,这些区别主要源于它们各自的设计初衷和功能特性:

  1. 来源和适用语言:

    • malloc 是 C 语言标准库函数,位于 <stdlib.h> 头文件中。

    • new 是 C++ 关键字,是 C++ 语言特有的内存管理机制。

  2. 内存分配与类型关联:

    • malloc 需要开发者指定内存分配的大小(以字节为单位),返回的是 void* 类型的指针,需要手动类型转换为所需类型。

    • new 则可以根据类型自动计算所需内存大小,例如 int* ptr = new int;,编译器知道一个 int 类型需要多少内存,所以不需要指定大小。new 返回的是相应类型的指针,无需额外类型转换。

  3. 构造函数和析构函数调用:

    • malloc 只负责分配内存,不执行任何构造函数,对于非 POD(Plain Old Data)类型(如带有构造函数的类),单纯使用 malloc 分配内存不足以初始化对象。

    • new 在分配内存的同时还会调用相应类型的构造函数初始化对象,而当使用 delete 释放内存时,会调用对象的析构函数完成必要的清理工作。

  4. 错误处理:

    • 如果 malloc 分配内存失败,会返回 NULL,需要程序员检查返回值判断是否分配成功。

    • new 在分配内存失败时会抛出 std::bad_alloc 异常,而不是简单地返回 NULL,这对于异常安全编程更加友好。

  5. 内存释放:

    • 使用 malloc 分配的内存需使用 free 函数来释放。

    • 使用 new 分配的内存则需使用 delete 或 delete[](对于数组)来释放。

  6. 重载操作符:

    • C++ 允许重载 new 和 delete 操作符,使得内存管理更为灵活,可以定制内存分配和释放的行为。

    • malloc 和 free 函数本身是不可以被重载的。

  7. 内存对齐和调试支持:

    • new 在分配内存时会考虑到对象的内存对齐要求,确保分配的内存满足目标平台的对齐约束。

    • new 还可以提供调试版本,比如 _DEBUG 宏定义开启时,Visual Studio 提供的 debug 版本 new 会记录分配信息,方便调试内存泄漏等问题。

总之,malloc 更接近底层,提供了基础的内存分配功能;而 new 是 C++ 中的高级抽象,除了分配内存外,还负责初始化和清理对象,具有更好的类型安全性,并且能更好地融入面向对象编程的环境。在 C++ 编程中,除非有特别的理由,一般推荐使用 newdelete 来进行内存管理。

在C++中的异同点详解

相同点:

  1. 均属于内存区域:堆和栈都是C++程序运行时使用的内存区域,都是为了存储程序运行过程中的数据。

  2. 参与程序执行流程:无论是栈上的局部变量还是堆上动态分配的对象,都直接参与到程序的执行过程中,对程序的运行状态有着直接影响。

不同点:

栈(Stack)

  • 自动管理:栈内存由编译器自动分配和释放,程序员无须手动介入。当函数调用发生时,栈帧(包含局部变量、函数参数等)自动压入栈;函数执行完毕,栈帧自动弹出并释放内存。

  • 分配效率:栈内存分配速度较快,通常不存在内存碎片问题。

  • 大小限制:栈内存大小有限制且相对较小,超出限制可能导致栈溢出错误。

  • 生命周期:栈上变量的生命周期与所属函数的执行期一致,函数结束时,所有局部变量随之消亡。

  • 存储内容:栈主要用于存储函数参数、局部变量、临时对象等。

堆(Heap)

  • 手动管理:堆内存由程序员通过newmalloc等函数动态申请,通过deletefree等函数手动释放。如果忘记释放,将会导致内存泄漏。

  • 分配效率:堆内存分配较慢,需要查找合适的内存区域,并有可能引发内存碎片。

  • 大小可变:堆内存大小相对于栈更大,没有预设的大小限制,可以根据程序需求动态调整。

  • 生命周期:堆上动态分配的对象生命期直到程序员显式释放为止,不受函数调用的影响。

  • 存储内容:堆主要用于存储动态分配的对象、大尺寸数组和其他需要长期存在的数据结构。

  • 碎片问题:频繁的动态内存分配和释放可能导致堆内存碎片化,降低内存利用率。

总结起来,栈内存适用于短期、快速分配和释放的小规模数据,而堆内存更适合用于存储大尺寸或生命周期不确定的数据。理解两者之间的差异对于编写高性能、稳定且不易出错的C++程序至关重要。

3. 数据段(Data Segment)

数据段又分为初始化数据段(Data Segment)和未初始化数据段(BSS Segment)。初始化全局变量和静态变量存储在初始化数据段,未初始化全局变量和静态变量存储在未初始化数据段,并在程序启动时自动清零。

int globalVar = 10; // 初始化全局变量存放在数据段
static int staticGlobalVar = 20; // 静态全局变量同样存放在数据段

void Test() {
    static int staticVar = 30; // 静态局部变量也在数据段
}

4. 代码段(Code Segment)

代码段存放程序的机器指令和常量字符串。例如:

void someFunction() {}

int main() {
    const char* pConstStr = "Hello, Code Segment!"; // 字符串字面量存放在代码段
    someFunction(); // 函数的机器指令也在代码段
}

5. 动态内存分配的陷阱

频繁在堆上进行动态内存分配可能导致内存碎片。例如:

void dynamicMemoryTest() {
    for (int i = 0; i < 1000; ++i) {
        int* p = new int[i % 100]; // 不规则的内存分配可能产生内存碎片
        delete[] p;
    }
}

通过以上介绍和代码示例,我们对C++中内存分布有了更直观的认识。了解这些基础知识有助于我们在设计程序时更好地管理和优化内存使用,减少不必要的性能损失和潜在的内存泄漏风险。在实践中,合理结合栈、堆以及其他内存区域的特点,将有助于编写出高效、健壮的C++应用程序。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

这些CTF,不仅学技术,还有巨额奖金!

前言&#xff1a; 不会吧&#xff0c;不会吧&#xff0c;不会还有安全er不知道CTF是什么吧&#xff1f; 在程序员的世界里&#xff0c;也有ACM这样的编程大赛&#xff0c;成为各路编程高手一较高下展示能力的平台。 那在网络安全的圈子里&#xff0c;各路黑客红客白帽子们又…

rag-embeddings基础流程

什么是检索增强的生成模型 LLM 固有的局限性 LLM 的知识不是实时的LLM 可能不知道你私有的领域/业务知识 检索增强生成 RAG&#xff08;Retrieval Augmented Generation&#xff09;顾名思义&#xff0c;通过检索的方法来增强生成模型的能力。 类比&#xff1a;你可以把这个…

linux 内核编译

目录 Linux操作系统框架 Linux内核的主要功能&#xff1a; Linux的内核目录结构&#xff1a; 结构图: 详细介绍&#xff1a; uname - a 补充 编译之前 UTC 时间补充 Linux内核编译流程: 方法一: 官方内核编译: 1. 运行 build.sh 脚本&#xff0c; 记得加 sudo 权…

Day 24 数据库管理及数据类型

数据库管理及数据类型 一&#xff1a;数据类型 1.数值类型 整数类型 ​ 整数类型&#xff1a;TINYINT SMALLINT MEDIUMINT INT BIGINT ​ 作用&#xff1a;用于存储用户的年龄、游戏的Level、经验值等 浮点数类型 ​ 浮点数类型&#xff1a;FLOAT DOUBLE ​ 作用&#xf…

linux或ubuntu环境下需要自行安装vivado USB Program下载程序驱动

如果在linux或ubuntu环境下&#xff0c;不安装驱动是无法下载FPGA程序的。在linux或ubuntu环境下安装程序不要自动安装。 johnjohn-wang:~/vitis2021.2/Vivado/2021.2/data/xicom/cable_drivers/lin64/install_script/install_drivers$ sudo ./install_drivers

1天搞定SpringBoot+Vue全栈开发 (7)Axios网络请求

1.Axios的使用 Axios中文文档 | Axios中文网Axios 是一个基于 promise 的网络请求库&#xff0c;可以用于浏览器和 node.jshttps://www.axios-http.cn/ 2.与vue整合 App.vue: <template><div id"app"><Moviev-for"movie in movies":key&qu…

一致性评价政策加速行业仿制药洗牌,惯爱为代表的新锐品牌崭露头角

从印度神油到以形补形&#xff0c;男人的问题&#xff0c;从古至今一直困扰着很多人&#xff0c;大多人都羞于启齿。然而&#xff0c;沉默的背后&#xff0c;隐藏着令人震惊的数据&#xff1a;据统计显示&#xff0c;ED&#xff08;勃起功能障碍&#xff09;是男性生殖系统发病…

9.Java内置锁的核心原理-Synchronized

文章目录 Java内置锁的核心原理-Synchronized1.线程安全问题1.1.自增运算分析1.2.临界区资源和临界区代码片段 2.synchronized关键字2.1.synchronized同步方法2.2.synchronized同步代码块2.3.synchronized同步方法和synchronized同步代码块区别2.4.静态的同步方法2.5.内置锁的释…

18、ESP32 ESP-NOW 点对点通信

ESP-NOW 是乐鑫自主研发的无连接通信协议&#xff0c;具有短数据包传输功能。该协议使多个设备能够以简单的方式相互通信。 ESP-NOW 功能 ESP-NOW 支持以下功能&#xff1a; 加密和未加密的单播通信;混合加密和未加密的对等设备;最多可携带 250 字节 的有效载荷;发送回调功能…

C#修改默认参数settings文件

右击项目在设置中进行修改&#xff1a; 千万不要在这里改。 如果要在自己的项目里添加这个文件&#xff0c;首先新建个文件夹&#xff0c;然后添加.setting文件&#xff0c;然后再像上面说的那样添加属性。

通过 Java 操作 redis -- String 基本命令

关于 redis String 类型的相关命令推荐看 Redis - String 字符串 要想通过 Java 操作 redis&#xff0c;首先要连接上 redis 服务器&#xff0c;推荐看通过 Java 操作 redis -- 连接 redis 本博客只介绍了一小部分常用的命令&#xff0c;其他的命令根据上面推荐的博客也能很简单…

Penpad再获 Presto Labs 投资,Scroll 生态持续扩张

​Penpad 是 Scroll 生态的 LaunchPad 平台&#xff0c;其整计划像收益聚合器以及 RWA 等功能于一体的综合性 Web3 平台拓展&#xff0c;该平台在近期频获资本市场关注&#xff0c;并获得了多个知名投资者/投资机构的支持。 截止到本文发布前&#xff0c;Penpad 已经获得了包括…

【免费】虚拟同步发电机(VSG)惯量阻尼自适应控制仿真模型【simulink】

目录 主要内容 仿真模型要点 2.1 整体仿真模型 2.2 电压电流双闭环模块 2.3 SVPWM调制策略 2.4 无功电压模块 2.5 自适应控制策略及算法 部分结果 下载链接 主要内容 该模型为simulink仿真模型&#xff0c;主要实现的内容如下&#xff1a; 随着风力发电、光…

4.请求体

什么是请求体(Request Body) 请求体是客户端发送到API的数据。 响应体是API发送给客户端的数据 API几乎总是必须发送一个响应体&#xff0c;但是客户端并不需要一直发送请求体 定义请求体&#xff0c;需要使用 Pydantic 模型 不能通过GET请求发送请求体发送请求体数据&…

ISIS的工作原理

1.邻居关系建立 &#xff08;1&#xff09;IS-IS领接关系建立原则 1、通过将以太网接口模拟成点到点接口&#xff0c;可以建立点到点链路邻接关系。 2、当链路两端IS-IS接口的地址不在同一网段时&#xff0c;如果配置接口对接收的Hello报文不作IP地址检查&#xff0c;也可以建…

基于Springboot的教学辅助系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的教学辅助系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&…

FreeBSD安装Miniconda,python启动core dumped的问题

综述&#xff1a; 学会在FreeBSD安装Miniconda后&#xff0c;在一台服务器上安装却碰到问题&#xff0c;安装好后&#xff0c;执行python报错&#xff1a;Segmentation fault (core dumped) 。 以前成功的是在FreeBSD13版本&#xff0c;报错的这个是FreeBSD14版本&#xff0c…

基于FPGA的多路彩灯控制器VHDL代码Quartus仿真

名称&#xff1a;基于FPGA的多路彩灯控制器VHDL代码Quartus仿真&#xff08;文末获取&#xff09; 软件&#xff1a;Quartus 语言&#xff1a;VHDL 代码功能&#xff1a; 多路彩灯控制器 综合训练内容要求 设计一台基于FPGA的多路彩灯控制器的设计。要求如下 1.彩灯从左…

SpringBoot中HandlerInterceptor拦截器的构建详细教程

作用范围&#xff1a;拦截器主要作用于Spring MVC的DispatcherServlet处理流程中&#xff0c;针对进入Controller层的请求进行拦截处理。它基于Java的反射机制&#xff0c;通过AOP&#xff08;面向切面编程&#xff09;的思想实现&#xff0c;因此它能够访问Spring容器中的Bean…

C语言栈的含义与栈数据操作代码详解!

引言&#xff1a;在本篇博客中&#xff0c;我们将学到数据结构——栈&#xff0c;讲到栈的含义与关于栈的数据操作代码。栈可以在顺序表、双向链表以及单链表的基础上实现&#xff0c;而于本篇博客中&#xff0c;我们选择在顺序表的基础上实现栈。 更多有关C语言和数据结构知识…