Did the lifetime of c_str() changed between g++ 4.8.4 and g++ 5.3.1?

223 Views Asked by At

I start processes from a daemon which makes sure that said processes run continuously and get started in the proper order, etc.

At some point, I want to start the process with execv() so I prepare an array of string for the arguments as so:

std::vector<std::string> args;
args.push_back("--debug");
args.push_back("--connect");
args.push_back("10.0.0.5:4040");
...

In most cases, I have around 10 such arguments.

Now, execv() only access an array of bare pointers. So I do the following to create an array of such pointers:

std::vector<char *> args_p; // sorry, my code actually uses std::vector<char const *> args_p -- so constness is fine here
for(auto a : args)
{
    args_p.push_back(a.c_str());
}
args_p.push_back(nullptr); // list needs to be null terminated

Then I can call execv() with that last array:

execv(
    args_p[0],
    const_cast<char * const *>(&args_p[0])
);

That was working perfectly in Ubuntu 14.04 with g++ 4.8.4, but somehow, the c_str() pointers get invalidated when I try to run the same code compiled with g++ 5.3.1.

From my understanding, this should not be since I do not modify the strings between the first loop which creates the array of bare pointers and the execv() call.

The reference says:

The pointer obtained from c_str() may be invalidated by:

  • Passing a non-const reference to the string to any standard library function, or
  • Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend().

P.S. I already have a fix, I now do an stdup() of the c_str() and it works just fine that way. Only I was hoping to avoid one extra copy of the string if possible...

1

There are 1 best solutions below

5
On BEST ANSWER

Two problems here:

for(auto a : args)
{
    args_p.push_back(a.c_str());
}

First, c_str() returns a char const* and args_p is a vector<char*>. Since you need a pointer to non-const char, you'll have to use &a[0].

Second, a goes out of scope at the end of each iteration of the loop. So the pointer that you're holding onto will get destroyed out from under you. You need the pointers into the actual strings in args - so you'll need to iterate by reference:

for(auto& a : args)
{
    args_p.push_back(&a[0]);
}