Can you safely get a pointer to a string from its c_str() const char*?

233 Views Asked by At

I have a const char pointer which I know for sure came from a string. For example:

std::string myString = "Hello World!";
const char* myCstring = myString.c_str();

In my case I know myCstring came from a string, but I no longer have access to that string (I received the const char* from a function call, and I cannot modify the function's argument list).

Given that I know myCstring points to contents of an existing string, is there any way to safely access the pointer of the parent string from which it originated? For example, could I do something like this?

std::string* hackyStringPointer = myCstring - 6; //Along with whatever pointer casting stuff may be needed

My concern is that perhaps the string's contents possibly cannot be guaranteed to be stored in contiguous memory on some or all platforms, etc.

4

There are 4 best solutions below

0
The Mawg On BEST ANSWER

Yes (it seems), although I agree that if I need to do this it's likely a sign that my code needs reworking in general. Nevertheless, the answer seems to be that the string pointer resides 4 words before the const char* which c_str() returns, and I did recover a string* from a const char* belonging to a string.

    #include <string>
    #include <iostream>
    std::string myString = "Hello World!";
    const char* myCstring = myString.c_str();
    unsigned int strPtrSize = sizeof(std::string*);
    unsigned int cStrPtrSize = sizeof(const char*);
    long strAddress = reinterpret_cast<std::size_t>(&myString);
    long cStrAddress = reinterpret_cast<std::size_t>(myCstring);
    long addressDifference = strAddress - cStrAddress;
    long estStrAddress = cStrAddress + addressDifference;
    std::string* hackyStringPointer = reinterpret_cast<std::string*>(estStrAddress);

    cout << "Size of String* " << strPtrSize << ", Size of const char*: " << cStrPtrSize << "\n";
    cout << "String Address: " << strAddress << ", C String Address: " << cStrAddress << "\n";
    cout << "Address Difference: " << addressDifference << "\n";
    cout << "Estimated String Address " << estStrAddress << "\n";
    cout << "Hacky String: " << *hackyStringPointer << "\n";

    //If any of these asserts trigger on any platform, I may need to re-evaluate my answer
    assert(addressDifference == -4);
    assert(strPtrSize == cStrPtrSize);
    assert(hackyStringPointer == &myString);

The output of this is as follows:

Size of String* 4, Size of const char*: 4

String Address: 15725656, C String Address: 15725660

Address Difference: -4

Estimated String Address: 15725656

Hacky String: Hello World!

It seems to work so far. If someone can show that the address difference between a string and its c_str() can change over time on the same platform, or if all members of a string are not guaranteed to reside in contiguous memory, I'll change my answer to "No."

0
Remy Lebeau On

Given that I know myCstring points to contents of an existing string, is there any way to safely access the pointer of the parent string from which it originated?

No, there is no way to obtain a valid std::string* pointer from a const char* pointer to character data that belongs to a std::string.

I received the const char* from a function call, and I cannot modify the function's argument list

Your only option in this situation would be if you can pass a pointer to the std::string itself as the actual const char* pointer, but that will only work if whatever is calling your function does not interpret the const char* in any way (and certainly not as a null-terminated C string), eg:

void doSomething(void (*func)(const char*), const char *data)
{
    ...
    func(data);
    ...
}
void myFunc(const char *myCstring)
{
    std::string* hackyStringPointer = reinterpret_cast<std::string*>(myCstring);
    ...
}

...

std::string myString = "Hello World!";
doSomething(&myFunc, reinterpret_cast<char*>(&myString));
6
gonutz On

This reference says

The pointer returned may be invalidated by further calls to other member functions that modify the object.

You say you got the char* from a function call, this means you do not know what happens to the string in the mean time, is that right? If you know that the original string is not changed or deleted (e.g. gets out of scope and thus is destructed) then you can still use the char*.

Your example code however has multiple problems. You want to do this:

std::string* hackyStringPointer = myCstring - 6;

but I think you meant

char* hackyStringPointer = myCstring;

One, you cannot cast the char* to a string* and second you do not want to go BEFORE the start of the char*. The char* points to the first character of the string, you can use it to access the characters up to the trailing 0 character. But you should not go before the first or after the trailing 0 character though, as you do not know what is in that memory or if it even exists.

3
NathanOliver On

You cannot convert a const char* that you get from std::string::c_str() to a std::string*. The reason you can't do this is because c_str() returns a pointer to the string data, not the string object itself.

If you are trying to get std::string so you can use it's member functions then what you can do is wrap myCstring in a std::string_view. This is a non-copying wrapper that lets you treat a c-string like it is a std::string. To do that you would need something like

std::string_view sv{myCstring, std::strlen(myCstring)};
// use sv here like it was a std::string