现在的位置: 首页 > 综合 > 正文

[转载]一个指针参数引发的血案

2013年10月16日 ⁄ 综合 ⁄ 共 1273字 ⁄ 字号 评论关闭

原文链接:http://blog.odichy.org/tag/malloc

今天在阅览室看《算法导论》,顺便用C写了链表相关的算法,结果在初始化链表的时候出现了问题,出现了Segmentation fault。先来看看错误的代码,省略一些定义了:

1
2
3
4
5
6
7
8
9
10
11
12
13
int main(void)
{
list *li;
init(li);
insert(li, 5);
//main函数以下省略,insert为插入一个元素到链表
}
 
static void init(list *li)
{
li = (list *)malloc(sizeof(list));
//init函数以下省略
}

这个是想在一个方法里面初始化链表,因为malloc是在堆上分配内存,所以其分配的空间不会随函数的结束而从栈上消失,所以自然而然的想把这的值 直接给li,以便在接下来的程序中引用。但是编译执行时显示Segmentation fault,不用想肯定是main中的li仍然指向NULL。开始比较疑惑,因为一般来说传一个指针进去,是会改变其指向的内容的,就好象是面向对象中的 按引用调用一样,这里为什么会出错呢?如果把init函数的代码段移回main中,程序运行没什么问题的,那么肯定问题出在malloc分配的空间上。

分析一下这个程序执行的过程,首先声明一个list *类型的参数,也就是一个可以指向list类型的指针,因为还没赋值,所以其空间内存的数据可能是任何东西。然后就是调用init方法,注意此时所传递的 是main.li这个变量内存的值,因此对于init.li,其值是main.li的值,它只是main.li的一个拷贝,但是它的存储空间是分配在 init函数活动栈上的,因此此时将malloc分配的堆内存地址赋给init.li时只是改变了init活动栈上的li的值,并未真正去改变 main.li这个main的活动栈上的li的值。因此当init函数完成调用,返回后,其活动栈将销毁,也就是将我们刚刚malloc的那块堆内存地址 给销毁了,此时就造成了内存泄漏。而我们再通过main.li调用时,其实它还是没有分配过存储空间的,所以产生了Segmentation fault。

分析明白产生错误的原因后,自然就好改了,要么将malloc分配的指针返回,要么将参数改成一个指针的指针(即main.li的地址),按第二个方法更改程序如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
int main(void)
{
list *li;
init(&li);
insert(li, 5);
//main函数以下省略,insert为插入一个元素到链表
}
 
static void init(list **li)
{
*li = (list *)malloc(sizeof(list));
//init函数以下省略
}

由此可见,其实在指针做参数时,还是存在一定的陷阱的,如果不清楚程度堆栈的建立过程,很容易出现内存泄漏,而且在看不懂GAS汇编的情况下,要想通过二进制机器码分析出问题所在,还是有一定困难的。在C中的函数存在很多陷阱,用的时候一定要时刻小心才是。

抱歉!评论已关闭.