malloc()函数调用失败


        近段时间在调试一个程序的时候遇到了malloc()函数调用失败的问题,该程序的内存消耗不大,系统内存也比较充足,在调试后终于知道原来是内存溢出破坏了内存分配表。相关代码抽象如下:

	typedef struct _COMPONENT_
	{
			char name[100 + 1];
			char value[100 + 1];
	}component_t;

	int some_function(const char *name, const char *value)
	{
			component_t* c = NULL;
			char *desc = NULL;

			c = (component_t *)malloc(sizeof(component_t));
			if(!c)
			{
					/* print error message! */
			}

			strncpy(c.name, name, strlen(name));
			strncpy(c.value, value, strlen(value));

			...

			desc = (char *)malloc(sizeof(char) * 16); /* malloc failed here.*/
			if(!desc)
			{
					/* print error message! */
			}

			...

	}

        刚开始执行程序是没有问题的,换了一些参数后就弹出一个崩溃的对话框。根据以往的经验,我认为是一个与内存相关的问题。于是我使用出现问题的参数进行调试,不久之后找到了出错的位置,原来程序的崩溃是上述模型中的第二个malloc()函数调用失败导致的。考虑到该程序对内存占用较小,当时系统的内存也还有很多空余,于是我知道了不是因为内存不够而导致分配内存失败,而应该是某处的操作破坏了系统内存分配表造成malloc()函数不能正常工作。接下来我使用打桩的方法来查看从何处开始就会造成malloc()调用失败。打桩的代码如下:

	/* For debugging. 2012-2-6 */
	{
			char *pTemp = NULL;
			pTemp = (char *)malloc(sizeof(char) * 16);
			if(!pTemp)
			{
					/* print error message! */
			}
	}

        其中第一句注释是统一加在调试代码之前的,可以在完成调试后通过搜索找出所有添加的调试代码,一并删除。代码开始和结束的大括号是为了将这一段代码隔离,避免影响周围其他代码。将这段代码插入到程序中的几个关键位置后再调试,终于发现在some_function()的第二个strncpy()调用后,malloc()函数调用就会失败。再次调试,停在第二个调用strncpy()的地方,发现value的长度已经超过了100,而c.value只能容纳100个字符和一个结束符。value中多余的数据就会被写到c.value之后的内存中,Windows系统的内存分配大概要在已分配内存之后记录相关信息,这段信息被覆盖后当然不能分配内存成功。将component_t的定义中value的范围增大后,问题就暂时解决了。

        其实这在C语言中是一个相当常见的bug,但是在测试时又很难发现。为了避免这样的问题,可以根据情况采用下面的方法:

       (1). 如果对字段的长度有限定,就在赋值前检查赋值的长度。;例如,在上面的例子中先检查传入的value的长度,检查合法后再赋值给c.value.
       (2). 如果没有限定字段的长度,则需要使用能够动态增长的字符串。在接下来的博客里,我会实现一个能够动态增长的字符串,其原理就是在已有的空间不够存储字符串时,另外分配一块更大的内存,将以前的内容复制到新的内存里,再将要加入的字符串连接进去,最后释放之前的内存。

        以前在Open VMS系统里调试一个程序时也遇到过这样的问题,并且同样的代码在Windows里运行良好。当时我花了很久的时间去查找这个问题,最后也是利用上面这种打桩的方法解决的,这也是为什么在遇到上面的问题的时,我首先就想到了这种方法。由此可以看出,不同操作系统的内存管理方法不同,即使在一个系统里没有发现问题,在另一个系统里也可能出现问题。这就需要我们在设计和编码的时候多加注意,添加必要的检查语句,防止内存溢出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值