c_str() results from vector<string> become garbage with libc++

c++, garbage-collection, string, vector

Solution

If you have any re-allocations in `savedstrings`, the pointers to the underlying string data may become invalid. De-referencing such pointers would yield undefined behaviour.

You can avoid re-allocations by reserving the right amount of capacity:

vector<string> savedstrings;
savedstrings.reserve(10);

Here's an example showing how a vector's capacity grows as you push elements into is (g++ 4.7.3 on some ubuntu x86_64):

#include <vector>
#include <iostream>

int main()
{
  std::vector<int> v;

  for (int i = 0; i < 10; ++i){
    v.push_back(0);
    std::cout << v.capacity() << std::endl;
  }
}

Output:

1
2   # re-allocation
4   # re-allocation
4
8   # re-allocation
8
8
8
16  # re-allocation
16

Problem

I am trying to work out why the following C++ code works fine with libstdc++, but with libc++ the `c_str()` results become garbage. The code simply builds a vector of strings using `savedstrings.push_back("blah")`, and right after each string is added to the vector, `savedstrings.back().c_str()` is added to a separate vector of `const char*`. So each `const char*` should point to the corresponding string in the `savedstrings` vector. This works fine with libstdc++, but with libc++ the `const char*` at the start of the `cstrs` vector start to become garbage as the later ones are added. I'm not sure I understand what is going on here. Is the `savedstrings` vector moving the earlier strings as new ones are added, thus invalidating the `c_str()` result? How can I stop this from happening? ``` vector<string> savedstrings; vector<const char*> cstrs; for (int i = 0; i < 10; i++) { savedstrings.push_back("blah"); cstrs.push_back(savedstrings.back().c_str()); } vector<string>::iterator si; for(si=savedstrings.begin();si!=savedstrings.end();++si) cout << *si << endl; vector<const char*>::iterator ci; for(ci=cstrs.begin();ci!=cstrs.end();++ci) cout << *ci << endl; ```

Original source