C函数中的malloc和free

以前没有好好看过这些东西,只是觉得用到的时候用用就可以了,后来研究了一下,发现里面蕴含的东西太多了,研究的不算深,只是觉得想要分享一下,写的不好,有错的地方,欢迎各位大虾拍砖。

如果在main函数中malloc一段地址给某个指针,然后在某个函数中free这个指针,然后置NULL,这个函数返回后,判断该指针是否为NULL,若非NULL,则free指向的内存,然后置NULL。看起来好像很合理,每次操作前都有做是否NULL的判断,但是却出现了问题。

首先写一小段程序作为我试验的例子:(这段程序是出问题后修改之后的最终版,写的不算好,各位大虾见谅)

先是main函数:

int main()
{

    int * pi4Test = (int *)malloc(sizeof(int));
    int i4Count = 0;
    for (; i4Count < 1; i4Count++)
    {
    	*(&(*pi4Test) + i4Count) = i4Count;
    }
    cout<<"pi4Test    addr is:..."<<&pi4Test<<endl;
    cout<<"pi4Test[0] addr is:..."<<pi4Test<<endl;
    cout<<"pi4Test         is:..."<<&pi4Test[0]<<endl;
    cout<<"pi4Test[0]      is:..."<<*pi4Test<<endl;
    funcTest(pi4Test);
    cout<<"funcTest over!!!new line..."<<endl;
    if ( pi4Test == NULL)
        cout<<"yes"<<endl;
    else
    {
        //free(pi4Test);
        cout<<"pi4Test    addr is:..."<<&pi4Test<<endl;
        cout<<"pi4Test[0] addr is:..."<<pi4Test<<endl;
        cout<<"pi4Test[0]      is:..."<<*pi4Test<<endl;
        pi4Test = NULL;
        cout<<"no"<<endl;
    }
    return 0;
}

然后是main函数中的funcTest函数:

void funcTest(int * pi4Test)
{
    cout<<"funcTest start!!!new line..."<<endl;
    cout<<"pi4Test    addr is:..."<<&pi4Test<<endl;
    cout<<"pi4Test[0] addr is:..."<<pi4Test<<endl;
    if ( pi4Test != NULL)
    {
        free(pi4Test);
        cout<<"after free pi4Test..."<<endl;
        cout<<"pi4Test    addr is:..."<<&pi4Test<<endl;
        cout<<"pi4Test[0] addr is:..."<<pi4Test<<endl;
        pi4Test = NULL;
        cout<<"after free & NULL"<<endl;
        cout<<"pi4Test    addr is:..."<<&pi4Test<<endl;
        cout<<"pi4Test[0] addr is:..."<<pi4Test<<endl;
    }
}

这段代码的本意是想测试如果在函数funcTest中把在main函数中malloc的指针free掉,然后在funcTest返回后,做该指针是否NULL的判断,若非NULL则再free一次。这里就是问题所在了,试着运行一下代码可以发现,funcTest返回后,malloc所得的指针真的不是NULL!!!而且因为free了两次而报错了!!!(这里我把main函数里的else后面的free注释了,如果想重现错误需要把注释去掉),公司不能上传图片,所以这里看不到报错的原图:

*** glibc detected *** ./mallocTest: double free or corruption (fasttop): 0x089b0008 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x75ee2)[0xb7574ee2]
./mallocTest[0x804886b]
/lib/i386-linux-gnu/libc.so.6(__libc_start_main+0xf3)[0xb75184d3]
./mallocTest[0x8048651]
======= Memory map: ========
08048000-08049000 r-xp 00000000 08:04 1179985    /home/tiny/homework/test/mallocTest
08049000-0804a000 r--p 00000000 08:04 1179985    /home/tiny/homework/test/mallocTest
0804a000-0804b000 rw-p 00001000 08:04 1179985    /home/tiny/homework/test/mallocTest
089b0000-089d1000 rw-p 00000000 00:00 0          [heap]
b74b2000-b74b4000 rw-p 00000000 00:00 0 
b74b4000-b74d0000 r-xp 00000000 08:05 657589     /lib/i386-linux-gnu/libgcc_s.so.1
b74d0000-b74d1000 r--p 0001b000 08:05 657589     /lib/i386-linux-gnu/libgcc_s.so.1
b74d1000-b74d2000 rw-p 0001c000 08:05 657589     /lib/i386-linux-gnu/libgcc_s.so.1
b74d2000-b74fc000 r-xp 00000000 08:05 673549     /lib/i386-linux-gnu/libm-2.15.so
b74fc000-b74fd000 r--p 00029000 08:05 673549     /lib/i386-linux-gnu/libm-2.15.so
b74fd000-b74fe000 rw-p 0002a000 08:05 673549     /lib/i386-linux-gnu/libm-2.15.so
b74fe000-b74ff000 rw-p 00000000 00:00 0 
b74ff000-b76a3000 r-xp 00000000 08:05 673573     /lib/i386-linux-gnu/libc-2.15.so
b76a3000-b76a5000 r--p 001a4000 08:05 673573     /lib/i386-linux-gnu/libc-2.15.so
b76a5000-b76a6000 rw-p 001a6000 08:05 673573     /lib/i386-linux-gnu/libc-2.15.so
b76a6000-b76a9000 rw-p 00000000 00:00 0 
b76a9000-b7781000 r-xp 00000000 08:05 530411     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7781000-b7782000 ---p 000d8000 08:05 530411     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7782000-b7786000 r--p 000d8000 08:05 530411     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7786000-b7787000 rw-p 000dc000 08:05 530411     /usr/lib/i386-linux-gnu/libstdc++.so.6.0.16
b7787000-b778e000 rw-p 00000000 00:00 0 
b779e000-b77a2000 rw-p 00000000 00:00 0 
b77a2000-b77a3000 r-xp 00000000 00:00 0          [vdso]
b77a3000-b77c3000 r-xp 00000000 08:05 673475     /lib/i386-linux-gnu/ld-2.15.so
b77c3000-b77c4000 r--p 0001f000 08:05 673475     /lib/i386-linux-gnu/ld-2.15.so
b77c4000-b77c5000 rw-p 00020000 08:05 673475     /lib/i386-linux-gnu/ld-2.15.so
bf962000-bf983000 rw-p 00000000 00:00 0          [stack]
已放弃 (核心已转储)


其实上面的代码是因为出现这个问题后我才改写成现在这个样子,我想看看内存的分布是什么样,所以把main函数里的else中的free注释掉了,来看看执行结果,贴不了图:

pi4Test    addr is:...0xbfe16958
pi4Test[0] addr is:...0x8a21008
pi4Test         is:...0x8a21008
pi4Test[0]      is:...0
funcTest start!!!new line...
pi4Test    addr is:...0xbfe16940
pi4Test[0] addr is:...0x8a21008
after free pi4Test...
pi4Test    addr is:...0xbfe16940
pi4Test[0] addr is:...0x8a21008
after free & NULL
pi4Test    addr is:...0xbfe16940
pi4Test[0] addr is:...0
funcTest over!!!new line...
pi4Test    addr is:...0xbfe16958
pi4Test[0] addr is:...0x8a21008
pi4Test[0]      is:...0
no

从图中可以明确看出,funcTest中的指针和main函数的指针是两个指针,但是两个指针指向的地址是一样的,所以在funcTest中free后,是将指针指向的地址释放掉了,(之所以要置NULL,是为了防止野指针,后面会稍微聊到一点点)所以置NULL后,只是将funcTest中的指针置NULL,而main函数中的指针还没有置NULL,于是就变成了野指针,所以在funcTest返回后,判断该指针是否为NULL,结果自然不是NULL,然后再free,就会将之前已经free过的内存再次free一遍,这是不允许的。

因为free的地址会重新交给系统作为未分配的资源,可能已经被分配给别的process了,别人家的东西是不能碰的!当然这里还要补充一点的是,free之后的地址可能还没来得急被分配出去,所以如果你去访问,可能会发现这段地址上还保存着你之前保存的内容,这里有点类似于你把自己的房子卖给了张三,但是张三还没来得及住进去,所以你的钥匙还能进去,而且可能里面还有你之前搬家留下的照片,但是你要知道的是这个房子现在是张三的,你现在也只能看看而已,这个看还要趁着张三没来之前(系统还没有把这片地址分配出去),你也不能给别人看(你不能将free掉的内存地址再用作其他地方,只能看看而已)。这也就是free之后的地址,指向这个地址的指针没有被置NULL,再访问这片地址,可能还是原来的内容的原因。(希望我说的不算啰嗦)

这里涉及的知识点还是蛮多的,虽然在工作中都是大家比较注意的,不允许这样写,不允许那样写,但是了解一点还是比较好的,比如刚才提到的野指针的问题,值传递和地址传递,这里funcTest中的指针到底是值传递还是地址传递?如果是值传递,那么两个指针指向的地址是一样的,如果是地址传递,两个指针的地址却又不一样。是不是很有意思。是不是有人会问怎么解决上面的问题?不能老是free两次吧,其实我也没有什么好的方法,我觉得我能想到的就是把funcTest中的free注释掉,这样就只会有一次free了,这里希望各位大虾介绍点好的方法,不胜感激。

我朋友说如果用const指针不就好了,这个我还没有试,我晚上回去找找自己做的const的总结,明天再来写,好了,这是我今天的分享,各位大虾如果觉得哪写的不好或者写错了,欢迎批评指正

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值