为什么在使用g++ 4.8.4时,这段代码会产生一个包含单个元素的map ?

Why, when using g++ 4.8.4, does this code result in a map with a single element?

本文关键字:一个 map 元素 包含单 g++ 4时 为什么 段代码 代码      更新时间:2023-10-16

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

由于以下奇怪的代码行,您的代码有未定义的行为:

key.resize(sizes[i]);
memcpy(const_cast<char *>(key.data()), foo[i], sizes[i]);

几乎任何时候你发现自己需要抛弃const -ness(并且,就这件事而言,使用memcpy), 你做错了

确实,粗略地看一下std::string::data的文档(你读过,对吧?对吧?)确认:

修改通过data访问的字符数组是[sic]未定义的行为。

老式的作业有什么问题吗?

key.assign(foo[i], sizes[i]);`

由于UB,进一步分析这个是愚蠢的。