一、变量存储
1.内存分区概念
1.c语言在内存中分区 栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 堆区(heap) 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收 。它与数据结构中的堆不同,分配方式类似于链表。 全局区(静态区)(static) 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量、未初始化的静态变量在相邻的另一块区域。当程序结束后,变量由系统释放 。 文字常量区 存放常量字符串。当程序结束后,常量字符串由系统释放 。 程序代码区 存放函数体的二进制代码。 2.动静态分配内存
属性 | 描述 |
---|
静态分配内存 | 在程序编译和链接时就确定好的内存 | 动态分配内存 | 在程序加载、调入、执行的时候分配 / 回收的内存 |
3.内存分区详解
属性 | 描述 |
---|
.text | 也称为代码段(Code),用来存放程序执行代码,同时也可能会包含一些常量(如一些字符串常量等)。该段内存为静态分配,只读(某些架构可能允许修改)。 这块内存是共享的,当有多个相同进程(Process)存在时,共用同一个 text 段 | .data | 也有的地方叫 GVAR (global value),用来存放程序中已经初始化的非零全局变量。静态分配。data又可分为读写(RW)区域和只读(RO)区域。RO 段保存常量所以也被称为 .constdata ;RW 段则是普通非常全局变量,静态变量就在其中 | .bss | 存放程序中为初始化的和零值全局变量。静态分配。在程序开始时通常会被清零 |
堆和栈都属于动态分配内存,两者空间大小都是可变的。
属性 | 描述 |
---|
Stack:栈 | 存放 Automatic Variables ,按内存地址由高到低方向生长,其最大值由编译时确定,速度快,但自由性差,最大空间不大 | Heap:堆 | 自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存 / 虚拟内存上限决定,速度较慢,但自由性大,可用空间大。 |
2.STM32上分区验证
在串口通信所建的工程上修改代码 bsp_usart.h 文件: 添加头文件 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;
}
编译烧录到板子上,打开野火调试助手 按下reset键得到如下结果:
3.在Ubuntu中进行分区验证
1.使用命令创建一个hello.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;
}
编译执行文件 得到结果 从stm32和Ubuntu都可以看出:从高地址到低地址分区情况
二、Clion编辑器的学习与实践
1.Clion的下载与安装
下载传送门. 下载好之后进行安装 以下为安装步骤 等待安装完成。
2.安装clion的相关配置环境
安装MinGW 下载地址:https://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win64/Personal%20Builds/mingw-builds/. 下载后解压完成。 打开clion软件,点击New Project 点击file->setting->Build,Execution, Deployment->Embedded Development选择Tookchains 跟据刚刚下载的mingw64文件的路径将文件调价环境中,最后点击ok
3.安装OpenOCD环境
下载解压出来 按照以下步骤添加路径 点击ok就好了
3.利用clion点亮LED灯
1.打开clion软件,选择新建工程(New Project) 利用其打开STM32CubeMX 改选芯片为STM32F103C8 再进行芯片的相关设置 选择引脚作为输出口(PC13)
生成工程 返回到clion 回到clion软件中找到main.c文件,在main.c文件中找到while(1)语句,在该语句中添加如下代码
while (1)
{
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
HAL_Delay(500);
}
进行编译 烧录 结果展示:
三、总结
Clion 是一个多功能跨平台的 IDE,很有使用价值。在通过Clion进行STM32点亮LED灯的过程中,它一方面调用 STM32CubeMX 直接建立了 HAL 工程,另一方面使用 CMake 编译器完成了代码编译。在这个过程中遇到了很多bug,一遍遍的删除,一遍遍的下载,终于是做了出来。
四、参考
基于ubuntu和stm32(keil5)的C程序的内存分配问题. CLion2021 的详细安装并基于 CLion 实现 stm32F103 点亮 LED.
|