目录
  • 一、内存分区概念介绍
    • 1.1、C/C++编译程序的内存占用
    • 1.2、栈和堆、全局/静态存储区和常量存储区的对比
    • 1.3、图片说明
  •  二、C语言编程论证
    • 1.1、Ubuntu测试代码实现
    •  1.2、STM32验证代码实现
    • 1.3、keil下stm32存储观察
  • 三、总结
    • 四、参考资料

      一、内存分区概念介绍

      1.1、C/C++编译程序的内存占用

      1、栈区(stack)
      由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
      2、堆区(heap)
      一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。它与数据结构中的堆不同,分配方式类似于链表。
      3、全局区(静态区)(static)
      全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。当程序结束后,变量由系统释放 。
      4、文字常量区
      存放常量字符串。当程序结束后,常量字符串由系统释放 。
      5、程序代码区
      存放函数体的二进制代码。

      1、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。

      2、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
      3、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。

      1.2、栈和堆、全局/静态存储区和常量存储区的对比

      C程序中Ubuntu、stm32的内存分配问题

      1、栈区: 主要用来存放局部变量, 传递参数, 存放函数的返回地址。.esp 始终指向栈顶, 栈中的数据越多, esp的值越小。

      2、堆区: 用于存放动态分配的对象, 当你使用 malloc和new 等进行分配时,所得到的空间就在堆中。动态分配得到的内存区域附带有分配信息, 所以你能够 free和delete它们。

      3、数据区: 全局,静态和常量是分配在数据区中的,数据区包括bss(未初始化数据区)和初始化数据区。  

      注意: 堆向高内存地址生长; 栈向低内存地址生长; 堆和栈相向而生,堆和栈之间有个临界点,称为stkbrk。

      1.3、图片说明

      C程序中Ubuntu、stm32的内存分配问题 

      补充:

      Stack: 栈,存放Automatic Variables,按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。

      Heap: 堆,自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
      每个线程都会有自己的栈,但是堆空间是共用的。

      参考文献:https://www.icode9.com/content-1-772915.html 

       二、C语言编程论证

      1.1、Ubuntu测试代码实现

      1、main.c:

      #include <stdio.h>
      #include <stdlib.h>
      //定义全局变量
      int init_global_a = 1;
      int uninit_global_a;
      static int inits_global_b = 2;
      static int uninits_global_b;
      void output(int a)
      {
      	printf("hello");
      	printf("%d",a);
      	printf("\n");
      }
       
      int main( )
      {   
      	//定义局部变量
      	int a=2;
      	static int inits_local_c=2, uninits_local_c;
          int init_local_d = 1;
          output(a);
          char *p;
          char str[10] = "lyy";
          //定义常量字符串
          char *var1 = "1234567890";
          char *var2 = "qwertyuiop";
          //动态分配
          int *p1=malloc(4);
          int *p2=malloc(4);
          //释放
          free(p1);
          free(p2);
          printf("栈区-变量地址\n");
          printf("                a:%p\n", &a);
          printf("                init_local_d:%p\n", &init_local_d);
          printf("                p:%p\n", &p);
          printf("              str:%p\n", str);
          printf("\n堆区-动态申请地址\n");
          printf("                   %p\n", p1);
          printf("                   %p\n", p2);
          printf("\n全局区-全局变量和静态变量\n");
          printf("\n.bss段\n");
          printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
          printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
          printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
          printf("\n.data段\n");
          printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
          printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
          printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
          printf("\n文字常量区\n");
          printf("文字常量地址     :%p\n",var1);
          printf("文字常量地址     :%p\n",var2);
          printf("\n代码区\n");
          printf("程序区地址       :%p\n",&main);
          printf("函数地址         :%p\n",&output);
          return 0;
      }

      2、使用命令创建一个 main.c 文件:

      gedit main.c

      3、复制粘贴上述代码

      4、编译执行

      gcc main.c -o main

      ./main

      5、结果展示

      C程序中Ubuntu、stm32的内存分配问题 

      分析说明:可以从上图可以得出栈区内存地址由高到低方向生长,堆区内存地址由低到高方向生长。而且整个程序的内存也是从高到低的地址进行分配的。 

       1.2、STM32验证代码实现

      打开之前做过的串口通讯实验代码,在其中进行修改,后面我也会给出先关串口链接

      1、代码更改

      修改bsp_usart.h :

      添加头文件:

      #include <stdio.h>
      #include <stdlib.h>

      C程序中Ubuntu、stm32的内存分配问题

       修改bsp_usart.c :

      重写 fputc 函数:

      int fputc(int ch, FILE *f)
      {
      	USART_SendData(DEBUG_USARTx, (uint8_t)ch);
      	
      	while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
      	
      	return (ch);
      }

      C程序中Ubuntu、stm32的内存分配问题

      main.c : 

      #include "stm32f10x.h"
      #include "bsp_usart.h"  //添加 bsp_usart.h 头文件
       
      int init_global_a = 1;
      int uninit_global_a;
      static int inits_global_b = 2;
      static int uninits_global_b;
       
      void output(int a)
      {
      	printf("hello");
      	printf("%d",a);
      	printf("\n");
      }
       
      int main(void)
      {	
      	//定义局部变量
      	int a=2;
      	static int inits_local_c=2, uninits_local_c;
      	int init_local_d = 1;
      	char *p;
      	char str[10] = "lyy";
      	//定义常量字符串
      	char *var1 = "1234567890";
      	char *var2 = "qwertyuiop";
      	//动态分配
      	int *p1=malloc(4);
      	int *p2=malloc(4);
      	USART_Config();//串口初始化
      	output(a);
      	//释放
      	free(p1);
      	free(p2);
      	printf("栈区-变量地址\n");
      	printf("                a:%p\n", &a);
      	printf("                init_local_d:%p\n", &init_local_d);
      	printf("                p:%p\n", &p);
      	printf("              str:%p\n", str);
      	printf("\n堆区-动态申请地址\n");
      	printf("                   %p\n", p1);
      	printf("                   %p\n", p2);
      	printf("\n全局区-全局变量和静态变量\n");
      	printf("\n.bss段\n");
      	printf("全局外部无初值 uninit_global_a:%p\n", &uninit_global_a);
      	printf("静态外部无初值 uninits_global_b:%p\n", &uninits_global_b);
      	printf("静态内部无初值 uninits_local_c:%p\n", &uninits_local_c);
      	printf("\n.data段\n");
      	printf("全局外部有初值 init_global_a:%p\n", &init_global_a);
      	printf("静态外部有初值 inits_global_b:%p\n", &inits_global_b);
      	printf("静态内部有初值 inits_local_c:%p\n", &inits_local_c);
      	printf("\n文字常量区\n");
      	printf("文字常量地址     :%p\n",var1);
      	printf("文字常量地址     :%p\n",var2);
      	printf("\n代码区\n");
      	printf("程序区地址       :%p\n",&main);
      	printf("函数地址         :%p\n",&output);
      	return 0;
      }

      2、编译输出

      3、烧录

      打开串口调试助手,打开串口后,按一下 RESET 键,显示结果:

      C程序中Ubuntu、stm32的内存分配问题

       4、分析说明

         与Ubuntu一样,stm32的栈区的地址值是从上到下减小的,堆区则是从上到下增长的。从每个区来看,地址值是从上到下逐步减小的,即栈区的地址是高地址,代码区的地址是处于低地址。

      1.3、keil下stm32存储观察

      stm32数据的存储位置

      1、RAM(随机存取存储器)
      存储的内容可通过指令随机读写访问。RAM中的存储的数据在掉电是会丢失,因而只能在开机运行时存储数据。其中RAM又可以分为两种,一种是Dynamic RAM(DRAM动态随机存储器),另一种是Static RAM(SRAM,静态随机存储器)。栈、堆、全局区(.bss段、.data段)都是存放在RAM中。
      2、ROM(只读存储器)
      只能从里面读出数据而不能任意写入数据。ROM与RAM相比,具有读写速度慢的缺点。但由于其具有掉电后数据可保持不变的优点,因此常用也存放一次性写入的程序和数据,比如主版的BIOS程序的芯片就是ROM存储器。代码区和常量区的内容是不允许被修改的,所以存放于ROM中。

      查看:

      C程序中Ubuntu、stm32的内存分配问题

      分析说明:

          从图片中可以看出ROM的地址分配是从0x8000000开始,整个大小为0x40000,这个部分用于存放代码区和文字常量区。RAM的地址分配是从0x20000000开始,其大小是0xC000,这个区域用来存放栈、堆、全局区(.bss段、.data段)。与代码结果显示进行对比,也可以看出对应得部分得地址与设置的是相对应的。 

      三、总结

         通过对C语言程序里全局变量、局部变量、堆、栈等概念的重温以及在不同平台进行编程验证,熟悉掌握了C语言中相关概念,并对整体的内存地址分配由高到低,以及栈区内存地址由高到低方向生长,堆区内存地址由低到高方向生长进行了验证。经过本次实验,主要是对C程序的内存分配有进一步的认识,知道一个C程序内存应该包括哪些部分。其中,主要是程序段、数据段、堆栈三个部分。不同系统下面,区域内的地址值变化是不相同。总的来说,是对内存的分配有了比较新的认识。

      四、参考资料

      https://blog.csdn.net/qq_43279579/article/details/110308101

      https://blog.csdn.net/ssj925319/article/details/110727925?spm=1001.2014.3001.5501 

      USART串口通信:

      链接: https://pan.baidu.com/s/1YspOK2I3Ddm5XntKXKRnXA

      提取码: emev 

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。