ZeroMemory 具有 std::string 并为其赋值的结构在 VS2010 和 VS2017 中具有不同的行为

ZeroMemory a struct with std::string and assigning value to it has different behavior in VS2010 and VS2017

我们正在将一个旧的 MFC 项目从 VS2010 升级到 VS2017,我们发现在将数据分配给使用 ZeroMemory 清除的结构内的 std::string 时出现了不同的行为。

我创建了一个简单的 MFC 程序来重现该问题。

std::string getStrData() {
    std::string temp = "world";
    return temp;
}

CMainFrame::CMainFrame()
{
    struct mystruct{
        std::string mystr_in1;
        std::string mystr_in2;
    };
    std::string mystr_out = "hello";
    mystruct* sttemp = new mystruct();
    ZeroMemory(sttemp, sizeof(mystruct));  // <-- we think this is bad
    sttemp->mystr_in1 = mystr_out;         // <-- VS2010: "hello" is assigned, but VS2017: garbage is assigned
    sttemp->mystr_in2 = getStrData();      // <-- VS2010 and VS2017: "world" is assigned
}

在 VS2010 中,mystr_out(“hello”)的值被正确分配给 mystr_in1。 然而在VS2017中,垃圾数据被分配到mystr_in1。 我在 Whosebug 中读到类似的问题,在这种情况下执行 ZeroMemory 会破坏 std::string 所以我们认为这是问题的原因。 但在mystr_in2中,它能够在VS2010和VS2017中正确分配“world”。

如果有人能解释一下,我有 2 个关于此行为的问题。

  1. 对于第一种情况(mystr_in1),为什么在VS2010和VS2017中编译的代码有不同的行为? VS2017 中是否有某种项目设置可以解决此问题? 在升级旧的VS2010项目时,我们只是在VS2017中打开VS2010解决方案,选择升级解决方案。 (我们大部分老项目只要这样做就没有问题)

  2. 对于第二种情况(mystr_in2),它应该与第一种情况相同(?)但是分配给它的 std::string 是从函数返回的. 这与第一种情况有何不同,第一种情况也分配了 std::string?

密码从未正确过。通过将文字零值写入内存来初始化对象 doesn't have and never had1 any well defined behavior. For trivially copyable types you often get away with it, in practice, but the behavior is still undefined. The code in question, however, cannot claim that gray area for itself: mystruct isn't trivially copyable.

观察到的行为变化很可能是由于 SSO(短字符串优化)引起的,尽管对于从未有任何明确指定的行为开始的行为变化进行推理是没有实际意义的。

幸运的是,修复很简单:只需删除 ZeroMemory 调用即可。 default-constructed mystruct 将有 default-constructed std::string 个成员,因此没有理由再次“初始化”它们。


1 我相信,反正。这毕竟是 C++,它的复杂性似乎已经超出了平均心智能力。