内存分布和heap空间
内存分布程序没有加载到内存前,可执行程序内部已经分好3段信息,分别为代码区(text)、数据区(data)和未初始化数据区(bss)3 个部分(有些人直接把data和bss合起来叫做静态区或全局区)。代码区 存放 CPU 执行的机器指令。通常代码区是可共享的(即另外的执行程序可以调用它),使其可共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可。代码区通常是只读的,使其只读的原因是防止程序意外地修改了它的指令。另外,代码区还规划了局部变量的相关信息。 全局初始化数据区/静态数据区(data段) 该区包含了在程序中明确被初始化的全局变量、已经初始化的静态变量(包括全局静态变量和局部静态变量)和常量数据(如字符串常量)。 未初始化数据区(又叫 bss 区) 存入的是全局未初始化变量和未初始化静态变量。未初始化数据区的数据在程序开始执行之前被内核初始化为 0 或者空(NULL)。 程序在加载到内存前,代码区和全局区(data和bss)的大小就是固定的,程序运行期间不能改变。然后,运行可执行程序,系统把程序加载到内存,除了根据可执行程序的信息分出代码区(text)、数据区(data)和未初始化数据区(bss)之外,还额外增加了栈区、堆区。https://ask.qcloudimg.com/http-save/yehe-10182531/4cfb11e91e6c9d8f671da0af594b0f07.png代码区(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+https://ask.qcloudimg.com/http-save/yehe-10182531/6a602933ebc8369e02e3e512a88a569c.jpg
当全局变量与局部变量命名冲突时采用就近原则开辟释放 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 = {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 + 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 = (char *)malloc(sizeof(char) *10);
}
使用: 不能修改 p 的值。
for(i = 0; i < 5; i++)
{
strcpy(p, "helloheap");
}
释放内层:
for(i = 0; i < 5; i++)
{
free(p);
}
释放外层: free(p); 堆通常位于RAM的低地址区域,紧接.bss段之后,由链接脚本(如Keil的.sct文件或GCC的链接器脚本)定义其起始地址和大小。 灵活支持运行时内存需求,但需谨慎管理以避免碎片和泄漏。 Flash存储代码,RAM承载动态数据,堆位于RAM中用于动态分配。 用于存放常量数据,如字符串常量和用 const 修饰的变量。
这些数据在程序运行期间不可修改。 知晓原理对于编程很重要 平时都是只知道编程应用,还没有怎么关注过底层原理。 由程序员手动分配和释放,使用 malloc() 和 free() 等函数进行管理。
用于存放动态分配的内存。
堆区从低地址向高地址增长。 寄存器区:与 CPU 紧密相连,用于快速存储和处理数据,速度极快。可用于存放操作数、中间结果等,像累加器、通用寄存器等都在此区域。
栈区(Stack):主要用于函数调用和中断处理。当调用一个函数时,系统会在栈区为该函数的局部变量、返回地址、寄存器状态等信息分配空间;函数执行完毕后,这些空间会被自动释放。栈的生长方向通常是从高地址向低地址。
全局变量区(Data Segment):存储全局变量和静态变量。全局变量在程序的整个生命周期内都存在,静态变量的作用域可能局限于定义它的文件或函数内部,但同样在程序运行期间一直占据内存空间。
常量区(Constant Segment):存放常量数据,例如字符串常量、常量表达式等。这些常量在程序运行过程中不可修改,其内存空间在程序编译时就已确定。 动态内存分配容易产生内存碎片,这会导致内存利用率下降。在使用malloc()时,如果内存分配失败,程序可能会崩溃 由系统自动分配和释放。
用于存放局部变量、函数参数和函数的返回地址。
栈区的大小在编译时确定,通常从高地址向低地址增长。
页:
[1]