Hans Passant's comment is correct that the returned pointer to the buffer can be moved in memory by the garbage collector. This is because, when the function stack unwinds, pin_ptr will unpin the pointer.
The solution is to 
- Obtain the System::String buffer and pin it so that the GC cannot
move it. 
- Allocate memory on the unmanaged heap (or just heap) where
it is not under the GC's jurisdiction, and cannot be moved by the
GC.
- Copy memory (and convert to desired encoding) from the
System::String buffer to the buffer allocated on the unmanaged heap.    
- Unpin the pointer so the GC can once again move the System::String
in memory. (This is done when pin_ptr goes out of the function
scope.)
Sample code:
char* ManagedStringToUnmanagedUTF8Char(String^ str)
{
    // obtain the buffer from System::String and pin it
    pin_ptr<const wchar_t> wch = PtrToStringChars(str);
    // get number of bytes required
    int nBytes = ::WideCharToMultiByte(CP_UTF8, NULL, wch, -1, NULL, 0, NULL, NULL);
    assert(nBytes >= 0);
    // allocate memory in C++ where GC cannot move
    char* lpszBuffer = new char[nBytes];
    // initialize buffer to null
    ZeroMemory(lpszBuffer, (nBytes) * sizeof(char)); 
    // Convert wchar_t* to char*, specify UTF-8 encoding
    nBytes = ::WideCharToMultiByte(CP_UTF8, NULL, wch, -1, lpszBuffer, nBytes, NULL, NULL);
    assert(nBytes >= 0);
    // return the buffer
    return lpszBuffer;
}
Now, when using:
char* foobar = ManagedStringToUnmanagedUTF8Char("testing");
//do something with foobar
//when foobar is no longer needed, you need to delete it
//because ManagedStringToUnmanagedUTF8Char has allocated it on the unmanaged heap.
delete foobar;