打印
[学习资料]

内存分布和heap空间

[复制链接]
565|30
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
yeates333|  楼主 | 2025-3-23 14:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
内存分布
程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码区(text)、数据区(data)和未初始化数据区(bss)3 个部分(有些人直接把data和bss合起来叫做静态区或全局区)。
代码区
存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。
全局初始化数据区/静态数据区(data段)
该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。
未初始化数据区(又叫 bss 区)
存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空(NULL)。
程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区。



代码区(text segment)
加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。
未初始化数据区(BSS)
加载的是可执行文件BSS段,位置可以分开亦可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。
全局初始化数据区/静态数据区(data segment)
加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。
栈区(stack)
栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。
堆区(heap)
堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序。用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。
变量
局部变量:
  概念:定义在函数内部的变量。   作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。   生命周期:局部变量:从变量定义开始,函数调用完成。 --- 函数内部。
全局变量:
概念:定义在函数 外 部的变量。  作用域:从定义位置开始,默认到本文件内部。 其他文件如果想使用,可以通过声明方式将作用域导出。  生命周期: 程序启动开始,程序终止结束。  --- 程序执行期间。
static全局变量:
定义语法: 在全局变量定义之前添加 static 关键字。        static int a = 10;  作用域:被限制在本文件内部,不允许通过声明导出到其他文件。  生命周期:程序启动开始,程序终止结束。  --- 程序执行期间。
static局部变量:
  定义语法: 在局部变量定义之前添加 static 关键字。   特性: 静态局部变量只定义一次。在全局位置。 通常用来做计数器。   作用域:从定义位置开始,到包裹该变量的第一个右大括号结束。   生命周期:程序启动开始,程序终止结束。  --- 程序执行期间
全局函数:  函数
     定义语法: 函数原型 + 函数体      生命周期:程序启动开始,程序终止结束。  --- 程序执行期间。
static函数:
    定义语法:static + 函数原型 + 函数体     static 函数 只能在 本文件内部使用。 其他文件即使声明也无效。    生命周期:程序启动开始,程序终止结束。  --- 程序执行期间。  
内存4区模型
代码段:.text段。 程序源代码(二进制形式)。  数据段:只读数据段 .rodata段。初始化数据段 .data段。 未初始化数据段 .bss 段。   stack:栈。 在其之上开辟 栈帧。    windows 1M --- 10M    Linux: 8M --- 16M    heap:堆。 给用户自定义数据提供空间。 约 1.3G+  



当全局变量与局部变量命名冲突时采用就近原则
开辟释放 heap 空间
void *malloc(size_t size);  申请 size 大小的空间         返回实际申请到的内存空间首地址。 【我们通常拿来当数组用】   void free(void *ptr);    释放申请的空间         参数: malloc返回的地址值。
使用 heap 空间
     空间时连续。 当成数组使用。      free后的空间,不会立即失效。 通常将free后的 地址置为NULL。      free 地址必须 是 malloc申请地址。否则出错。      如果malloc之后的地址一定会变化,那么使用临时变量tmp 保存。  
代码
#define _CRT_SECURE_NO_WARNINGS
#include
<stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main()
{
        //int arr[1000000] = {10, 20, 40};
        int *p = (int *)malloc(sizeof(int) * 10);
        //char *str = (char *)malloc(sizeof(char)*10);
        if (p == NULL)
        {
                printf("malloc error\n");
                return -1;
        }
        char *tmp = p;  // 记录malloc返回的地址值。用于free

        // 写数据到 malloc 空间。
        for (size_t i = 0; i < 10; i++)
        {
                p[i] = i + 10;
        }
        // 读出malloc空间中的数据
        //for (size_t i = 0; i < 10; i++)
        //{
        //        printf("%d ", *(p+i));
        //}
        for (size_t i = 0; i < 10; i++)
        {
                printf("%d ", *p);
                p++;
        }

        // 释放申请的内存。
        free(tmp);
        p = NULL;

        system("pause");
        return EXIT_SUCCESS;
}



二级指针对应的 heap空间
申请外层指针: char **p = (char **)malloc(sizeof(char *) * 5);
申请内层指针:




for(i = 0; i < 5; i++)
{
     p[i] = (char *)malloc(sizeof(char) *10);
}



    使用: 不能修改 p 的值。


for(i = 0; i < 5; i++)
{
     strcpy(p[i], "helloheap");
}



    释放内层:


 
for(i = 0; i < 5; i++)
{
     free(p[i]);
}



    释放外层:
   free(p);

使用特权

评论回复
沙发
albertaabbot| | 2025-4-4 11:08 | 只看该作者
堆通常位于RAM的低地址区域,紧接.bss段之后,由链接脚本(如Keil的.sct文件或GCC的链接器脚本)定义其起始地址和大小。

使用特权

评论回复
板凳
mickit| | 2025-4-4 12:32 | 只看该作者
灵活支持运行时内存需求,但需谨慎管理以避免碎片和泄漏。

使用特权

评论回复
地板
uiint| | 2025-4-4 14:04 | 只看该作者
Flash存储代码,RAM承载动态数据,堆位于RAM中用于动态分配。

使用特权

评论回复
5
yorkbarney| | 2025-4-4 17:06 | 只看该作者
用于存放常量数据,如字符串常量和用 const 修饰的变量。
这些数据在程序运行期间不可修改。

使用特权

评论回复
6
caigang13| | 2025-4-5 11:00 | 只看该作者
知晓原理对于编程很重要

使用特权

评论回复
7
chenjun89| | 2025-4-5 16:41 | 只看该作者
平时都是只知道编程应用,还没有怎么关注过底层原理。

使用特权

评论回复
8
bestwell| | 2025-4-5 20:54 | 只看该作者
由程序员手动分配和释放,使用 malloc() 和 free() 等函数进行管理。
用于存放动态分配的内存。
堆区从低地址向高地址增长。

使用特权

评论回复
9
cemaj| | 2025-4-8 09:40 | 只看该作者
寄存器区:与 CPU 紧密相连,用于快速存储和处理数据,速度极快。可用于存放操作数、中间结果等,像累加器、通用寄存器等都在此区域。
栈区(Stack):主要用于函数调用和中断处理。当调用一个函数时,系统会在栈区为该函数的局部变量、返回地址、寄存器状态等信息分配空间;函数执行完毕后,这些空间会被自动释放。栈的生长方向通常是从高地址向低地址。
全局变量区(Data Segment):存储全局变量和静态变量。全局变量在程序的整个生命周期内都存在,静态变量的作用域可能局限于定义它的文件或函数内部,但同样在程序运行期间一直占据内存空间。
常量区(Constant Segment):存放常量数据,例如字符串常量、常量表达式等。这些常量在程序运行过程中不可修改,其内存空间在程序编译时就已确定。

使用特权

评论回复
10
minzisc| | 2025-4-8 12:46 | 只看该作者
动态内存分配容易产生内存碎片,这会导致内存利用率下降。在使用malloc()时,如果内存分配失败,程序可能会崩溃

使用特权

评论回复
11
lzbf| | 2025-4-8 15:49 | 只看该作者
由系统自动分配和释放。
用于存放局部变量、函数参数和函数的返回地址。
栈区的大小在编译时确定,通常从高地址向低地址增长。

使用特权

评论回复
12
burgessmaggie| | 2025-4-9 19:44 | 只看该作者
尽量使用静态分配或栈空间(stack)存储数据,避免使用堆空间。

使用特权

评论回复
13
ccook11| | 2025-4-10 02:21 | 只看该作者
单片机的RAM资源有限,堆空间的大小也受到限制,不适合频繁的动态内存分配。

使用特权

评论回复
14
cemaj| | 2025-4-10 14:33 | 只看该作者
​内存区域        ​地址范围        ​用途        ​特点
​Flash/ROM        0x08000000–0x0803FFFF        存储程序代码和只读数据(.text, .rodata)        非易失性,掉电不丢失
​SRAM(RAM)​        0x20000000–0x20007FFF        存储动态数据(.data, .bss, 堆、栈)        易失性,需初始化
​EEPROM        可选(依型号而定)        存储需长期保存的非易失数据        容量小,写入速度慢
​外设寄存器        特定地址映射        控制外设(如GPIO、UART)        通过内存映射访问

使用特权

评论回复
15
yeates333|  楼主 | 2025-4-10 16:47 | 只看该作者
与栈区和全局变量区不同,堆区的内存分配是动态的,可在程序运行时根据需要申请和释放。

使用特权

评论回复
16
nomomy| | 2025-4-10 18:51 | 只看该作者
堆空间是单片机内存中用于动态内存分配的区域,通常位于RAM中。堆空间的管理方式直接影响程序的灵活性和性能。

使用特权

评论回复
17
macpherson| | 2025-4-10 20:55 | 只看该作者
频繁的动态内存分配和释放可能导致内存碎片,降低内存利用率。

使用特权

评论回复
18
pl202| | 2025-4-12 20:21 | 只看该作者
单片机的内存资源有限,堆区的大小通常也受到严格限制。

使用特权

评论回复
19
vivilyly| | 2025-4-12 22:16 | 只看该作者
C语言标准库提供了malloc和free函数用于堆空间的动态分配和释放。

使用特权

评论回复
20
pl202| | 2025-4-13 10:57 | 只看该作者
单片机资源有限,尽量使用全局/局部变量

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

20

主题

1398

帖子

1

粉丝