探究C程序编译得到的可执行文件加载到内存运行时不同变量分配的存储位置,通过在Ubuntu 18.04系统和STM32系统上编译,验证C程序编译后内存地址分配是否和理论一致。
目录
一、程序的内存分配
1.内存分配介绍
- 内存栈区: 存放局部变量名;
- 内存堆区: 存放new或者malloc出来的对象;
- 常数区: 存放局部变量或者全局变量的值;
- 静态区: 用于存放全局变量或者静态变量;
- 代码区:存放二进制代码。
2.内存分配图解
二、ubuntu系统中编程,输出信息进行验证
1.详细代码
```c
#include <stdio.h>
#include <stdlib.h>
int k1 = 1; //已初始化全局int型变量k1
int k2; //未初始化全局int型变量k2
static int k3 = 2; //已初始化静态全局int型变量k3
static int k4; //未初始化静态全局int型变量k4
int test()
{
int j1;
int j2;
printf("未初始化局部int型变量j1 :%p\n", &j1);
printf("未初始化局部int型变量j2 :%p\n", &j2);
return 0;
}
int main()
{
static int m1 = 2; //已初始化静态局部int型变量m1
static int m2; //未初始化静态局部int型变量m2
int i1; //未初始化局部int型变量i1
int i2; //未初始化局部int型变量i2
char *p; //未初始化局部char型指针变量p
char str[10] = "hello"; //已初始化局部char型数组str
char *var1 = "123456"; //已初始化局部char型指针变量var1
char *var2 = "abcdef"; //已初始化局部char型指针变量var2
int *p1 = malloc(4); //已初始化局部int型指针变量p1
int *p2 = malloc(4); //已初始化局部int型指针变量p2
printf("栈区-变量地址\n");
printf("未初始化局部int型变量i :%p\n", &i1);
printf("未初始化局部int型变量i2 :%p\n", &i2);
printf("未初始化局部char型指针变量p :%p\n", &p);
printf("已初始化局部char型数组str :%p\n", str);
test();
printf("\n堆区-动态申请地址\n");
printf("已初始化局部int型指针变量p1 :%p\n", p1);
printf("已初始化局部int型指针变量p2 :%p\n", p2);
printf("\n.bss段地址\n");
printf("未初始化全局int型变量 k2 :%p\n", &k2);
printf("未初始化静态全局int型变量k4 :%p\n", &k4);
printf("未初始化静态局部int型变量m2 :%p\n", &m2);
printf("\n.data段地址\n");
printf("已初始化全局int型变量k1 :%p\n", &k1);
printf("已初始化静态全局int型变量k3 :%p\n", &k3);
printf("已初始化静态局部int型变量m1 :%p\n", &m1);
printf("\n常量区地址\n");
printf("已初始化局部char型指针变量var1:%p\n", var1);
printf("已初始化局部char型指针变量var2:%p\n", var2);
printf("\n代码区地址\n");
printf("程序代码区main函数入口地址 :%p\n", &main);
free(p1);
free(p2);
return 0;
}
2.实验结果
3.实验结果分析
栈区地址并未按理论的从高到低变化,查阅资料知道同一函数内的局部变量的地址分配的大小不能说明问题,因为编译器分配空间可以随意,得看多个函数调用时分配局部变量的地址大小。
变量i和j1分别于源于函数main和test,因此分别看变量i和j1的地址可以看出栈区地址分配是从高地址往低地址生长,符合理论。
三、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)
{
}
}
分别在stm32中定义了全局变量和局部变量,并把它们的地址返回给上位机。
2.烧录程序
生成hex文件
3.实验结果
打开串口之后还需在STM32开发板上按下RESET按钮
结果如下:
part1、part2、part3为栈中的局部变量,地址逐渐减小。
global1、global2、global3为静态区中的全局变量,地址逐渐增加。
4.实验代码
再对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)
{
}
}
定义静态变量和指针,并返回它们的地址给上位机。
5.实验结果
结果如下:
st1、st2、st3都是静态变量,他们的地址依次增加。
p1、p2、p3是堆中的指针,他们的地址也是依次增加。
总结
通过在ubuntu和STM32上验证C程序编译后在内存的地址分配,了解与实践了不同变量及代码在内存中地址分配的不同,对C的内存分配有了更多理解。