C/C++程序内存的各种变量存储区域及验证

一、C/C++程序内存的各种变量存储区域

在C\C++中,通常可以把内存理解为4个分区:栈、堆、全局/静态存储区和常量存储区。

(一)栈

栈是用于存放临时变量和函数调用的。栈也是一种先进后出的数据结构,函数的递归调用正得益于栈的存在。需注意存在栈的数据只在当前函数和子函数中有效,一旦函数返回数据将会被自动释放。栈通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的,栈的大小是有限的。

(二)堆

通常是用于那些在编译期间不能确定存储大小的变量的存储区,它的存储空间是不连续的,一般由malloc(或new)函数来分配内存块,并且需要用free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨着定义的指针变量,所指向的malloc出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达4GB。

(三)全局/静态存储区

和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。

(四)常量存储区

和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量的存储区,并且在程序运行期间,存储区内的常量是全局可见的。这是一块比较特殊的存储去,他们里面存放的是常量,不允许被修改。

二、在ubuntu系统中编程,输出信息进行验证

1、打开ubantu,新建一个.c文件,把下列程序粘贴进去,然后使用gcc命令转换为可执行文件,然后执行文件

#include <stdio.h>
#include <stdlib.h>
int k1 = 1;
int k2;
static int k3 = 2;
static int k4;
int main( )
{   static int m1=2, m2;
    int i = 1;
    char *p;
    char str[10] = "hello";
    char *var1 = "123456";
    char *var2 = "abcdef";
    int *p1=malloc(4);
    int *p2=malloc(4);
    free(p1);
    free(p2);
    printf("栈区-变量地址\n");
    printf("                i:%p\n", &i);
    printf("                p:%p\n", &p);
    printf("              str:%p\n", str);
    printf("\n堆区-动态申请地址\n");
    printf("                   %p\n", p1);
    printf("                   %p\n", p2);
    printf("\n.bss段\n");
    printf("全局外部无初值 k2:%p\n", &k2);
    printf("静态外部无初值 k4:%p\n", &k4);
    printf("静态内部无初值 m2:%p\n", &m2);
    printf("\n.data段\n");
    printf("全局外部有初值 k1:%p\n", &k1);
    printf("静态外部有初值 k3:%p\n", &k3);
    printf("静态内部有初值 m1:%p\n", &m1);
    printf("\n常量区\n");
    printf("文字常量地址     :%p\n",var1);
    printf("文字常量地址     :%p\n",var2);
    printf("\n代码区\n");
    printf("程序区地址       :%p\n",&main);
    return 0;
}

2、验证结果:
在这里插入图片描述
3、结论
通过程序运行结果来看,

变量类型地址增长
地址向下增长
地址向上增长
静态变量地址向下增长
全局常量地址向上增长
函数地址向上增长

三、Keil验证

(一)在Keil中针对stm32系统进行编程,调试变量,进行验证

1、打开上个博客时打开的工程,把main.c函数改为以下内容

#include "stm32f10x.h"
#include "bsp_usart.h"

char global1[16];
char global2[16];
char global3[16];
	
int main(void)
{	
  char part1[16];
  char part2[16];
  char part3[16];

  USART_Config();

  printf("part1: 0x%p\n", part1);
  printf("part2: 0x%p\n", part2);
  printf("part3: 0x%p\n", part3);
	 
  printf("global1: 0x%p\n", global1);
  printf("global2: 0x%p\n", global2);
  printf("global3: 0x%p\n", global3);
  while(1)
	{	
		
	}	
}

2、编译检查
在这里插入图片描述
此时可以看出内存中存放了Code、RO-data、RW-data、ZI-data四个代码段,其中Code就是代码占用大小,RO-data是只读常量、RW-data是已初始化的可读可写变量,ZI-data是未初始化的可读可写变量。
3、再把main.c改为以下程序

#include "stm32f10x.h"
#include "bsp_usart.h"
#include <stdlib.h>

int main(void)
{	
  static char st1[16];
  static char st2[16];
  static char st3[16];
  char *p1;
  char *p2;
  char *p3;

 
  USART_Config();

  printf("st1: 0x%p\n", st1);
  printf("st2: 0x%p\n", st2);
  printf("st3: 0x%p\n", st3);
	 
  p1 = (char *)malloc(sizeof(char) * 16);
  p2 = (char *)malloc(sizeof(char) * 16);
  p3 = (char *)malloc(sizeof(char) * 16);
	
  printf("p1: 0x%p\n", p1);
  printf("p2: 0x%p\n", p2);
  printf("p3: 0x%p\n", p3);
  while(1)
	{	
		
	}	
}


4、编译检查
在这里插入图片描述

MDK 下分为:Code、RO-data、RW-data、ZI-data 这几个段;

Code是存储程序代码的;

​RO-data是存储const常量和指令;

​RW-data是存储初始化值不为0的全局变量;

​ZI-data是存储未初始化的全局变量或初始化值为0的全局变量;

Flash=Code + RO Data + RW Data;(实际存储数据)

RAM= RW-data+ZI-data;

上文代码来源:STM32的USART串口通讯

(二)通过串口输出信息到上位机,进行验证。

1、以下是上文第一个main.c函数,在调试和烧录到开发板后,通过多功能调试助手看到的程序运行结果
在这里插入图片描述

part1、part2、part3为栈中的局部变量,地址向下增长。
global1、global2、global3为静态区中的全局变量,地址向上增长。

2、第二个main.c程序输出结果在这里插入图片描述

st1、st2、st3都是静态变量,p1、p2、p3是堆中的指针,它们的地址都是向上增长。

五、stm32的堆、栈、全局变量的分配地址

点击Keil5的魔术棒,打开Target选项,在红色方框处我们可以看到stm32给ROM和RAM分配的地址
在这里插入图片描述

由图片可以看出:
1、ROM的地址分配是从0x8000000开始,大小为0x80000;代码区和常量区的内容编译后存储在ROM中。
2、RAM的地址分配是从0x20000000开始,大小为0x10000。而栈、堆、全局区(.bss段、.data段)都是存放在RAM中。

参考资料:
1、 C/C++程序内存的各种变量存储区域和各个区域详解.
2、 C语言中程序内存的各种变量存储区域的理解.
3、【IoT】STM32 内存分配详解

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值