algrebe
algrebe

Reputation: 1651

The substring shouldnotexist in an operand causes std::string concatenation by + operator to return an empty string

Background - I was writing a benchmark to measure the cost of stat on a missing file and appended the string "shouldNotExist" to the end of a filepath to do so.

e.g. /foo should become /fooshouldNotExist

However, the code was just trying to stat an empty string "".

After digging a little deeper, I noticed that when concatenating two std::string types using the + operator, the result is an empty string if the result is not stored in an std::string immediately. For example, in the code below, I wrap it in parenthesis and convert to c_str().

I'm using g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0.

#include <stdio.h>
#include <string>
int main() {
    const char *path, *x, *y;
    std::string a = "abcd";
    std::string result;

    path = (a + std::string("shouldnotexist")).c_str();
    printf("path is [%s]\n", path); // path is []

    x = std::string("shouldnotexist").c_str();
    printf("x is [%s]\n", x); // path is [shouldnotexist]

    y = (std::string("hello") + x).c_str();
    printf("y is [%s]\n", y); // path is []

    path = (a + std::string("fooo")).c_str();
    printf("path is [%s]\n", path); // path is [abcdfoo]

    path = (a + std::string("foooshouldnotexist")).c_str();
    printf("path is [%s]\n", path); //path is []

    path = (std::string("fooshouldnotexist") + a).c_str();
    printf("path is [%s]\n", path); // path is []

    result = std::string("fooshouldnotexist") + a;
    // Storing in an intermediate std::string appears to work
    // path is [fooshouldnotexistabcd], length is 21
    printf("path is [%s], length is %lu\n", result.c_str(), result.length());

    return 0;
}

Are you observing a similar result? If so, why does this happen and is it documented anywhere?

Upvotes: 0

Views: 42

Answers (1)

Mormegil
Mormegil

Reputation: 8071

c_str gives you a pointer to the contents of a string instance to use in plain-C functions (like printf). However, when you apply it on a temporary string instance created by e.g. concatenation, it is invalidated immediately afterwards, as the temporary object goes out of scope:

  1. A temporary string instance for the concatenated string is created: std::string _temporary = a + std::string("shouldnotexist");
  2. The c_str method is called on it, storing the result: path = _temporary.c_str()
  3. The temporary instance goes out of scope, deleting the string, invalidating the pointer stored in path.
  4. You use the now-invalid stored pointer for printf.

As you write, by storing the temporary instance in a variable, the problem goes away, because now the instance does not go out of scope prior to the printf, keeping the pointer valid.

Upvotes: 2

Related Questions