在过去一年半的时间里,我参与了将一个较旧的Win32-MFC项目移植到Linux上,并最终遇到了一些我不太了解的东西。起初我以为这可能是由于引入了C ++ 11移动语义,但我不确定这是不是问题。在g ++ 4.8.4下使用-std = c ++ 11标记以下代码:
#include <map>
#include <string>
#include <iostream>
#include <iomanip>
#include <cstring>
const char* foo[] = { "biz", "baz", "bar", "foo", "yin" };
const int sizes[] = { 3, 3, 3, 3, 3 };
typedef std::map <std::string, int> simpleMap_t;
typedef std::pair<std::string, int> simplePair_t;
int main()
{
simpleMap_t map;
std::string key;
for (int i = 0; i<5; i++)
{
key.resize(sizes[i]);
memcpy(const_cast<char *>(key.data()), foo[i], sizes[i]);
simplePair_t pair = std::make_pair(key, 0);
std::cout << "key: \"" << key << "\" - " << static_cast<const void*>(key.data())
<< " pair.first: \"" << pair.first << "\" - " << static_cast<const void*>(pair.first.data())
<< std::endl;
map.insert(map.end(), pair);
}
std::cout << "map size = " << map.size() << std::endl;
return 0;
}
将产生此输出:
key: "biz" - 0x1dec028 pair.first: "biz" - 0x1dec028
key: "baz" - 0x1dec028 pair.first: "baz" - 0x1dec028
key: "bar" - 0x1dec028 pair.first: "bar" - 0x1dec028
key: "foo" - 0x1dec028 pair.first: "foo" - 0x1dec028
key: "yin" - 0x1dec028 pair.first: "yin" - 0x1dec028
map size = 1
虽然在Visual Studio 2013中编译的相同代码将产生以下结果:
key: "biz" - 0039FE14 pair.first: "biz" - 0039FDE0
key: "baz" - 0039FE14 pair.first: "baz" - 0039FDE0
key: "bar" - 0039FE14 pair.first: "bar" - 0039FDE0
key: "foo" - 0039FE14 pair.first: "foo" - 0039FDE0
key: "yin" - 0039FE14 pair.first: "yin" - 0039FDE0
map size = 5
有趣的是,当字符串的大小改变每次迭代时,使用g ++编译时代码将“正常工作”。替换:
const char* foo[] = { "biz", "baz", "bar", "foo", "yin" };
const int sizes[] = { 3, 3, 3, 3, 3 };
使用:
const char* foo[] = { "bizbiz", "baz", "barbar", "foo", "yinyin" };
const int sizes[] = { 6, 3, 6, 3, 6 };
将产生:
key: "bizbiz" - 0xc54028 pair.first: "bizbiz" - 0xc54028
key: "baz" - 0xc54098 pair.first: "baz" - 0xc54098
key: "barbar" - 0xc54108 pair.first: "barbar" - 0xc54108
key: "foo" - 0xc54178 pair.first: "foo" - 0xc54178
key: "yinyin" - 0xc541e8 pair.first: "yinyin" - 0xc541e8
map size = 5
我对移动语义的理解是不完整的,但我想知道这是不是在这里发挥作用。在生成std :: pair时是否释放内部std :: string缓冲区的所有权?或者它是否像std :: string :: resize()方法中的优化一样,它不应该重新分配新的字符缓冲区?
答案 0 :(得分:3)
由于以下奇怪的代码行,您的代码具有未定义的行为:
key.resize(sizes[i]);
memcpy(const_cast<char *>(key.data()), foo[i], sizes[i]);
几乎每当你发现自己需要抛弃const
时(并且,就此而言,使用memcpy
),你做错了。
确实,粗略地瞥了一眼documentation for std::string::data
(你读过,对吗?对吗?)证实:
修改通过
data
访问的字符数组是 [sic] 未定义的行为。
一个好的老式作业有什么问题吗?
key.assign(foo[i], sizes[i]);`
由于UB,进一步分析这一点是愚蠢的。