C语言程序里全局变量、局部变量、堆、栈等概念

C语言程序里全局变量、局部变量、堆、栈等概念

  • 一、 存储区域的介绍
    • 1.1 内存分配
    • 1.2 变量的内存分配
    • 1.3 图解
  • 二、 在ubuntu系统中编程并进行验证
    • 2.1 源码
    • 2.2 结果
    • 2.3 结果分析
  • 三、 在Keil中针对stm32系统进行编程进行验证
    • 3.1 详细代码
    • 3.2 结果
    • 3.3结果分析
  • 四、 总结
  • 五、 参考资料

一、 存储区域的介绍

1.1 内存分配

  1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  2. 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  3. 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。

相关介绍来源:C语言程序的内存分配方式

1.2 变量的内存分配

栈区(stack):指那些由编译器在需要的时候分配,不需要时自动清除的变量所在的储存区,如函数执行时,函数的形参以及函数内的局部变量分配在栈区,函数运行结束后,形参和局部变量去栈(自动释放)。栈内存分配运算内置与处理器的指令集中,效率高但是分配的内存空间有限。

堆区(heap):指哪些由程序员手动分配释放的储存区,如果程序员不释放这块内存,内存将一直被占用,直到程序运行结束由系统自动收回,c语言中使用malloc,free申请和释放空间。

静态储存区(static):全局变量和静态变量的储存是放在一块的,其中初始化的全局变量和静态变量在一个区域,这块空间当程序运行结束后由系统释放。

常量储存区(const):常量字符串就是储存在这里的,如“ABC”字符串就储存在常量区,储存在常量区的只读不可写。const修饰的全局变量也储存在常量区,const修饰的局部变量依然在栈上。

程序代码区:存放源程序的二进制代码。

引自:C语言:内存分配

1.3 图解

C语言程序里全局变量、局部变量、堆、栈等概念_第1张图片

图片来源:sw-at ,需要科学上网才可以访问。

补充:

  • Stack: 栈,存放Automatic Variables,按内存地址由高到低方向生长,其最大大小由编译时确定,速度快,但自由性差,最大空间不大。
  • Heap: 堆,自由申请的空间,按内存地址由低到高方向生长,其大小由系统内存/虚拟内存上限决定,速度较慢,但自由性大,可用空间大。
    每个线程都会有自己的栈,但是堆空间是共用的。

引自:C/C++程序内存的各种变量存储区域和各个区域详解

我们以下将按着图解以及补充信息进行相关编程验证。

二、 在ubuntu系统中编程并进行验证

2.1 源码

  • main.c
#include 
#include 

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.2 结果

C语言程序里全局变量、局部变量、堆、栈等概念_第2张图片

2.3 结果分析

C语言程序里全局变量、局部变量、堆、栈等概念_第3张图片

查看相关资料解释说在一个函数中难以查看的地址分配高低,所以这里用了两个函数进行了验证。

可以从上图可以得出栈区内存地址由高到低方向生长堆区内存地址由低到高方向生长。而且整个程序的内存也是从高到低的地址进行分配的。

三、 在Keil中针对stm32系统进行编程进行验证

3.1 详细代码

#include "led.h"
#include "delay.h"
//#include "key.h"
#include "sys.h"
#include "usart.h"
#include 
#include 

int k1 = 1;        //已初始化全局int型变量k1
int k2;            //未初始化全局int型变量k2
static int k3 = 2; //已初始化静态全局int型变量k3
static int k4;     //未初始化静态全局int型变量k4

 int main(void)
 {	
    delay_init();	    	 //延时函数初始化	  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(115200);	 //串口初始化为115200
    LED_Init();			     //LED端口初始化
    //KEY_Init();          //初始化与按键连接的硬件接口
    while(1)
    {
        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("栈区-变量地址\r\n");
        printf("未初始化局部int型变量i1       :0x%p\r\n", &i1);
        printf("未初始化局部int型变量i2       :0x%p\r\n", &i2);
        printf("未初始化局部char型指针变量p   :0x%p\r\n", &p);
        printf("已初始化局部char型数组str     :0x%p\r\n", str);
        //test();

        printf("\n堆区-动态申请地址\r\n");
        printf("已初始化局部int型指针变量p1   :0x%p\r\n", p1);
        printf("已初始化局部int型指针变量p2   :0x%p\r\n", p2);

        printf("\n.bss段地址\r\n");
        printf("未初始化全局int型变量k2       :0x%p\r\n", &k2);
        printf("未初始化静态全局int型变量k4   :0x%p\r\n", &k4);
        printf("未初始化静态局部int型变量m2   :0x%p\r\n", &m2);

        printf("\n.data段地址\r\n");
        printf("已初始化全局int型变量k1       :0x%p\r\n", &k1);
        printf("已初始化静态全局int型变量k3   :0x%p\r\n", &k3);
        printf("已初始化静态局部int型变量m1   :0x%p\r\n", &m1);

        printf("\n常量区地址\r\n");
        printf("已初始化局部char型指针变量var1:0x%p\r\n", var1);
        printf("已初始化局部char型指针变量var2:0x%p\r\n", var2);

        printf("\n代码区地址\r\n");
        printf("程序代码区main函数入口地址    :0x%p\r\n", &main);

        free(p1);
        free(p2);        
    }
}

3.2 结果

C语言程序里全局变量、局部变量、堆、栈等概念_第4张图片

其中Code就是代码占用大小,RO-data是只读常量、RW-data是已初始化的可读可写变量,ZI-data是未初始化的可读可写变量。

C语言程序里全局变量、局部变量、堆、栈等概念_第5张图片

串口显示XCOM中出现乱码,通过分析和检查之后发现是软件版本过低,导致快速输出串口信息时出现乱码现象。但并不影响分析。

3.3结果分析

C语言程序里全局变量、局部变量、堆、栈等概念_第6张图片

四、 总结

通过对C语言程序里全局变量、局部变量、堆、栈等概念的重温以及在不同平台进行编程验证,熟悉掌握了C语言中相关概念,并对整体的内存地址分配由高到低,以及栈区内存地址由高到低方向生长,堆区内存地址由低到高方向生长进行了验证。同时也非常感谢身边老师同学对我的帮助。

五、 参考资料

[1]C语言程序编译后内存地址的分配
[2]CSDN-问答
[3]C/C++程序内存的各种变量存储区域和各个区域详解

你可能感兴趣的:(嵌入式,内存管理,c语言,stm32,ubuntu)