被C++摧残的经历

转自:https://www.zhihu.com/question/310052411/answer/620132502

前几天帮人看了个出错的C++程序。我这里只给出错的部分好了:

string s1 = string("string1");
string s2 = string("string2");
const char* p1 = s1.substr(1).data();    // substr(1)的意思是取字符串从下标1到结尾的部分
const char* p2 = s2.substr(1).data();
cout << p1 << p2 << endl;

短短的六行程序,大家猜一下程序的输出。我想一般人的第一反应应该是tring1tring2吧。毕竟程序就这六行,能出什么错呢?

然而不!当时程序的输出是tring2tring2!而我为什么说当时呢?因为换了台机器这个问题又没有了,输出又变成了正常的tring1tring2!

面对这六行代码,我思考,调试了很久。最终发现了问题所在:

s1.substr(1)创建了一个临时对象存储s1的子串。然后我们用data()函数获取了这个子串的内存指针。到目前为止,一切正常。

然而!到了下一行!这个存储s1子串的字符串对象,被析构了!因为编译器发现这是个临时对象,于是没有等到块结束再析构,而是直接立即析构了它!然后这个临时对象的内存被释放了,而我们此时又建立了一个新的对象s2.substr(1),猜猜它被建立到哪了?没错,就是s1.substr(1)死的地方= =于是我们又调用data(),得到了和p1一样的一个指针,而此时内存的数据已经被修改为s2的子串了。

按照我的这个猜想,我修改了一下程序:

string s1 = string("string1");
string s2 = string("string2");
ss1 = s1.substr(1);
ss2 = s2.substr(1);
const char* p1 = ss1.data();    // substr(1)的意思是取字符串从下标1到结尾的部分
const char* p2 = ss2.data();
cout << p1 << p2 << endl;

果然问题没有再出现。然后我带着这个结论去查了一下资料,发现一篇相同内容的文章:

https://blog.csdn.net/stpeace/article/details/46461167​

程序不太一样,但是主要内容是一样的:临时对象会被当即析构,而不会等到块结束。

但是这还没有到达这个问题最恐怖的地方。这个问题最恐怖的地方在于,就在我编辑回答的这个时间,这段错误的程序在当时出错的机器上用相同的编译器编译,问题复现不出来了!

如果在某个大型程序中出现这么一段程序,会花费编程人员多少个下午?

猜你喜欢

转载自blog.csdn.net/F_hawk189/article/details/88429571
C++