一、基本概念
全局常量是在程序运行期间不可更改的常量,它们的值在程序的整个执行过程中保持不变。
全局变量是在程序的任何地方都可以访问的变量,在整个程序执行期间都会存在,并且可以在不同的函数之间共享数据。
局部变量是在一个特定的代码块或函数中定义的变量,它们的作用域只在其所在的代码块或函数中,当代码块或函数执行完毕后,局部变量会被销毁。
静态变量是在程序执行过程中一直存在的变量,它的生命周期从变量的声明到程序结束。静态变量在内存中的位置固定不变,而且只会被初始化一次。
堆是用于动态内存分配的一种数据结构,它是在程序运行时动态分配内存空间的一块区域。堆中的内存可以通过new
关键字或者类似的分配函数进行手动管理,同时在合适的时机需要手动释放内存。
栈是一种数据结构,它用于存储函数调用的参数、局部变量等数据。栈是自动分配和释放内存的,每当函数被调用时,其局部变量和参数都会被压入栈中,当函数执行完毕后,这些数据会被自动释放。栈的大小一般是由系统预先设定的,不建议在栈中存储过大的数据。
二、ubuntu下的地址分配
1.编写代码
首先在Ubuntu系统上编写c语言代码,示例如下:
#include <stdio.h>
#include <stdlib.h>
int global_var; // 全局变量
const int global_const = 10; // 全局常量
int main() {
int local_var; // 局部变量
static int static_var; // 静态变量
int *heap_var = (int *)malloc(sizeof(int)); // 动态分配堆内存
printf("地址比较结果:\n");
printf("全局变量地址:%p\n", &global_var);
printf("全局常量地址:%p\n", &global_const);
printf("局部变量地址:%p\n", &local_var);
printf("静态变量地址:%p\n", &static_var);
printf("堆变量地址:%p\n", heap_var);
free(heap_var); // 释放堆内存
return 0;
}
2.输出结果
由以上结果可以看出,在Ubuntu中,除.bss段,其余所有区的内存分配都是从上向下不断增长的。
三、stm32下的地址分配
KEILL MDK调试环境下的内存分配具体说明
IROM1为默认分配的ROM区域,由0x8000000开始,大小为0x80000。
IRAM1为默认分配的RAM区域,由0x20000000开始,大小为0x10000。
类似的代码,但是需要初始化串口,按照参考文献2中的新建模板工程进行建立就好了。
#include "sys.h"
#include "usart.h"
#include "delay.h"
#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("the result is:");
printf("\n");
}
int main(void)
{
u16 t; u16 len; u16 times=0;
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
uart_init(72,115200); //串口初始化为115200
while(1)
{
int a=2;
static int inits_local_c=2, uninits_local_c;
int init_local_d = 1;
output(a);
char *p;
char str[20] = "wuwuwuwuwuwu";
//定义常量字符串
char *var1 = "123456";
char *var2 = "abcdef";
//动态分配
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);
if(USART_RX_STA&0x8000)//串口发送部分
{
len=USART_RX_STA&0x3FFF;//得到此次接收到的数据长度
for(t=0;t<len;t++)
{
USART1->DR=USART_RX_BUF[t];
while((USART1->SR&0X40)==0);//等待发送结束
}
USART_RX_STA=0;
}else
{
times++;
delay_ms(100);
}
}
return 0;
}
将代码导入keil中进行调试,发现报错。
此时需要进行调试设置,因为声明不能出现在可执行状态之后,C语言关于变量的定义只能放在函数的开头,放在执行语句的前面定义,这是C89的标准。后来的C99标准就已经改变了,无论定义在之前还是之后都是可以的。
所以我们打开魔术棒,再点c/c++,打钩上C99 mode 即可。
随后,在“Target”选项中勾选 “Use MicroLIB”
这样就可以调试成功了,我们将代码烧录到芯片中。
然后打开串口调试助手
可以发现,在STM32中,栈区的地址是自下而上增加的,与Linux系统恰好相反;堆区地址则是从上向下增长。
四、总结
-
堆(Heap):
- 在Ubuntu中,堆是由malloc、calloc或realloc等动态内存分配函数分配的内存区域。堆是由操作系统动态分配的,大小可根据需要进行增加或减少。
- 在STM32中,堆的实现通常需要通过重定向malloc和free函数,并在启动文件中指定堆的起始地址和大小。
-
栈(Stack):
- 在Ubuntu中,栈用于存储函数的局部变量和函数调用的相关信息。栈的分配和释放是由编译器和操作系统完成的,通常具有固定的大小,存储顺序为后进先出。
- 在STM32中,栈用于保存函数的局部变量、函数参数和返回地址等信息。栈的大小通常在编译时由链接器和启动文件定义,存储顺序也是后进先出。
-
全局变量(Global Variables):
- 在Ubuntu和STM32中,全局变量是定义在函数外部或在文件作用域中的变量。全局变量在程序启动时就被分配到静态存储区,包括存储于数据段或BSS段,并且在整个程序生命周期内都是有效的。
-
局部变量(Local Variables):
- 在Ubuntu中,局部变量是定义在函数内部的变量。它们通常存储在栈上,并在函数执行期间使用,并在函数返回时释放。
- 在STM32中,局部变量也存储在栈上。由于STM32通常具有较小的内存资源,函数执行完成后栈上的局部变量会自动释放。
总体上,Ubuntu和STM32在内存管理方面有一些差异。Ubuntu操作系统拥有较为完善的内存管理系统,可以通过动态内存分配来灵活管理堆的大小。而在STM32嵌入式系统中,由于资源限制,内存管理方面需要更加谨慎,并通过静态定义和栈的使用来实现变量的分配和释放。