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

Kiy*_*ano 0 c++ mfc visual-studio-2010 visual-studio-2017

我们正在将旧的 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
}
Run Code Online (Sandbox Code Playgroud)

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

如果有人可以解释的话,我对这种行为有两个问题。

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

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

IIn*_*ble 5

代码从来都不是正确的。通过将零值写入内存来初始化对象没有也从来没有1任何明确定义的行为。对于简单可复制的类型,在实践中你经常会侥幸逃脱,但行为仍然是未定义的。然而,有问题的代码不能声称其本身是灰色区域:mystruct 不是简单可复制的

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

幸运的是,解决方法很简单:只需删除ZeroMemory呼叫即可。默认构造的mystruct将具有默认构造的std::string成员,因此没有理由再次“初始化”它们。


1 无论如何,我相信。毕竟这是 C++,而且它的复杂性似乎已经远远超过了平均心理能力。