• 栈(stack): 由编译器自动分配和释放

  • 堆(heap) : 一般由程序员分配和释放

    int a = 0; // 全局初始化区                    
    char *p1;  // 全局未初始化区                    
    main() {   
    		
    		/************ 定义的这些局部变量所占用的空间都来源于**栈空间** , 当这个**函数运行完**结束之后 , 这些局部变量占用的**栈内存空间**就会**被释放**掉 *************/                  
    		int b;            // 栈                       
    		char s[] = "abc"; // 栈                       
    		char *p2;         // 栈                       
    		char *p3 = "123456"; // 123456\\0在常量区,p3在栈上                       
    		static int c =0;     // 全局(静态)初始化区
    		
    		/*********** 通过C语言内存函数所得到的内存空间来源于**堆空间** *************/                       
    		p1 = (char *)malloc(10);  // 堆                       
    		p2 = (char *)malloc(20);  // 堆                   
    }
    
  • 裸机动态内存配置和使用

    • Libraries\CMSIS\Device\ST\STM32F1xx\Source\Templates\arm\startup_stm32f103xe.s

      // **栈空间**的设置
      Stack_Size      EQU     0x00000400
      
                      AREA    STACK, NOINIT, READWRITE, ALIGN=3
      Stack_Mem       SPACE   Stack_Size
      __initial_sp
      
      // **堆空间**的设置
      Heap_Size       EQU     0x00000200
      
                      AREA    HEAP, NOINIT, READWRITE, ALIGN=3
      __heap_base
      Heap_Mem        SPACE   Heap_Size
      __heap_limit
      
    • target\libraries\gd32_lib\CMSIS\GD\GD32F4xx\Source\ARM\startup_gd32f4xx.s 注意:使用的是GCC下面的.s文件,不是ARM下的.s文件

      // **栈空间**的设置
      Stack_Size      EQU     0x00000400
      
                      AREA    STACK, NOINIT, READWRITE, ALIGN=3
      Stack_Mem       SPACE   Stack_Size
      __initial_sp
      
      // **堆空间**的设置
      Heap_Size       EQU     0x00000400
      
                      AREA    HEAP, NOINIT, READWRITE, ALIGN=3
      __heap_base
      Heap_Mem        SPACE   Heap_Size
      __heap_limit
      
    • 在裸机中,设置完堆栈空间之后,就可以通过C语言标准函数 malloc 去分配相应的动态内存,使用完之后,通过C语言标准函数 free 去释放相应的动态内存

      • 例如:

        char *p;
        p = (char*)malloc(10);  // 分配动态内存
        ......
        free(p);                // 释放动态内存
        
  • RT-Thread 系统动态内存的配置和使用

    • 在RT-Thread中,我们需要使用动态内存的时候,先需要有一个API函数,帮我们配置好动态内存,也就是从芯片中,取到一个动态内存,设置为动态内存区。
      • drivers\board.c

        /**
         * This function will initial STM32 board.
         */
        void rt_hw_board_init(void)
        {
            HAL_Init();
            SystemClock_Config();
        #ifdef RT_USING_HEAP
            rt_system_heap_init((void *)HEAP_BEGIN, (void *)HEAP_END);
        #endif
        		......
        }
        
      • target\board\board.c

        void bsp_early_initialize(void)
        {
            ......
            
            // 初始化**系统动态内存堆**(定义**可用RAM**区域)
            rt_system_heap_init((void*)SYSTEM_FREE_MEM_BEGIN, (void*)SYSTEM_FREE_MEM_END);
            
            ......
        }
        
      • 在RT-Thread系统中,设置完堆栈空间之后,就可以通过RT-Thread系统中特定的函数 rt_malloc 去分配相应的动态内存,使用完之后,通过 rt_free 去释放相应的动态内存

        • 例如:

          char *p;
          p = (char*)rt_malloc(10);  // 分配**动态内存**
          ......
          rt_free(p);                // 释放**动态内存**
          
      • rt_malloc 分配动态内存空间的时候,会有两个结果,一个是成功,一个是失败

        • 比如,要分配一个固定大小的内存空间,如果系统当时所剩的内存空间不足以分配的话,rt_malloc 函数就会返回 RT_NULL
        • 也就是只有我们要申请的内存空间 < 系统所拥有的空闲空间 的时候 才能分配成功,否则的话会分配失败
  • RT-Thread 系统动态内存配置的函数

    • rt-thread\src\mem.c

      /**
       * @ingroup SystemInit  初始化系统堆内存 (堆[heap]: 一般由程序员分配和释放)
       *
       * 该函数用于**初始化**系统**堆内存管理模块**,将**指定**的**内存区域**划分为**堆内存**,并**建立**初始**内存块**结构
       * 堆内存管理采用链表结构,支持动态内存分配与释放,同时通过信号量保证线程安全
       *
       * 用于从SRAM分出(动态内存空间)
       *
       * @param begin_addr 堆内存起始地址(需要按RT_ALIGN_SIZE对齐)
       * @param end_addr   堆内存结束地址(需要按RT_ALIGN_SIZE向下对齐)
       */
      void rt_system_heap_init(void *begin_addr, void *end_addr)
      {
          ......
      }
      
    • rtos\rt-thread\src\mem.c

      void rt_system_heap_init(void *begin_addr, void *end_addr)
      {
      		......
      }
      
    • 先给系统配置好动态内存空间占用的区域,然后再去使用它

    • 如果整个芯片没有外部的RAM的话,那就把片内的RAM拿来当系统的动态内存空间使用

    • 如果芯片的外部空间有外扩RAM,还可以把芯片的外扩RAM拿来当系统的动态内存空间使用

    • 只需要换掉,这段空间的 begin_addr 和 end_addr 就行

  • 动态内存使用过程中要注意的问题

    • 内存复位

      • 每次申请到新的内存块之后,建议对所申请到的内存块进行清零操作
      • 因为整个动态内存空间是大家公用的,所以从公用的这段内存空间中申请到的特定内存中所存的数据可能是原来的应用遗留下来的,初始值不一定是0
    • 避免内存泄漏

      • 内存泄漏(Memory Leak)是指程序中己动态分配的堆内存,由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果
      • 在使用动态内存时需要注意:rt_malloc 需要和 rt_free 配套使用
      char *p;
      p = (char*)rt_malloc(10);  // 申请长度为 10 Bytes 的动态内存空间
      
      // 判断是否申请成功
      if(p != RT_NULL) {
      		rt_memset(p,0,10);     // 对动态内存进行清0操作(**内存复位**)
          ......
      }
      rt_free(p);                // 释放动态内存(**避免内存泄漏**)
      
  • 其他动态内存相关API

    • 在已分配内存块的基础上重新分配内存块的大小(增加或缩小),在进行重新分配内存块时,原来的内存块数据保持不变(缩小的情况下, 后面的数据被自动截断)

      • 比如之前分配了50个字节的动态内存空间,可是在后面的使用中发现,50个字节的内存空间不够用,想要加大到100个字节

        void *rt_realloc(void *rmem, rt_size_t newsize)     // 只需要1个形参:newsize
        
    • 从内存堆中分配连续内存地址的多个动态内存块

      • 分配 count 个 size 字节大小的连续内存地址的动态内存块

        void *rt_calloc(rt_size_t count, rt_size_t size)    // 需要两个形参