Chinaunix首页 | 论坛 | 博客
  • 博客访问: 548288
  • 博文数量: 493
  • 博客积分: 2891
  • 博客等级: 少校
  • 技术积分: 4960
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-17 17:11
文章分类

全部博文(493)

文章存档

2010年(493)

分类:

2010-05-12 18:29:35

1 现象:问题描述
在一次版本开发单元测试中发现,程序出现内存地址访问非法,造成程序的core,
2 关键过程:根本原因分析
查看代码,程序以动态方式载入动态链接库中的函数,然后以函数指针的方式调用动态链接库中的函数,并把返回值放在一个string类中,然后释放了这个动态链接库。释放后,输入hostname的内容。
main()
{
     //载入动态链接库中的函数
hDll = LoadLibraray(…..);
pFun =  GetModule(hDll, "GetHostName");
//调用动态链接库中的函数
string hostname = (*pFun)("192.168.0.1");
……
……
//释放动态链接库
FreeLibrary(hDll);
……
cout << hostname << endl;
}
string GetHostName (string ip)
{
    static string hostname;
    ……
    ……
    return hostname;
}
初看这段代码觉得没任何问题,但是单步跟踪发现当cout<下面这个程序的意图就是让第二个string通过第一个string构造,然后打印出其存放数据的内存地址,然后分别修改str1和str2的内容,再查一下其存放内存的地址。程序的输出是这样的(VC6.0的结果):
main()
{
       string str1 = "hello world";
       string str2 = str1;//发生拷贝构造
       printf ("Sharing the memory:\n");
       printf ("\tstr1's address: %x\n", str1.c_str() );
       printf ("\tstr2's address: %x\n", str2.c_str() );
     str1[1]='q';
       str2[1]='w';
       printf ("After Copy-On-Write:\n");
       printf ("\tstr1's address: %x\n", str1.c_str() );
       printf ("\tstr2's address: %x\n", str2.c_str() );
       return 0;
}
结果如下,注意开始2个对象地址一致,后面修改后str1的地址变化了,str2的地址确没变:
Sharing the memory:
        str1's address: 4316e1
        str2's address: 4316e1
After Copy-On-Write:
        str1's address: 431691
        str2's address: 4316e1
Press any key to continue
(上面这段代码当string2被string1拷贝构造出来时,它们2个共享一块内存地址,如上内存地址一致,当string1的内容被修改后,string1发现string2和它共享内存,所以它会新分配一块内存存放它修改的值(如上),而当修改string2的值时候,由于已经没有和它共享内存的对象了,所以它的内存地址没变化);
再来看我们的程序代码,根据GetHostName函数的定义,我们知道该函数是"值返回"的,所以,函数返回时,一定会调用拷贝构造函数,又根据string类的copy-on-write机制,在主程序中变量hostname是和函数内部的那个静态string变量共享内存(这块内存区是在动态链接库的地址空间的)。而我们假设在整个主程序中都没有对hostname的值进行修改过。那么在当主程序释放了动态链接库后,那个共享的内存区也随之释放。所以,以后对hostname的访问,必然造成内存地址访问非法,造成程序崩溃。即使你在以后没有使用到hostname这个变量,那么在主程序退出时也会发生内存访问异常,因为程序退出时,hostname会析构,在析构时就会发生内存访问异常。所以就出现了上述的问题。
3 结论:解决方案及效果
解决方法如下,修改调用GetHostName的方法,避免产生拷贝构造;
string hostname = (*pFun)("192.168.0.1").cstr();
程序处理正常。
阅读(358) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~