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();
程序处理正常。
阅读(384) | 评论(0) | 转发(0) |