问题分析
malloc内存分配失败无非是下面两种情况:
- 可用内存不足
- 数组指针越界
但是这两种情况的形成原因有多种,此处以STM32F407VGT6举例,192+4KB SRAM,1MB FLASH。
内存不足
第一种是造成内存不足的原因是由于初始化的堆长度不够引起的,一般STM32的例程默认的栈长度为0x0400,堆长为0x0200,也就是说分别只有1024字节和512字节,堆和栈具体用来干嘛的大家可以参考下面这两篇文章,简单来说栈就是用来存储局部变量的,局部变量会在退出函数时由系统自动释放,而堆是给用户malloc用的,由用户主动申请,主动释放,当堆长度不足时,自然申请不到内存。
解决方法:
打开.s文件
找到Heap_Size
将其调大,可根据自己的实际需求增大堆长度,也可以直接调到最大,不同的STM32芯片最大的堆长不同,可以慢慢增大直到编译报错为止。
编译器在编译时会把堆、栈等空间分配好,具体占用多少内存可根据build output的提示
其中各个含义请参考下面这篇文章
第二种内存不足的情况就是用户没用主动free却不停得申请内存,比如在一个循环或中断中申请了内存,用完之后没有及时释放,第二次又去申请,不断申请过程中使得堆慢慢被填满,最终导致无法申请更多内存,malloc失败。
解决方法:
这种原因的解决方法就是去查找到底有没有存在没用free,重复申请的代码存在。一般这种情况主要表现为前几个循环没问题,后面循环进入HardFault_IRQ。该问题排查比较费时,可以每次写一个malloc的时候对应写一个free,不一定要调用这个释放的函数,但是可以提醒自己这个指针是自己申请的,需要自己主动free。另外需要注意的一点是,free完后需要把该指针赋值为NULL,不然可能会变成野指针,导致一些未知的错误。
数组越界
数组越界而导致malloc失败是由于实际使用的数组长度大于申请的数组长度,然后会在下一次malloc的时候进入HardFault。造成数组越界原因比较多,这里列举两个。
有一位博主遇到的情况是申请时指针为uint_8*,后又强制转换为unsigned int*,因为unsigned int*为4个字节的指针,因此需要4倍长度的数组才行,指针强制类型转换时需要注意该类型的字节个数,比如uint8_t为一个字节,该类型指针++时,地址+1个字节,int为4个字节,该类型的指针++时,地址+4。具体可参考其博文。
这是我最近遇到的一种情况,准确的说算是没弄清strcpy和memcpy之间的区别。
图中使用了strcpy函数,而strcpy是复制一个数组从第一个字节到一个不是'/0'的字节为止,而我传进去的data指针实际上并不是一个字符串,只是普通的uint8*数组,因此strcpy一直复制该数组直到出现'/0'为止,长度远超len,导致数组越界,下一次malloc分配内存的时候由于指针指向未知地址而直接进入HardFault。
解决方法
将strcpy()函数改为memcpy()函数,memcpy()可指定复制数组的长度,传入指针为void*,也就是说只要知道你要复制的数组长度(个数*类型长度,比如3个float类型的字节,长度为3*4=12字节),就可以用这个函数。
区分malloc分配失败成因
当malloc由于内存不足而导致分配失败时,malloc会返回NULL,如果程序中做好malloc返回值的判断可有效避免这种原因造成的问题;当malloc由于数组越界而导致分配失败时,往往是没有返回NULL,运行到malloc语句后直接进入HardFault。
这种判断方法也不是绝对的,特殊情况还是需要特殊处理。