参考:程序运行时对应的内存分布关系
作者:嵌入式基地(公众号)
发布时间: 2021-04-28
网址:https://mp.weixin.qq.com/s/AVDPZawSjg9HtxEm8vsFBA

参考:静态变量与动态变量的定义与区别
作者:JoannaJuanCV
发布时间: 2019-01-23 13:14:33
网址:https://blog.csdn.net/zfjBIT/article/details/86608393?spm=1001.2014.3001.5502

内存相关博文:
1、内存四区(代码区 静态区 栈区 堆区)
2、程序运行时对应的内存分布(BSS段、数据段、代码段、堆、栈)关系
3、深入理解STM32内存管理

目录

  • 前言
    • 全局变量初始化
    • 局部变量(栈的引入)
  • 内存分区介绍
    • 程序运行时的内存分区主要分为BSS段、数据段、代码段、堆、栈
    • 静态内存
    • 动态内存
    • 静态变量和动态变量
  • 代码测试
  • 简单说明

前言

以下是韦东山老师讲解笔记: https://www.bilibili.com/video/BV1VM4y137Pm?p=14&spm_id_from=pageDriver

全局变量初始化


注:全局变量全部copy到RAM,也就是重定位。

局部变量(栈的引入)

main()函数里面的局部变量在栈里面


之前全局变量是整段复制,局部变量没有那么高的效率,是一个一个执行汇编指令写入到栈里面

内存分区介绍

程序运行时的内存分区主要分为BSS段、数据段、代码段、堆、栈

  • BSS段:Block Started by Symbol,一般是指存放程序中未初始化的全局变量的一块内存区域。BSS段属于静态内存(下面有介绍,韦东山老师称之为ZI段)分配。

  • 数据段:data segment,一般是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。

  • 代码段:code segment/text segment,通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些 只读的常数变量 ,例如字符串常量等。程序段为程序代码在内存中的映射。一个程序可以在内存中有多个副本。

  • 堆:heap,堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc/free等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张)/释放的内存从堆中被剔除(堆被缩减)。

  • 栈:stack,栈又称堆栈,存放程序的局部变量(但不包括static声明的变量, static 意味着在数据段中 存放变量)。除此以外,在函数被调用时,栈用来传递参数和返回值。

静态内存

静态内存是由系统自动分配内存,由系统自动释放。静态内存是在栈中分配的,假如main函数调用另一个函数,那么就把被调用函数压到一个栈里面。执行这个被调函数就是使系统为这个函数分配的所有内存空间逐个出栈。出栈全部结束就是被调用函数执行完毕。

出栈的顺序是先进后出,也就是先进栈的后执行(前面介绍的print输出例子),因为先进栈被压在下面,所以执行的永远是栈顶的内容。程序执行完毕的意思就是“栈里面所有的内容全部都出栈了”。出栈就是“释放”。栈顶全部出栈后原来位于栈顶就会成为栈顶,然后继续执行该栈定内容,继续出栈。整个程序全部执行完毕的意思就是“系统所分配的内存空间全部出栈”,内存全部释放完毕。所以系统为静态变量分配的内存空间在程序中执行完毕后都会被释放。

动态内存

动态内存是由程序员手动释放,函数终止不会被系统自动释放。这说明他肯定不是在栈里面分配的。那他是在什么地方分配的呢?是在“堆中”分配的,栈是一种存储结构,堆不是一种存储结构,堆是分配内存的一种排序方式。也就是说,动态内存是以堆排序的方式分配的。以前讲排序,如冒泡排序,插入排序,选择排序,快速排序。堆排序也是一种排序方式,因为动态内存是在堆中分配的,是以排序的方式分配的,不是在栈中,所以函数运行结束后也不会被释放。

也因为动态内存是由程序员手动分配,手动释放,所以这时候就会有一个比较严重的问题:如果忘记释放了,就会导致内存泄露,所以动态分配内存有优点,也有缺点。动态内存的使用非常灵活,但需要注意的问题也很多。

静态变量和动态变量

1.定义上,静态变量比动态变量在多一个关键字static,比如:

动态变量::int i;

静态变量:static int i;

2.动态变量在子程序中,每次调用都会从它的初始值开始调用,而不管他在函数中经历了什么变化;静态变量会从变化后的值继续改变。

void fun()
{int j=0;j++;printf("%d",j);
}void fun1()
{static int j=0;j++;printf("%d",j);
}void main()
{int i;for(i=0;i<5;i++)fun();//输出结果为11111printf("\n");for(i=0;i<5;i++)fun1();//输出结果为12345
}

程序中内存分布图

APUE中的C内存分布图

代码测试

#include <stdio.h>
#include <stdlib.h>int g1=0, g2=0, g3=0;static int max(int i) {int m1 = 0, m2 , m3 = 0, *p_max;static n1_max = 0, n2_max , n3_max = 0;p_max = (int*)malloc(10);printf("打印max程序地址\n");printf("in max: %p\n\n",max);printf("打印max传入参数地址\n");printf("in max: %p\n\n",&i);printf("打印max函数中静态变量地址\n");printf("%p\n",&n1_max); < 打印各本地变量的内存地址printf("%p\n",&n2_max);printf("%p\n\n",&n3_max);printf("打印max函数中局部变量地址\n");printf("%p\n",&m1);     < 打印各本地变量的内存地址printf("%p\n",&m2);printf("%p\n\n",&m3);printf("打印max函数中malloc分配地址\n");printf("%p\n\n",p_max); < 打印各本地变量的内存地址if(i) {return 1;} else {return 0;}}int main(int argc, char **argv) {static int s1 = 0, s2, s3 = 0;int v1 = 0, v2, v3 = 0;int *p;   p = (int*)malloc(10);printf("打印各全局变量(已初始化)的内存地址\n");printf("%p\n",&g1); < 打印各全局变量的内存地址printf("%p\n",&g2);printf("%p\n\n",&g3);printf("======================\n");printf("打印程序初始程序main地址\n");printf("main: %p\n\n", main);printf("打印主参地址\n");printf("argv: %p\n\n",argv);printf("打印各静态变量的内存地址\n");printf("%p\n",&s1); < 打印各静态变量的内存地址printf("%p\n",&s2);printf("%p\n\n",&s3);printf("打印各局部变量的内存地址\n");printf("%p\n",&v1); < 打印各本地变量的内存地址printf("%p\n",&v2);printf("%p\n\n",&v3);printf("打印malloc分配的堆地址\n");printf("malloc: %p\n\n",p);printf("======================\n");max(v1);printf("======================\n");printf("打印子函数起始地址\n");printf("max: %p\n\n",max);system("pause");return 0;}

根据输出结果可以看出,传入的参数,局部变量,都是在栈顶分布,随着子函数的增多而向下增长。函数的调用地址(函数运行代码),全局变量,静态变量都是在分配内存的低部存在,而malloc分配的堆则存在于这些内存之上,并向上生长。

打印各全局变量(已初始化)的内存地址
00405008
0040500C
00405010======================
打印程序初始程序main地址
main: 00401457打印主参地址
argv: 00DE15C8打印各静态变量的内存地址
00405014
00405018
0040501C打印各局部变量的内存地址
0060FEF8
0060FEF4
0060FEF0打印malloc分配的堆地址
malloc: 00DE15D8======================
打印max程序地址
in max: 00401334打印max传入参数地址
in max: 0060FEE0打印max函数中静态变量地址
00405020
00405024
00405028打印max函数中局部变量地址
0060FEC8
0060FEC4
0060FEC0打印max函数中malloc分配地址
00DE3E38======================
打印子函数起始地址
max: 00401334请按任意键继续. . .

简单说明

左边的是UNIX/LINUX系统的执行文件,右边是对应进程逻辑地址空间的划分情况。

首先是堆栈区(stack),堆栈是由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。栈的申请是由系统自动分配,如在函数内部申请一个局部变量 int h,同时判别所申请空间是否小于栈的剩余空间,如若小于的话,在堆栈中为其开辟空间,为程序提供内存,否则将报异常提示栈溢出。

其次是堆(heap),堆一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。堆的申请是由程序员自己来操作的,在C中使用malloc函数,而C++中使用new运算符,但是堆的申请过程比较复杂:当系统收到程序的申请时,会遍历记录空闲内存地址的链表,以求寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,此处应该注意的是有些情况下,新申请的内存块的首地址记录本次分配的内存块大小,这样在delete尤其是 delete[]时就能正确的释放内存空间。

接着是全局数据区(静态区) (static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。另外文字常量区,常量字符串就是放在这里,程序结束后有系统释放。

最后是程序代码区,放着函数体的二进制代码。

内存分布对应关系

程序运行时对应的内存分布(BSS段、数据段、代码段、堆、栈)关系相关推荐

  1. 【解决】Python程序运行时所占内存越来越大

    1.问题描述 最近在用Python(Pyqt5)编写一个可以获取gpu信息(功耗.显存占用.利用率等)并将这些信息保存成csv文件的程序.在程序编写完成后,运行时却发现,随着程序的运行,所占用的内存每 ...

  2. 程序运行时的内存空间分布

    本文转载自http://blog.csdn.net/ljianhui/article/details/21666327 谢谢原文作者的辛勤付出. 我们在写程序时,既有程序的逻辑代码,也有在程序中定义的 ...

  3. Android字体占有内存,android随意创建字体对象引发的应用程序运行时占用内存过大...

    android随意创建字体对象引发的应用程序运行时占用内存过大 在实际开发android项目过程中,由于是初次设置android系统没有的字体,所以用到了一个外部文件FounderFineRoundS ...

  4. Linux系统程序运行时加载动态库路径顺序

    程序运行时加载动态库路径顺序(Linux) 在linux系统中,如果程序需要加载动态库,它会按照一定的顺序(优先级)去查找: 链接时路径(Link-time path)和运行时路径(Run-time ...

  5. error C2057: expected constant expression (C语言中数组的大小可以在程序运行时定义吗? )

    数组的大小可以在程序运行时定义吗? 不.在数组的定义中,数组的大小必须是编译时可知的,不能是在程序运行时才可知的.例如,假设i是一个变量,你就不能用i去定义一个数组的大小: char array[i] ...

  6. Delphi-TScreen表示应用程序运行时屏幕的状态

    TScreen表示应用程序运行时屏幕的状态. 类关系 TObject->TPersistent->TComponent TScreen引进具有表示下列各种情况的属性 什么窗体和数据模块已经 ...

  7. springboot_通过Actuator了解应用程序运行时的内部状况

    Actuator 的端点 Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,可以分为三大类:配置端点.度量端点和其他端点.通过它们了解应用程序运行时的内部状况.有了A ...

  8. 程序运行时的存储组织及管理

    目录 程序运行时的存储组织及管理概述 静态存储分配 临时变量的地址分配 简单栈式动态存储 嵌套过程语言的栈式实现 display 表方法 存取链(静态链)方法 参数传递 程序运行时的存储组织及管理概述 ...

  9. 如何在程序运行时获取 dpdk-16.04 大页使用情况?

    前言 基于 dpdk-16.04 开发的 dpdk 程序需要使用的大页内存总大小可以通过计算得出,但由于 dpdk-16.04 legacy memory 模型只支持物理地址连续的 segment 内 ...

最新文章

  1. Winio驱动在64位windows下无法使用的解决方法
  2. 华为开源加法神经网络 | CVPR20 Oral
  3. R语言使用ggplot2绘制带有边缘直方图的散点图实战
  4. 【Verilog HDL 训练】第 11 天(分频电路)
  5. extjs多选下拉树
  6. 用Canvas画圆环百分比进度条
  7. C++unique函数应用举例
  8. 【APICloud系列|35】小米应用商店版本更新
  9. android -------- Data Binding的使用 ( 四 )ListView
  10. 在线修改域控的IP和机器名
  11. php 字符串大写转小写转大写,字符串大小写批量互相转换 - 在线工具
  12. Dell电脑,Win10系统,插入耳机没反应或者说听筒没声音该怎么解决?
  13. 如何在word中输入带方框的对钩
  14. 关于零基础入门金融风控挑战赛的笔记系列
  15. 用新开放的 notion api 结合 python 爬虫搞个羊毛线报页面
  16. Photon网络中Player中存取数据
  17. 谷歌浏览器开启并行下载
  18. SIP协议详解(中文)-1
  19. 码流、码率、比特率、帧速率、分辨率、 高清的区别
  20. android 图片 生成视频,照片制作成视频的方案有吗?如何视频安卓手机视频编辑器将手机里的照片制作成视频...

热门文章

  1. After Effects(AE) 基本操作(一)
  2. 同济大学软件学院万院长谈择业
  3. RK3399平台开发系列讲解(内核驱动外设篇)6.14、ES8323音频芯片驱动分析
  4. 多个模糊匹配条件下对两个数据集的高效聚合方法(加权最近邻优化)及实例代码
  5. 《软件测试》— 软件测试知识点汇总
  6. 快速了解条码打印机碳带
  7. java浅拷贝和深拷贝的区别_Java 浅拷贝与深拷贝的区别
  8. 为什么偏爱mod 1e9+7呢?using namespace std又是什么?
  9. 微信公众号标题栏设置青协招新报名专栏
  10. windows10音量图标上红叉点入声音设置显示找不到输出设备