Hi,

I am facing an issue in string to const char pointer conversion. I am doing stuff in the fun() but here i am just giving my example. Here it the code:

// Helper function
string fun() {
   string abc = "Daniweb";
   // print here  #1
   return abc;
}

// main function
int main() {
   const char * returnValue = fun().c_str();
   // print here #2
   string returnValue2 = fun().c_str();
   // print here  #3
}

When I print the value in fun() function before returning it, it is printing fine. (#1)
When I print the value after const char (#2), it i giving some random values (sometimes empty, adding some characters at the end etc.).
When I print the value after returnValue2, it is working fine in all cases. (#3)

What's the problem with the case #2? Can't we do the way I have done in #2? Please explain.

Thanks in advance.

I think your problem might be your compiler. GCC seems to work but VS2015 doesn't. One work around for VS2015 is to assign the output of the function to a variable then use its address:

#include <iostream>
#include <string>
using namespace std;
string fun() {
    string abc = "Daniweb";
    // print here  #1
    return abc;
}
// main function
int main() {
    string temp = fun();
    const char * returnValue = temp.c_str();
    // print here #2
    string returnValue2 = fun().c_str();
    // print here  #3
}

Edited 1 Month Ago by tinstaafl

@tinstaffl Can you please explain the reason behind this? I am curious to know why compiler might be doing this? Please throw some light on this.

Can you please explain the reason behind this?

You're capturing a direct pointer to the internal storage of the string returned by fun(), which is a temporary object. The temporary object is subsequently deconstructed, and the internal storage goes away. I'd suggest stepping into everything in the debugger to see how your compiler is handling the objects, because this is correct behavior.

returnValue2 works fine as you're copying the temporary object and preserving its contents in a new object.

Comments
+1 for to-the-point answer

@deceptikon Thanks a lot for the great explanation.

One question: when fun() returns, it calls the copy constructor again, right? As told here: Click Here

I got the point when you said it is temporary object but I need to understand the WHY part in this. In the fun(). string abc is a local which would be destructed after it returns from the function. I agree.

But, won't it call the copy constructor and then make the copy of the object and return it to the returnValue variable?

Please make me correct.

Edited 1 Month Ago by nitin1

when fun() returns, it calls the copy constructor again, right?

Ehh, kind of. Return value optimization is a thing, but conceptually it's okay to think of things that way for simplicity.

But, won't it call the copy constructor and then make the copy of the object and return it to the returnValue variable?

Yes, but the lifetime of that temporary object ends after the enclosing expression's sequence point. The enclosing expression is your initialization statement for returnValue, which is why you can call c_str successfully (ie. the temporary object still exists), but the pointer is dangling at the end of the statement (where the temporary is destroyed), because it points into the temporary object's memory rather than directly owning the memory.

Boiled down, the problem is really no different from returning a pointer to a local variable even though throwing unnamed temporaries into the mix makes it harder to recognize.

Edited 1 Month Ago by deceptikon

Comments
+1

Can anyone copy/paste the exact code where this was a problem (ie uncommenting the exact print statements)? I figured it had something to do with local memory being released too soon (though certainly could not have explained it like deceptikon) and that it COULD happen, but I was having problems MAKING it happen. Just dumb luck, I would imagine, but wanted to try to recreate the problem. I'm figuring that the subsequent call to the print statement itself overwrites the now out of scope "Daniweb" string or overwrites some some now out of scope pointer to the "Daniweb" string.

Would be nice to be able to recreate it, see what's happening.

Here's the code I used to test this:

#include <iostream>
#include <string>
using namespace std;
string fun() {
    string abc = "Daniweb\n";
    // print here  #1
    cout << abc;
    return abc;
}
// main function
int main() {
    const char * returnValue = fun().c_str();
    cout << returnValue;
    // print here #2
    string returnValue2 = fun().c_str();
    cout << returnValue2;
    // print here  #3
}

With CodeBlocks/GCC I get this output:

#1 Daniweb
#2 Daniweb
#1 Daniweb
#3 Daniweb

With VS2015 I get this:

#1 Daniweb
#2
#1 Daniweb
#3 Daniweb

When I examine returnValue in debug, it shows only the string terminator('\0').

Did some experimentation. Interesting stuff. Even if you make abc global, it still doesn't work in Visual Studio 2015, so it's not just that abc is going out of scope and getting overwritten. Result of below code is "DanIweb?!DanI". Thus c_str() isn't making a copy of anything, globalPtr is pointing to the same array of characters that abc uses and if you make a change using globalPtr or abc, both are affected. returnValue seems to point to I'm not sure where, but clearly it's not pointing to whatever c_str() returned?

string abc;
char* globalPtr;

string fun() {
    abc = "Daniweb";
    globalPtr = (char*) abc.c_str();
    *(globalPtr + 3) = 'I';
    cout << abc << "?";
    abc[4] = 0;
    return abc;
}
// main function
int main() {
    const char * returnValue = fun().c_str();
    cout << returnValue << "!" << globalPtr;
    return 0;
}

@Deceptikon

Awesome explanation. +1. Got the complete point. But, just one more question from your point only. Can you explain this little more?

The enclosing expression is your initialization statement for returnValue, which is why you can call c_str successfully (ie. the temporary object still exists)

So, calling c_str() is perfectly fine? if temporary object won't exist after enclosing braces of fun(), then callingc_str() should also be wrong. Isn't it?

Secondly, when exactly the temporray object be destroyed? I want to understand so that I can know what all operations I can do with the any object any function is returning.

Thanks in advance.

Edited 1 Month Ago by nitin1

Thus c_str() isn't making a copy of anything, globalPtr is pointing to the same array of characters that abc uses and if you make a change using globalPtr or abc, both are affected.

Yup, c_str isn't required to make a copy of anything, and usually doesn't.

returnValue seems to point to I'm not sure where, but clearly it's not pointing to whatever c_str() returned?

returnValue points to the internal buffer of the string object. VS2015's string class maintains a null terminator in the internal buffer to facilitate simple logic for c_str. The destructor also shrinks the string to zero length, which leaves the released memory clean and explains why the output is "" rather than the previous contents of the string.

I'd suspect GCC "works" because it doesn't have this cleaning step, so whatever was at that memory location remains the same while it "fails" with VS2015 because of the truncation in the destructor. Regardless of compiler quirks, the code exhibits undefined behavior due to accessing memory you don't own.

So, calling c_str() is perfectly fine?

Yes.

if temporary object won't exist after enclosing braces of fun(), then callingc_str() should also be wrong. Isn't it?

It would be if the braces of fun() were the sequence point I mentioned. Instead it's the full expression of returnValue = fun().c_str(). When the initialization of returnValue is complete, the temporary is destroyed, not before.

Secondly, when exactly the temporray object be destroyed?

The rules are somewhat complex, but a general guideline is temporary objects are destroyed at the end of the full expression in which they're created. In your case, the fun() call creates the temporary, and the enclosing expression is returnValue = fun().c_str().

Comments
Awesome :)
Good explanation

Even if you make abc global, it still doesn't work in Visual Studio 2015

Because your function is returning a copy of abc and that copy is a temporary object that goes out of scope at the sequence point, just as deceptikon explained.

returnValue seems to point to I'm not sure where, but clearly it's not pointing to whatever c_str() returned?

returnValue is exactly equal to the pointer retunred by c_str() - after all you just assigned it to that on the line before. However you called c_str() on the temporary object, not on abc and c_str() on the temporary object returns a different pointer than c_str() on abc, specifically it returns a pointer to temporary storage that gets freed when the temporary goes out of scope, which is why you see those results.

Comments
Good explanation
This question has already been answered. Start a new discussion instead.