malloc及free使用注意事项
1. 时间不确定性
malloc的实现很简单,它首先会扫描之前由free()所释放的空闲内存块列表,以求找到尺寸大于或等于要求的一块空闲内存。如果这一内存块的尺寸正好与要求相当,就将它返回给调用者,如果是一块较大的内存,那么将对其进行分割,在将一块大小相当的内存返回给调用者的同时,把较小的那块空闲内存块保留在空闲列表中。由于malloc算法的实现特性,malloc不具有确定性(每次调用执行的时间可能会不同),在高实时性要求的场合中需要充分考虑到这一点。比如不建议在中断中调用malloc。
2. 线程不一定安全
按照我个人的理解,在linux或windows系统中,如果使用-pthread进行编译,malloc将成为线程安全的。但在ANSI C中malloc是不可重入的,也就是非线程安全。对于我们这些苦逼的嵌入式程序员来说,这就意味着在keil、IAR等编译器中使用的malloc是非线程安全的,需要自行加锁,以保证线程安全。以在FreeRTOS中内存管理中,方法3(heap_3.c)是对malloc进行了封装,实现线程安全功能。
1 void *pvPortMalloc( size_t xWantedSize ) 2 { 3 void *pvReturn; 4 5 vTaskSuspendAll(); 6 { 7 pvReturn = malloc( xWantedSize ); 8 traceMALLOC( pvReturn, xWantedSize ); 9 } 10 ( void ) xTaskResumeAll(); 11 12 #if( configUSE_MALLOC_FAILED_HOOK == 1 ) 13 { 14 if( pvReturn == NULL ) 15 { 16 extern void vApplicationMallocFailedHook( void ); 17 vApplicationMallocFailedHook(); 18 } 19 } 20 #endif 21 22 return pvReturn; 23 } 24 /*-----------------------------------------------------------*/ 25 26 void vPortFree( void *pv ) 27 { 28 if( pv ) 29 { 30 vTaskSuspendAll(); 31 { 32 free( pv ); 33 traceFREE( pv, 0 ); 34 } 35 ( void ) xTaskResumeAll(); 36 } 37 }
3. malloc不一定成功
当内存碎片化,导致剩余最大块的内存都不足以分配时,此时malloc返回NULL,一般的,我们需要对malloc的返回值进行判断。
1 uint32_t *p; 2 p = malloc(100); 3 if (p ==NULL) 4 { 5 printf("申请内存失败\r\n"); 6 //do something 7 }
4. malloc和free必须成对出现
为了避免内存泄漏,malloc与free必须成对出现,下面代码展示了malloc与free貌似成对出现,其实没有free的现象。
1 void malloc_free_test1(void) 2 { 3 uint32_t p; 4 5 p = malloc(100); 6 if (NULL == p) 7 { 8 return; 9 } 10 11 if (xxx) 12 { 13 free(p); //此处释放后,return离开 14 return; 15 } 16 17 //此时xxx条件不成立,却return离开,没有free,造成内存泄漏 18 return; 19 }
5. 给野指针加一根缰绳
· free一片内存,只是把这一片内存对应的链表删除,内存内容实际还是存在的,
· free之后对应的指针不为NULL,很多地方只会判断指针是否为NULL,而实际内存已释放,可能已经被分配给其他人,导致意想不到的的后果。
· 所以free之后请把指针置为NULL。
1 void malloc_free_test2(void) 2 { 3 uint32_t *p; 4 5 p = malloc(100); 6 if(NULL == p) 7 { 8 return; 9 } 10 11 //错误的做法 12 free(p); 13 if (NULL != p) //此时p不等与NULL,防御编程不起作用 14 { 15 *p = 0x123456;//已经free,此时对p的任何操作都存在危险 16 } 17 18 19 //正确的做法 20 free(p); 21 p = NULL; 22 if (NULL != p)//p已经为NULL,此时防御编程起作用 23 { 24 *p = 0x123456; 25 } 26 }