std::string的一个隐蔽错误
先看第一个程序,for循环生成5个Key,然后把它们对应的字符串地址放入vector
中,最后依次输出字符串。
#include <iostream>
#include <string>
#include <vector>
std::string GenKey(int i) {
char buf[32];
snprintf(buf, sizeof(buf), "key%06d", i);
return std::string(buf);
}
int main() {
std::vector<const char*> vec;
for (int i=0; i < 5; i++) {
std::string key = GenKey(i);
vec.push_back(key.data());
}
for (auto p : vec) {
std::cout << p << std::endl;
}
return 0;
}
从代码逻辑来说,应该会依次输出5个不同的Key才对,其实不然。
运行这段代码,你会发现输出是:
key000004
key000004
key000004
key000004
key000004
竟然输出了5个一模一样的Key!
我们再来一下第二个程序,它与第一个程序的唯一区别是,新增加了一个vector
存储string
类型的Key。
#include <iostream>
#include <string>
#include <vector>
std::string GenKey(int i) {
char buf[32];
snprintf(buf, sizeof(buf), "key%06d", i);
return std::string(buf);
}
int main() {
std::vector<std::string> strs;
std::vector<const char*> vec;
for (int i=0; i < 5; i++) {
std::string key = GenKey(i);
strs.push_back(key);
vec.push_back(key.data());
}
for (auto p : vec) {
std::cout << p << std::endl;
}
return 0;
}
运行第二个程序,输出:
key000000
key000001
key000002
key000003
key000004
这次输出和预期的一样了。
那第一个程序出现了什么错误呢?
这里有一个容易被忽视的错误:我们在for循环中定义的std::string key
是局部变量
, 每次循环完成后key
都会被销毁,那key
对应的字符串数组当然也被释放了!循环结束后vec
中存储的全是野指针
,但是因为这些Key分配的堆空间都在相同的位置,所以vec
中他们指向了相同的地址,翻译为字符串,即是最后一次循环赋值的key000004
!
在第二个程序中,虽然key
仍然是局部变量,但因为将他们存到了vector
中,所以循环结束后,分配的堆空间也不会被释放(这里需要注意的一点是std::string
类型是写时复制
的,即std::string
类型间相互赋值,但只要没有发生修改操作,它们底层的字符串都指向了相同的地址)。