千万注意,不要 hack std::string
2007-02-25 14:49
316 查看
前段时间被一个bug折磨了两个星期,最后发现竟然是如此一个陷阱——我为了减少内存用量并且减少一次内存拷贝,直接通过string.data()修改了string的内部表示。这与其说是一个陷阱,不如说是我自己给自己造了一个陷阱然后把自己给掉进去了。发病机制可以用如下代码简单的勾画出来:
using namespace std;
int main(int argc, char* argv[])
{
string str1 = "abcde";
string str2 = str1;
strcpy(const_cast<char*>(str2.data()), "1234");
cout << "str1=" << str1 << endl
<< "str2=" << str2 << endl;
return 0;
}
在windows+msvc 中的输出是:
str1=abcde
str2=1234
在linux+gcc中的输出是:
str1=1234
str2=1234
在boost::serialization中,对string的load也是采用这样的hack方式,目的也是为了减少内存用量并且减少一次内存拷贝。使用boost::serialization的同志们需要注意,不要掉进这个陷阱!
我们可以看出,在msvc中,string拷贝时是真拷贝,而在gcc中,必定是用了引用计数+copy on write。str1和str2内部引用的是同一块内存。因为string.data()和string.c_str()都是const成员,所以不会有copy,只会增加引用计数。所以导致修改str2实际上也修改了str1。
c++标准甚至允许把const string的成员放入带写保护的内存区域中,或者把string的成员实际上存储在不相邻的内存块中,而仅在调用 string.data() 或 string.c_str() 时将数据拷贝到一块临时内存中然后返回,这块临时内存将在下一次调用string的一个非const成员函数时释放,如果目标平台真这样实现,往 string.data()中写数据就会导致更加微妙的错误。
using namespace std;
int main(int argc, char* argv[])
{
string str1 = "abcde";
string str2 = str1;
strcpy(const_cast<char*>(str2.data()), "1234");
cout << "str1=" << str1 << endl
<< "str2=" << str2 << endl;
return 0;
}
在windows+msvc 中的输出是:
str1=abcde
str2=1234
在linux+gcc中的输出是:
str1=1234
str2=1234
在boost::serialization中,对string的load也是采用这样的hack方式,目的也是为了减少内存用量并且减少一次内存拷贝。使用boost::serialization的同志们需要注意,不要掉进这个陷阱!
我们可以看出,在msvc中,string拷贝时是真拷贝,而在gcc中,必定是用了引用计数+copy on write。str1和str2内部引用的是同一块内存。因为string.data()和string.c_str()都是const成员,所以不会有copy,只会增加引用计数。所以导致修改str2实际上也修改了str1。
c++标准甚至允许把const string的成员放入带写保护的内存区域中,或者把string的成员实际上存储在不相邻的内存块中,而仅在调用 string.data() 或 string.c_str() 时将数据拷贝到一块临时内存中然后返回,这块临时内存将在下一次调用string的一个非const成员函数时释放,如果目标平台真这样实现,往 string.data()中写数据就会导致更加微妙的错误。
相关文章推荐
- 千万注意,不要 hack std::string
- PPPoE-在配置PPPoE的用户名和密码的时候一定要准确严格,不要在后面加空格,千万记住,特别是复制张贴的时候,同时还有注意大小写的问题!!!
- 说明 安卓开发的时候需要进行注意,路径名称千万不要用中文否则会出现一系列的问题........
- 八种菜隔夜万万吃不得!尤其是第三种,千万要注意!
- 注意啦!新站千万不可用的网站推广方法
- String注意点
- 大家千万不要带新人。否则你会被搞得很惨。
- 用户习惯分析:千万不要写长邮件!
- PHP防SQL注入不要再用addslashes和mysql_real_escape_string了
- 林家翘先生提醒青年学者:千万不要Garbage in,garbage out
- 能率热水器存在严重质量隐患!千万不要购买!买了之后每年都需要自费维修!
- 事关每个人的健康---千万不要死于无知
- 【总结】大规模数据测试,数据准备时需要注意的问题(【保护已有数据】【大规模数据影响普通测试】【不要着急删除数据】)
- 女孩!再爱一个人,也千万不要为他做的4件事!
- 【廖雪峰 python教程 课后题 切片】利用切片操作,实现一个trim()函数,去除字符串首尾的空格,注意不要调用str的strip()方法:
- C++请不要问我string s=”a”+”b”分配了几次内存
- 千万不要使用6位密码!密码破解速度全面披露
- 千万不要使用6位密码!密码破解速度全面披露
- GetPrivateProfileString和WritePrivateProfileString头文件引入的注意点
- 零碎的小知识点 ----------C# ToString()函数注意事项