Probably because the temp
object is re-using its internal allocation. When you store the c_str()
result you are only storing a memory address. The std::string
class does not create a completely new allocation each time you read into it from your string stream, rather it reuses the allocation it already has (if possible).
Further, using a pointer returned by c_str()
after you have done anything else to the std::string
object from which it was obtained invokes undefined behavior.1
If possible, just change args
to std::vector<std::string>
. If this is not possible then you need to strdup()
the pointer returned by c_str()
in order to create an entirely new allocation that copies the value of the string at that moment. You must, of course, remember to free()
the allocations when you are done.
Additionally, casting away the const
qualifier and writing to the pointer results in undefined behavior.2 At a minimum you need to change args
to be const char * args[100];
, but I would strongly suggest using a vector of strings instead.
1 http://en.cppreference.com/w/cpp/string/basic_string/c_str
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()
.
2 http://en.cppreference.com/w/cpp/string/basic_string/c_str
Writing to the character array accessed through c_str()
is undefined behavior.
Based on your comment indicating that you need to use exec()
, it sounds like you do need an array of pointers-to-char
. However, we can still do this using vectors. You need one vector to hold the std::string
objects, which will own the char*
allocations. Then you can use another vector to hold the actual pointers. Something like this:
const char * binaryPath = "/bin/foo";
std::vector<std::string> argStrings;
std::vector<char *> argPointers;
std::string temp = "some text and stuff here";
istringstream s(temp);
// argv[0] should always contain the binary's path.
argPointers.push_back(binaryPath);
while (s >> temp) {
argStrings.push_back(temp);
std::cout << "TOKEN " << argStrings.size()
<< " =" << argStrings.back() << std::endl;
// We cast away the const as required by the exec() family of functions.
argPointers.push_back(const_cast<char *>(argStrings.back().c_str()));
}
// exec() functions expect a NULL pointer to terminate the arguments array.
argPointers.push_back(nullptr);
// Now we can do our exec safely.
execv(binaryPath, &argPointers[0]);
In this case argStrings
owns the actual string allocations, and we are using argPointers
only to hold the array of pointers we will be passing to execv()
. The const_cast
is safe because execv()
does not modify the strings. (The argument is char * const []
for compatibility with older C code; the functions behave as though the argument is const char * const []
.)