为什么在使用g ++ 4.8.4时,这个代码会导致一个包含单个元素的地图?

时间:2015-10-05 18:27:34

标签: c++ c++11 g++

在过去一年半的时间里,我参与了将一个较旧的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()方法中的优化一样,它不应该重新分配新的字符缓冲区?

1 个答案:

答案 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,进一步分析这一点是愚蠢的。