C/C++:内存分区,内存分配

embedded/2025/3/17 11:18:21/

一、内存分布图

直接上图分析 

向下增长的栈:栈顶指针(SP)从高地址向低地址移动;

向上增长的栈:栈顶指针(SP)从底地址向高地址移动。 

验证代码(linux平台下测试,这个栈的生长方向不同平台会有些差异,感兴趣的话可自行测试~):

#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>int unInitialG;
static int sUnInitialG;
int initialG = 0;
static int sInitialG = 10;
const int cG = 0;void testStackIncreaseDirection(){int a;int b;printf("stack testStackIncreaseDirection allocation (stack):     %p\n", &a);printf("stack testStackIncreaseDirection allocation (stack):     %p\n", &b);
}int main() {int a = 0;int b = 0;int c = 0;void* heap_ptr = malloc(1024);  // 通过 malloc 分配(通常在堆)void* mmap_ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);  // mmap 分配(通常在映射区域)printf("stack hight allocation (stack):     %p\n", &a);printf("stack middle allocation (stack):    %p\n", &b);printf("stack slow allocation (stack):      %p\n", &c);testStackIncreaseDirection();printf("Mmap hight allocation (mmap):       %p\n", mmap_ptr);printf("Heap slow allocation (malloc):      %p\n", heap_ptr);printf("bss1 allocation (.bss):             %p\n", &sUnInitialG);printf("bss2 allocation (.bss):             %p\n", &unInitialG);printf("data1 allocation (.data):           %p\n", &initialG);printf("data2 allocation (.data):           %p\n", &sInitialG);printf("data3 allocation (.data):           %p\n", &cG);free(heap_ptr);       // 释放 malloc 分配的内存munmap(mmap_ptr, 4096);  // 释放 mmap 分配的内存return 0;
}

结果图如下:

 

 注意:在一个函数里面的局部变量,比如void testStackIncreaseDirection()函数,先定义的后压栈。 

二、C++的内存怎么分配?

简洁版 

  1. 全局变量,全局静态变量未初始化的存放在(.bss);
  2. 全局变量,全局静态变量初始化的存放在(.data);
  3. 函数参数、局部变量和函数返回值存放在栈区(stack);
  4. malloc和new申请的内存存放在堆区(heap),详细区别如下所示;
  5. 可执行文件存放在代码区(text)。

详细版 

(1)代码区(Code Segment / Text Segment)

  • 存放程序的 可执行代码(即编译后的指令)。
  • 只读(防止程序意外修改自身代码)。
  • 共享(多个进程可共享相同的代码段)。

(2)全局/静态区(Global & Static Segment)

  • 存放 全局变量静态变量(static 变量)
  • 变量的生命周期贯穿整个程序运行期(直到进程结束)。
  • 初始化的变量存放在 .data 段,未初始化的存放在 .bss 段。

(3)栈区(Stack Segment)

  • 由编译器自动分配释放,用于存储局部变量、函数参数、返回地址等
  • 栈是 后进先出(LIFO) 结构,每进入一个函数,就会在栈上分配一块新的内存,函数执行完毕后自动释放。

特点

  • 速度快(直接由 CPU 指令管理)。
  • 不需要手动管理内存。
  • 受限于栈空间大小(通常 1~8MB),递归太深可能导致栈溢出(Stack Overflow)

(4)堆区(Heap Segment)

  • 由程序员手动管理(使用 new / deletemalloc / free)。
  • 适合大对象动态分配的对象(大小和生存期不确定)。
  • 堆区通常比栈大,但分配/释放效率比栈低。

特点

  • 适用于大对象、动态数组、类对象
  • 程序员必须手动管理,否则可能会导致内存泄漏

(5)常量区(Readonly Data Segment)

  • 存储 字符串字面量const 修饰的全局变量
  • 该区域的内容通常不可修改(否则可能会导致段错误 Segmentation Fault)。

三、栈和堆的对比

对比项栈(Stack)堆(Heap)
分配方式由编译器自动分配由程序员手动分配 (new / malloc)
释放方式由编译器自动释放需要手动释放 (delete / free)
访问速度快(LIFO 结构,CPU 指令直接管理)慢(自由分配,可能导致碎片化)
生命周期随函数调用自动管理需要程序员手动管理
大小较小(一般 1MB~8MB)较大(可动态扩展)
典型应用局部变量、函数参数、返回地址动态数组、对象、大型数据结构

四、malloc vs new 的核心区别

区别点mallocnew
函数 / 关键字C 标准库函数C++ 关键字
返回类型void*(需要强制转换)具体类型指针(自动转换)
初始化不会初始化(内存内容不确定)调用构造函数,自动初始化
内存分配方式使用 heap manager 调用 sbrk() / mmap()使用 operator new(可能使用 malloc 底层分配)
释放方式free(ptr)delete ptr(调用析构函数)
适用于C 语言和 C++ 低级内存操作C++ 面向对象编程(支持构造 / 析构函数)

五、malloc 分配流程

调用 malloc(size) 时的底层步骤:

  1. 检查 size
    • size < 128KB,使用 sbrk() 增长堆。
    • size ≥ 128KB,使用 mmap() 直接映射内存(调用 mmap() 直接映射内存页)。
  2. 检查 free list(空闲链表)
    • free list 中有足够大的块,则复用空闲块。
    • 若没有足够的空闲块,则调用 sbrk()mmap()
  3. 返回指针,供程序使用。

六、malloc/free vs new/delete 的使用场景

使用场景推荐方式
C 语言malloc/free
C++ 对象(类)new/delete
需要构造函数 / 析构函数new/delete
简单数据结构(如 int[]new/deletestd::vector
手动内存管理,性能优化(如内存池)malloc/free

end!

有哪里不对的欢迎指出,批评指正,谢谢。

制作不易,麻烦观众老爷点个赞,再走呗,鼓励一下,感谢! 


http://www.ppmy.cn/embedded/173335.html

相关文章

单机DeepSeek做PPT,YYDS!

今天同事问我 AI 能不能做 PPT&#xff0c;有个述职报告要做&#xff0c;问我能不能帮忙&#xff0c;这时我脑海中的第一画面就是 DeepSeek Kimi DeepSeek 擅长逻辑构建与内容生成&#xff0c;其深度思考能力当前测试下来&#xff0c;不愧为国内No.1&#xff0c;而且还会把中间…

Spark Sql 简单校验的实现

在网上参考了很多资料&#xff0c;都是要依赖Sparksession&#xff0c;这个需要spark环境&#xff0c;非常不友好&#xff0c;jdk版本也不好控制。不使用Sparksession获取上下文&#xff0c;利用spark和antlr的静态方法使用java 实现简单的spark sql 的语法以及内置函数的校验。…

【AIGC】OpenAI 集成 Langchain 操作实战使用详解

目录 一、前言 二、前置准备 2.1 安装 Langchain必须的依赖 2.1.1 python环境 2.1.2 langchain openai 环境 2.1.3 准备一个apikey 2.1.4 langchain 核心组件 三、Langchain 各组件使用 3.1 Chat models组件 3.1.1 Invocation 使用 3.1.1.1 结果解析 3.2 提示词模板…

97.HarmonyOS NEXT跑马灯组件教程:基础概念与架构设计

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; HarmonyOS NEXT跑马灯组件教程&#xff1a;基础概念与架构设计 1. 跑马灯组件概述 跑马灯&#xff08;Marquee&#xff09;是一种常见的UI组件&a…

操作系统八股文整理(一)

操作系统八股文整理 一、进程和线程的区别二、进程与线程的切换过程一、进程切换进程切换的步骤&#xff1a; 二、线程切换线程切换的步骤&#xff1a; 三、进程切换与线程切换的对比四、上下文切换的优化 三、系统调用一、系统调用的触发二、从用户空间切换到内核空间三、执行…

2025-03-16 学习记录--C/C++-PTA 练习4-7 求e的近似值

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、题目描述 ⭐️ 练习4-7 求e的近似值 自然常数 e 可以用级数 11/1!1/2!⋯1/n!⋯ 来近似计算。本题要求对给定的非负整数 n&…

【鸿蒙开发】Hi3861学习笔记-Visual Studio Code安装(New)

00. 目录 文章目录 00. 目录01. Visual Studio Code概述02. Visual Studio Code下载03. Visual Studio Code安装04. Visual Studio Code插件05. 附录 01. Visual Studio Code概述 vscode是一种简化且高效的代码编辑器&#xff0c;同时支持诸如调试&#xff0c;任务执行和版本管…

Linux内核传输层TCP源码分析

一、传输控制协议&#xff08;TCP&#xff09; 1.tcp报头 TCP 是 Internet 中最常用的传输协议&#xff0c;很多著名协议都基于 TCP。其中最著名的可能就是 HTTP&#xff0c;但这里有必要提及其它一些著名协议&#xff0c;如 SSH、SMTP、SSL 等&#xff0c;不同于 UDP&…