一、C程序的内存分配
对应C程序而言,不同类型的变量因为功能和用法不同,所以被分配在不同的区域里面
存放区域 | 存放内容 | 特点 |
---|
栈区 | 局部变量值、返回值、参数 | 由编译器自动分配释放,具有 “后进先出” 的内存结构 | 堆区 | 程序运行中动态分配内存的数据 | 堆在内存中位于 BSS 区和栈区之间, | 静态区 | 全局变量 / 静态变量 | 初始化的全局变量和静态变量在一块区域(RW),未初始化的全局变量和静态变量在相邻的另一块区域(ZI) | 常量区 | 全局变量 / 局部变量的值 | 常量字符串也是放在这里的,退出程序由系统自动释放 | 代码 | 二进制代码 | 存放函数体的二进制代码 |
由图可见,内存存放的顺序为:栈区-> 堆区 -> 全局区 -> 代码区
二、分别通过Ubuntu和STM32程序输出验证
1.Ubuntu下输出地址验证
#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;
}
- 保存文件后,输入
gcc test.c -o test 命令编译文件,然后输入./test 命令进行运行 运行后输出以下结果:
由输出结果可以看出,Ubuntu下栈区和堆区的地址都是逐步增大的
2.STM32下输出地址验证
- 建立工程,在工程文件里面进行修改
打开 bsp_usart.h 文件,并且将两个头文件stdio.h 和stdlib.h include进去
然后打开 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);
}
最后修改main.c文件
#include "stm32f10x.h"
#include "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;
}
- 编译运行程序,并烧录到芯片
- 打开串口调试助手,输出显示
由串口输出可以看到,STM32下栈区和堆区的变量地址都是从低到高增大的
三、总结
本次验证实验主要是帮助了解C程序的内存分配存放问题,通过Ubuntu和STM32的不同环境下进行验证,对堆、栈、全局变量、局部变量的地址进行了初步的了解。通过此次实验,有帮助于了解C程序的变量存放问题。
四、参考资料
基于ubuntu,树莓派和stm32的C程序的内存分配问题 C程序在 Ubuntu 和 STM32 中内存分区【全局变量、局部变量、堆、栈】
|