简体   繁体   中英

Passing string 'by value' change in local value reflect in original value

Why is the change of my local variable's value getting reflected into original variable? I am passing it by value in C++.

#include <string>
#include <iostream>

void test(std::string a)
{
    char *buff  =  (char *)a.c_str();

    buff[2] = 'x';
    std::cout << "In function: "  << a;

}
int main()
{
    std::string s = "Hello World";
    std::cout  << "Before : "<< s << "\n" ;
    test(s);
    std::cout << "\n" << "After : " << s << std::endl;
    return 0;
}

Output:

Before : Hello World
In function: Hexlo World
After : Hexlo World

As soon as you wrote

buff[2] = 'x';

and compiled your code all bets were off. Per [string.accessors]

 const charT* c_str() const noexcept; 
  1. Returns: A pointer p such that p + i == &operator[](i) for each i in [0,size()] .
  2. Complexity: constant time.
  3. Requires: The program shall not alter any of the values stored in the character array.

emphasis mine

Since you are not allowed to modify the characters that the pointer points to but you do, you have undefined behavior. The compiler at this point is allowed to do pretty much whatever it wants. Trying to figure out why it did what it did is meaningless as any other compiler might not do this.

The moral of the story is do not cast const away unless you are really sure that you know what you are doing and if you do you need to, then document the code to show you know what you are doing.

Your std::string implementation uses reference counting and makes a deep copy only if you modify the string via its operator[] (or some other method). Casting the const char* return value of c_str() to char* will lead to undefined behavior.

I believe since C++11 std::string must not do reference counting anymore, so switching to C++11 might be enough to make your code work ( Edit: I did not actually check that before, and it seems my assumption was wrong).

To be on the safe side, consider looking for a string implementation that guarantees deep copying (or implement one yourself).

#include <cstring>
#include <string>
#include <iostream>

void test(std::string a)
{
    // modification trough valid std::string API
    a[2] = 'x';
    const char *buff  = a.c_str(); // only const char* is available from API
    std::cout << "In function: "  << a << " | Trough pointer: " << buff;

    // extraction to writeable char[] buffer
    char writeableBuff[100];

    // unsafe, possible attack trough buffer overflow, don't use in real code
    strcpy(writeableBuff, a.c_str());

    writeableBuff[3] = 'y';
    std::cout << "\n" << "In writeable buffer: "  << writeableBuff;
}

int main()
{
    std::string s = "Hello World";
    std::cout  << "Before : "<< s << "\n" ;
    test(s);
    std::cout << "\n" << "After : " << s << std::endl;
    return 0;
}

Output:

Before : Hello World
In function: Hexlo World | Trough pointer: Hexlo World
In writeable buffer: Hexyo World
After : Hello World

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM