基于linux下unbantu环境验证C语言程序里全局变量、局部变量、堆、栈等概念

序 言

本次实战,我们会用到stm32,所以博主在这里要首先介绍一下基于寄存器与基于固件库的stm32 LED流水灯例子的编程方式的差异

STM32标准外设库之前的版本也称固件函数库或简称固件库,是一个固件函数包,它由程序、数据结构和宏组成,包括了微控制器所有外设的性能特征。
寄存器是中央处理器的组成部分。寄存器是有限存贮量的高速存贮部件,它们可用来暂存指令、数据和地址。
在中央处理器的控制部件中,包含的寄存器有指令寄存器(IR)和程序计数器(PC)。在中央处理器的算术及逻辑部件中,寄存器有累加器(ACC)。

我理解的是其实寄存器就是就是芯片里的某一个部件,我们在开发流水灯时,假如是用的是寄存器的话,我们是通过利用寄存器里的地址的设置从而使我们的IO口实现相应的功能。
而假如使用的是固件库,其实就是调用我们的库函数,库函数是我们的stm32的提供商已经为我们准备好了的各种各样功能的函数,我们只要调用就可以了。
下面是我了解的这两种方式的优缺点:
1、基于寄存器方式的开发特点:
优点:
1.)我们的运行具体参数更直观;
2.)程序运行占用资源少。
缺点:
1)对开发者不友好,我们的开发速度慢,;
2)寄存器繁杂,程序可读性差;
3) 程序较为繁杂,维护复杂。
2、基于固态库开发:
1)我们直接可以调佣函数,外设交流方便;
2)程序简单明了,查错简单;
3)对主控制器STM32上手简单。

一.全局变量、局部变量、堆、栈等概念理解

1)C语言内存分配

C语言在内存中一共分为如下几个区域,分别是:
1 .内存栈区: 存放局部变量名;
2. 内存堆区: 存放new或者malloc出来的对象;
3. 常数区: 存放局部变量或者全局变量的值;
4. 静态区: 用于存放全局变量或者静态变量;
5. 代码区:二进制代码。

详细说明
1 栈
     通常是用于那些在编译期间就能确定存储大小的变量的存储区,用于在函数作用域内创建,在离开作用域后自动销毁的变量的存储区。通常是局部变量,函数参数等的存储区。他的存储空间是连续的,两个紧密挨着定义的局部变量,他们的存储空间也是紧挨着的。栈的大小是有限的,通常Visual C++编译器的默认栈的大小为1MB,所以不要定义int a[1000000]这样的超大数组。
2 堆
     通常是用于那些在编译期间不能确定存储大小的变量的存储区,它的存储空间是不连续的,一般由malloc(或new)函数来分配内存块,并且需要用free(delete)函数释放内存。如果程序员没有释放掉,那么就会出现常说的内存泄漏问题。需要注意的是,两个紧挨着定义的指针变量,所指向的malloc出来的两块内存并不一定的是紧挨着的,所以会产生内存碎片。另外需要注意的一点是,堆的大小几乎不受限制,理论上每个程序最大可达4GB。
3 全局/静态存储区
     和“栈”一样,通常是用于那些在编译期间就能确定存储大小的变量的存储区,但它用于的是在整个程序运行期间都可见的全局变量和静态变量。
4 常量存储区
     和“全局/静态存储区”一样,通常是用于那些在编译期间就能确定存储大小的常量的存储区,并且在程序运行期间,存储区内的常量是全局可见的。这是一块比较特殊的存储去,他们里面存放的是常量,不允许被修改。
5 总结
根据上面的内容,分别将栈和堆、全局/静态存储区和常量存储区进行对比,结果如下。
在这里插入图片描述
在这里插入图片描述
栈区:主要用来存放局部变量, 传递参数, 存放函数的返回地址。.esp 始终指向栈顶, 栈中的数据越多, esp的值越小。
堆区:用于存放动态分配的对象, 当你使用 malloc和new 等进行分配时,所得到的空间就在堆中。动态分配得到的内存区域附带有分配信息, 所以你能够 free和delete它们。
数据区:全局,静态和常量是分配在数据区中的,数据区包括bss(未初始化数据区)和初始化数据区。

注意:
1) 堆向高内存地址生长;
2) 栈向低内存地址生长;
3) 堆和栈相向而生,堆和栈之间有个临界点,称为stkbrk。


楔子
     一个可执行程序文件需要在计算机硬件上运行起来,其实质就是静态的文件被加载到内存中的过程,可执行程序文件只是一个程序的载体。那么执行一个应用后,它在内存中是一个怎样的结构呢,请关注今天的走进科学——《C/C++ 程序内存结构》。
动&静
     一个程序被加载到内存中,这块内存首先就存在两种属性:静态分配内存和动态分配内存。
静态分配内存:是在程序编译和链接时就确定好的内存。
动态分配内存:是在程序加载、调入、执行的时候分配/回收的内存。
Text & Data & Bss
     .text:也称为代码段(Code),用来存放程序执行代码,同时也可能会包含一些常量(如一些字符串常量等)。该段内存为静态分配,只读(某些架构可能允许修改)。
这块内存是共享的,当有多个相同进程(Process)存在时,共用同一个text段。
     .data: 也有的地方叫GVAR(global value),用来存放程序中已经初始化的非零全局变量。静态分配。
data又可分为读写(RW)区域和只读(RO)区域。
-> RO段保存常量所以也被称为.constdata
-> RW段则是普通非常全局变量,静态变量就在其中
     .bss: 存放程序中为初始化的和零值全局变量。静态分配,在程序开始时通常会被清零。
text和data段都在可执行文件中,由系统从可执行文件中加载;而bss段不在可执行文件中,由系统初始化。
这三段内存就组成了我们编写的程序的本体,但是一个程序运行起来,还需要更多的数据和数据间的交互,否则这个程序就是死的,无用的。所以我们还需要为更多的数据和数据交互提供一块内存——堆栈。
堆栈(Heap& Stack)
堆和栈都是动态分配内存,两者空间大小都是可变的。
     Stack: 栈,存放Automatic Variables,按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
     Heap: 堆,自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
每个线程都会有自己的栈,但是堆空间是共用的。

Tips

2) 基于STM32分析栈、堆、全局区、常量区、代码区、RAM、ROM内存分配

在这里插入图片描述
一、栈区(stack)
临时创建的局部变量存放在栈区。
函数调用时,其入口参数存放在栈区。
函数返回时,其返回值存放在栈区。
const定义的局部变量存放在栈区。
2、堆区(heap)
堆区用于存放程序运行中被动态分布的内存段,可增可减。
可以有malloc等函数实现动态分布内存。
有malloc函数分布的内存,必须用free进行内存释放,否则会造成内存泄漏。
3、全局区(静态区)
全局区有.bss段和.data段组成,可读可写
4、.bss段
未初始化的全局变量存放在.bss段。
初始化为0的全局变量和初始化为0的静态变量存放在.bss段。
.bss段不占用可执行文件空间,其内容有操作系统初始化。
5、.data段
已经初始化的全局变量存放在.data段。
静态变量存放在.data段。
.data段占用可执行文件空间,其内容有程序初始化。
const定义的全局变量存放在.rodata段。
6、常量区
字符串存放在常量区。
常量区的内容不可以被修改。
7、代码区
程序执行代码存放在代码区。
字符串常量也有可能存放在代码区。

二.全局变量、局部变量、堆、栈等概念验证理解

1)C语言内存分配验证

  1. 代码段(.txt)
      .txt段存放代码(如函数)与部分整数常量,.txt段的数据可以被执行

  2. 数据段(.data)
      .data用于存放初始化过的全局变量。若全局变量值为0,为了优化编译器会将它放在.bss段中

  3. bss段(.bss)
      .bss段被用来存放那些没有初始化或者初始化为0的全局变量。bss段只占运行时的内存空间而不占文件空间。在程序运行的整个周期内,.bss段的数据一直存在
    我们不把全局变量赋值我们发现这个程序所占的空间比较小,所以没有占文件空间

#include <stdio.h>
 
char global_arr[1024 * 1024];    //存放在.bss段
int main(void)
{
    return 0;
}
hcr@ubuntu:~$ vim bss.c
hcr@ubuntu:~$ gcc bss.c
hcr@ubuntu:~$ ls -l a.out
-rwxrwxr-x 1 hcr hcr 8584 11月 25 20:04 a.out

在这里插入图片描述
我们修改一下程序,把我们的全局变量赋值,我们可以再观察一下

#include <stdio.h>
 
char global_arr[1024 * 1024=9];    //存放在.bss段
int main(void)
{
    return 0;
}
hcr@ubuntu:~$ vim data.c
hcr@ubuntu:~$ gcc data.c
hcr@ubuntu:~$ ls -l a.out
-rwxrwxr-x 1 hcr hcr 1057176 11月 25 20:26 a.out

我们把全局变量赋值后我们发现这个程序所占的空间很大,所以我们可以这个data占了文件空间
在这里插入图片描述

3. 栈
  栈是用于存放临时变量和函数调用的。栈也是一种先进后出的数据结构,函数的递归调用正得益于栈的存在。需注意存在栈的数据只在当前函数和子函数中有效,一旦函数返回数据将会被自动释放。
4. 堆
  堆的使用周期有使用者控制,程序中的内存泄漏多因程序员对堆的管理不当引起,需谨慎
我们利用这两点来验证,1 .内存栈区: 存放局部变量名;2. 内存堆区: 存放new或者malloc出来的对象;我们通过以下程序验证
代码


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
void before()
{
 
}
 
char g_buf[16];
char g_buf2[16];
char g_buf3[16];
char g_buf4[16];
char g_i_buf[]="123";
char g_i_buf2[]="123";
char g_i_buf3[]="123";
 
void after()
{
 
}
 
int main(int argc, char **argv)
{
        char l_buf[16];
        char l_buf2[16];
        char l_buf3[16];
        static char s_buf[16];
        static char s_buf2[16];
        static char s_buf3[16];
        char *p_buf;
        char *p_buf2;
        char *p_buf3;
 
        p_buf = (char *)malloc(sizeof(char) * 16);
        p_buf2 = (char *)malloc(sizeof(char) * 16);
        p_buf3 = (char *)malloc(sizeof(char) * 16);
 
        printf("g_buf: 0x%x\n", g_buf);
        printf("g_buf2: 0x%x\n", g_buf2);
        printf("g_buf3: 0x%x\n", g_buf3);
        printf("g_buf4: 0x%x\n", g_buf4);
 
        printf("g_i_buf: 0x%x\n", g_i_buf);
        printf("g_i_buf2: 0x%x\n", g_i_buf2);
        printf("g_i_buf3: 0x%x\n", g_i_buf3);
 
        printf("l_buf: 0x%x\n", l_buf);
        printf("l_buf2: 0x%x\n", l_buf2);
        printf("l_buf3: 0x%x\n", l_buf3);
 
        printf("s_buf: 0x%x\n", s_buf);
        printf("s_buf2: 0x%x\n", s_buf2);
        printf("s_buf3: 0x%x\n", s_buf3);
 
        printf("p_buf: 0x%x\n", p_buf);
        printf("p_buf2: 0x%x\n", p_buf2);
        printf("p_buf3: 0x%x\n", p_buf3);
 
        printf("before: 0x%x\n", before);
        printf("after: 0x%x\n", after);
        printf("main: 0x%x\n", main);
 
        if (argc > 1)
        {
                strcpy(l_buf, argv[1]);
        }
        return 0;

hcr@ubuntu:~$ vim b.c
hcr@ubuntu:~$ gcc -o b.o b.c
hcr@ubuntu:~$ ./b.o
g_buf: 0x6010a0
g_buf2: 0x6010d0
g_buf3: 0x6010b0
g_buf4: 0x6010c0
g_i_buf: 0x601050
g_i_buf2: 0x601054
g_i_buf3: 0x601058
l_buf: 0xb52af550
l_buf2: 0xb52af560
l_buf3: 0xb52af570
s_buf: 0x601070
s_buf2: 0x601080
s_buf3: 0x601090
p_buf: 0x14f4010
p_buf2: 0x14f4030
p_buf3: 0x14f4050
before: 0x400626
after: 0x40062d
main: 0x400634
hcr@ubuntu:~$ 

在这里插入图片描述

2)Keil中针对stm32系统进行编程,调试变量,进行验证; 通过串口输出信息到上位机,进行验证。

这部分博主会在下一个博客中一起说
另一篇博客链接: link.

三. 结语

我们这个对于全局变量、局部变量、堆、栈等概念的理解对于我们学习stm32很有意义,所以博主在这里希望各位也要好好学习,争取掌握这个知识点。博主在这次的实践中发现了几个坑,一是,我们的串口助手可能对于我们的实验结果有影响,博主今天试了三个串口工具,其中有两个似乎有些小问题,但是上面那个串口工具对我们没影响,这博主也有点疑问。二是,博主在使用串口助手的时候还遇到过串口被占用的问题,搜了很多博主,但是还是没有解决,只能重启电脑,很麻烦。还有就是希望各位有问题可以联系博主,博主很乐意和各位一起学习。请您关注我个人的微信公众号,微信搜索h生活剪影很期待您的关注,我们一起进步。在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值