C语言开发过程中容易碰到的内存相关错误
在内存受限的嵌入式开发中,经常会碰到各种各样的内存错误,若是在没有内存保护的系统来说,出现内存相关的错误造成的结果可能是系统崩溃或造成重要数据的丢失。在开发嵌入式系统中关注稳定性特别重要。本篇文章主要收集和整理了内存相关错误,用以备忘。
内存相关错误:
1. 栈溢出
eCos各个线程的堆栈由线程初始化时确认,如果函数内调用了过大的局部变量,或者函数调用栈太深导致栈溢出。
2. 内存对齐访问
C语言中涉及到强制指针取结构体访问的时候由于结构体可能字节未对齐造成指令调用访问出错。
3. 大小端出错
大小端问题容易出现在网络编程相关里,一般网络字节序为大端。在嵌入式系统中,系统可能是大端也可能是小端。
需要注意这一点。
4. 返回临时变量
函数内部定义或者初始化的局部变量地址不可返回。
5. 分配/释放不配对
某些库或者第三方代码可能会使用自有的malloc以及free函数,在添加新代码时,需要注意统一的分配与释放操作。
6. 指针运算含糊不清
在操作指针运算时,需要留意可能容易混淆的地方,例如:int *p=**; p+1等于(size_t)p + 1,实际上 p + n
相当于(size_t)p + n * sizeof(*p),指针相关的加减操作与该指针指向的变量结构高度相关。
7. 引用未初始化的变量
当引用局部临时变量时,容易忘记初始化该变量的内存空间。直接使用会有出错的风险,特别是在循环利用buffer进行数据处理的时候。
8. 访问已经释放的指针(NULL)
访问一个已经被释放(且置NULL)的指针通常会导致程序崩溃或者其它未知的错误,不同系统对访问0,具有不同的含义。嵌入式系统中可能对0有特殊的含义。
9. 访问已经free掉的指针(野指针)
当调用free函数时,该指针并不会被显式的置为0,而是被内存管理模块标记该内存空间为可用状态。仍然可用继续访问该内存空间,但是此时已经埋下了程序崩溃的隐患。
10. 内存泄漏
最常见的内存问题,申请了内存空间,由于逻辑问题或者其它问题造成该处内存丢失管理(即无人释放),对于内存有限的嵌入式设备来说,一定量的内存泄漏会引起系统运行不稳定。
11. 内存越界读写
当内存出现越界读时,可能会导致数据错误,例如:如果读取的空间超过了界限,无法保证字符串以0结尾,当该数据传递给其它函数时,可能会引起不可预料的后果。如果出现了越界写,
则会出现随机的踩内存问题,这种问题往往难以定位。所以在开发时,需要时刻保证内存的边界是否可控。
12. 内存复制时存在覆盖
当源与目的直接进行数据拷贝时,可能由于地址的重叠造成未知的错误。
内存相关调试工具:
-
动态内存调试
-
- valgrind 工具
- 命令valgrind --leak-check=full -v ./target
- gcc 工具
- 命令gcc -fsanitize=address -g -o target target.c
- Windows
-
使用vs studio 在开始包含:
#define _CRTDBG_MAP_ALLOC #include <crtdbg.h>
然后使用:
_CrtDumpMemoryLeaks();//即可打印函数泄漏信息。
-
- valgrind 工具
-
静态代码分析
-
- clang 静态分析
- 命令 scan-build --use-analyzer=/usr/local/bin/clang -enable-checker alpha.security gcc target.c
- cppcheck静态分析
- http://cppcheck.sourceforge.net/ 主要能检测一些风格问题。
- clang 静态分析
以上每一点都可以展开来讲,其中还有很多细节或者其它需要注意的问题都需要记录,待续。