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

Roland Jude Macasero 21 Reputation points
2022-03-18T05:00:04.47+00:00

I asked the same question in StackOverflow, but I think this could be a Visual Studio issue so I'm asking it here.

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 to this 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?
Developer technologies C++
0 comments No comments
{count} votes

Accepted answer
  1. Minxin Yu 13,501 Reputation points Microsoft External Staff
    2022-03-18T07:04:18.333+00:00

    Hi, @Roland Jude Macasero

    why is the code compiled in VS2010 and VS2017 have different behavior?

    The sdk used in VS2010 is not the same as that in VS2017.

    For the 2nd case

    This is due to the corrupted memory.

    Try the snippet:

     sttemp->mystr_in1 = (std::string)mystr_out;  
     sttemp->mystr_in2 = getStrData();  
    

    Best regards,

    Minxin Yu


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    1 person found this answer helpful.

3 additional answers

Sort by: Most helpful
  1. Guido Franzke 2,191 Reputation points
    2022-03-18T07:01:01.23+00:00

    But we just want to know the answers of the 2 questions above.

    Hello,
    ZeroMemory on a std::string will result in undefined behaviour. The result will vary on different compilers (because it's undefined behaviour). Other programmes crash when assigning a string after ZeroMemory. You only were lucky before.
    As said already, do not use ZeroMemory on std::string. Use a constructor and set the strings to "".
    Regards, Guido

    1 person found this answer helpful.

  2. Castorix31 90,521 Reputation points
    2022-03-18T06:34:39.263+00:00

    You must not call ZeroMemory
    You can see this article which explains it : memset structure with std::string contained


  3. RLWA32 49,461 Reputation points
    2022-03-18T10:39:31.917+00:00

    There have been significant changes in the C++ standard and Microsoft's implementation between VS2010 and VS2017.
    I don't claim to be an expert on the Standard C++ Library internals but one thing to notice is that the VS2017 library includes an optimization to avoid allocating memory from the heap for small strings. So in VS2017 using ZeroMemory on a std::string object corrupts the internal bookkeeping used with this optimization.

    The GetStrData function returns a temporary std::string object. The std::string move assignment is used with this object. However, the std::string copy assignment is used with the mystr_out std::string object.

    The following sample code is presented only for purposes of this discussion and should not be used in real life.

    #define WIN32_LEAN_AND_MEAN
    #include <Windows.h>
    
    #include <string>
    
    std::string getStrData() {
        std::string temp = "world";
        return temp;
    }
    
    int main()
    {
        struct mystruct {
            std::string mystr_in1;
            std::string mystr_in2;
            std::string mystr_in3;
            std::string mystr_in4;
        };
    
    
        std::string mystr_out_long = "Hello - Longer string than hello to force use of heap allocation";
        std::string mystr_out = "Hello";
        mystruct* sttemp = new mystruct();
        ZeroMemory(sttemp, sizeof(mystruct));  // <-- we think this is bad
        sttemp->mystr_in1 = mystr_out;         // Uses copy assignment
        sttemp->mystr_in2 = getStrData();      // Uses move assignment
        sttemp->mystr_in3 = std::move(mystr_out); // Force move assignment instead of copy assignment for short string
        sttemp->mystr_in4 = mystr_out_long;  // Use copy assignment with long string
        return 0;
    }
    

    Note the different results between 1) copy vs. move assignment and 2) avoid small string optimization.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.