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

Time:03-18

We are upgrading an old MFC project from VS2010 to VS2017, and we discovered a different behavior when assigning data to an std::string inside a struct which was cleared using ZeroMemory.

I created a simple MFC program to replicate the issue.

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
}

In VS2010, the value of mystr_out ("hello") is assigned properly to mystr_in1. However in VS2017, garbage data is assigned to mystr_in1. I read similar questions here in StackOverflow that doing ZeroMemory in this case destroys the std::string so we think this is the cause of the problem. But in mystr_in2, it is able to assign "world" properly in both VS2010 and VS2017.

I have 2 questions about this behavior if anyone can explain.

  1. For the 1st case (mystr_in1), why is the code compiled in VS2010 and VS2017 have different behavior? Is there some kind of Project settings in VS2017 to fix this? When upgrading the old VS2010 project, we just opened the VS2010 solution in VS2017 and chose upgrade solution. (Most of our old projects have no problem by just doing this)

  2. For the 2nd case (mystr_in2), it's supposed to be the same(?) with the 1st case but the std::string assigned to it was returned from a function. How is this different with the 1st case which also assigns an std::string?

CodePudding user response:

The code has never been correct. Initializing an object by writing literal zero values into memory 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.

The observed change in behavior might well be due to SSO (short string optimization), though it's moot to reason about a change in behavior that never had any well specified behavior to begin with.

Luckily, the fix is simple: Just remove the ZeroMemory call. A default-constructed mystruct will have default-constructed std::string members, so there's no reason to "initialize" them again.


1 I believe, anyway. This is C after all, and it seems that its complexity has pretty much outgrown average mental capacities.

  • Related