嵌入式系统中动态内存申请应注意的问题
在阅读《嵌入式系统高级C语言编程》时,读到第5章指针与数组中有关动态存储区的申请时,文中描述的例子非常值得记录下来,以警示自己。
架设在一个图形用户界面中需要保存LCD屏幕上的一个矩形区域的背景图形,为此我们需要申请一块存储区用来存放该背景图形的数据。假设矩形区域的宽度为x,高度为y,那么在这个矩形区域一共有x*y个像素点,如果我们采用的是四级灰度LCD,那么每个像素需要2比特来表示,因此一共需要x*y*2/8个字节的存储空间来保存这个背景图形的数据。则代码如下:
char *buffer;
buffer = (char *)malloc(x8y*2/8);
上面的代码乍看起来没有任何问题,而且如果凑巧,这段代码确实会表现一切正常。但是,如果x*y的结果不能被4整除怎么办?比如x=10,y=15,那么我们会申请多少个字节的缓冲区呢?C语言中整数除法的结果是只计商,而直接舍弃余数(实际编译器可能会将上面的表达式x*y*2/8优化成x*y>>2),也就是说结果是37个字节。如果我们通过malloc()函数分配37个字节的缓冲区,那么这个缓冲区理论上来说只能存放37*4=148个像素的数据,这样最后两个像素的数据就会保存到buffer缓冲区之外。这对于malloc()动态分配的内存而言是非常危险的。首先,如果写溢出的数据有可能会冲掉其它malloc()函数分配空间中的数据,这会造成其它数据在没有任何警告的情况下被修改,更致命的是这块数据可能属于一段距离非常远的代码,当程序的执行流在经过很长时间后访问这些被修改的数据时,程序才可能出错。程序员在调试这个bug时想回溯到最初修改这些数据的代码是非常困难的。其次,就算没有冲掉其它的数据,写溢出依然可能破坏malloc()函数用于分配块管理的头部数据,这些数据对于free()函数正确释放这块空间有着至关重要的作用,造成的后果就是当程序试图释放这块存储区时将无法正确执行,这个错误的后果就是这块内存永远(至少在重启系统前)丢失了。这就是所谓的“内存泄露”。修正后的代码为:
char *buffer;
buffer = (char *)malloc((x*y*2+7)/8);
动态申请内存是嵌入式系统中常用的操作,在利用malloc()函数申请内存时非常值得注意的点:
1.strlen()函数获得的字符串的长度不包括最后的“ ",在为字符串申请空间时必须用strlen()+1
2.用malloc()申请空间,涉及到除法运算时,应该将被除数加上除数-1,然后再除以除数,这样可以避免由于除不尽导致实际申请的内存空间少于需要的空间的问题。