Doubt regarding returning const char*

K

kiran.agashe

Hi,
Please refer program below:

#include <string>
#include <cstdio>
using namespace std;
const char* f();

main()
{
const char* str = f();
printf("%s", const_cast<char*> (str));
}


Now I have 2 implementations for f():

1.
const char* f()
{
return "Hello";
}

2.
const char* f()
{
string str = "Hello";
return str.c_str();
}


Are both of these implemetations exactly equivalent and "Safe" with
respect to memory model?


[
e.g.
char* f1()
{
char a[10];
return a;
}
is not safe.
]

What happens when we return a const char*?

Thanks & Regards,
- Kiran Agashe
 
G

GB

Hi,
Please refer program below:

#include <string>
#include <cstdio>
using namespace std;
const char* f();

main()


int main()
{
const char* str = f();
printf("%s", const_cast<char*> (str));
}


Now I have 2 implementations for f():

1.
const char* f()
{
return "Hello";
}

This is okay.
2.
const char* f()
{
string str = "Hello";
return str.c_str();
}

This is not okay. The str object will be destroyed when it goes out of
scope, which happens when f returns. Since the object str will no longer
exist, the pointer returned by std::string::c_str will no longer be valid.

Gregg
 
P

Peteris Krumins

Hi,
Please refer program below:

#include <string>
#include <cstdio>
using namespace std;
const char* f();

main()
{
const char* str = f();
printf("%s", const_cast<char*> (str));
}

main() should be int main().
const_cast said:
Now I have 2 implementations for f():

1.
const char* f()
{
return "Hello";
}

This one returns a const char pointer to string a "Hello" which
probably
was stored in a read only segment of the program which is good.
2.
const char* f()
{
string str = "Hello";
return str.c_str();
}

This is not safe.

writing string str = "Hello"; might leave you with an impression that
it is basically
the same as in the previous implementation of f() but this actually is
interpreted
as if you had typed string str("Hello"); which creates a local string
object and
copies "Hello" internally.

Thus when the the function returns the local string object is
destructed and the
pointer to it's internal copy of "Hello" is no longer valid.
Are both of these implemetations exactly equivalent and "Safe" with
respect to memory model?

Neither equivalent nor safe.
What happens when we return a const char*?

In general it means that a pointer to constant character is returned,
that is
the character const points to cannot be changed.


P.Krumins
 
I

Ian Collins

Hi,
Please refer program below:

#include <string>
#include <cstdio>
using namespace std;
const char* f();

main()
{
const char* str = f();
printf("%s", const_cast<char*> (str));

Why the const_cast?
}


Now I have 2 implementations for f():

1.
const char* f()
{
return "Hello";
}
Should be OK as you are returning a literal, not sure if it is
guaranteed to be safe.
2.
const char* f()
{
string str = "Hello";
return str.c_str();
}
Assuming you mean std::string, unsafe because str is a temporary. I
will probably work in your trivial case, but not safe.
Are both of these implemetations exactly equivalent and "Safe" with
respect to memory model?
No, one uses a string literal, one a temporary variable.

All the const is doing is preventing you from modifying the data pointed
to by the returned pointer.
 
K

kiran.agashe

Hi again,

I tried to check the assembler code for both implemetations of f().
1.
const char* f()
{
return "Hello";
}

2.
const char* f()
{
string str = "Hello";
return str.c_str();
}
In both cases there is an entry:

..asciz "Hello"

in the assembler files generated.

Does this mean that even in the second case a string literal "Hello" is
present for the entire lifetime of the program?

I agree that std::string str is temporary.

But instead of temporary variable, str.c_str() may be refering to the
string literaral stored in the read only segment of the program!

I don't understand assemble code fully.
Please correct me if I am wrong :)

Regards,
- Kiran
 
G

GB

Hi again,

I tried to check the assembler code for both implemetations of f().
1.
const char* f()
{
return "Hello";
}

2.
const char* f()
{
string str = "Hello";
return str.c_str();
}
In both cases there is an entry:

.asciz "Hello"

in the assembler files generated.

Does this mean that even in the second case a string literal "Hello" is
present for the entire lifetime of the program?

I agree that std::string str is temporary.

But instead of temporary variable, str.c_str() may be refering to the
string literaral stored in the read only segment of the program!

It's possible that the std::string object is optimized out of this
trivial program, but there is no guarantee, and it is not safe to assume
that this will work in general.

The .asciz entry exists in the second case because there is still a
string literal in your program that has to come from somewhere at
runtime. But if you look at the assembly code, you will probably see
that the string object allocates its own memory and then copies the
..asciz location into its own memory so it has a copy of it. This is what
the c_str function is likely to be returning, and this will probably get
deallocated before f returns.

Gregg
 
K

kiran.agashe

Does this mean that:

const char* f()
{
const char* s = "Hello";
return s;
}

is not also safe???

Thanks & Regards,
- Kiran Agashe
 
I

Ian Collins

Does this mean that:
Does what? Please quote!
const char* f()
{
const char* s = "Hello";
return s;
}

is not also safe???
No, it is safe, you are still returning a pointer to the literal. With
a string, you are returning a pointer to a _copy_ of the literal.
 
J

Jim Langston

Hi again,

I tried to check the assembler code for both implemetations of f().
1.
const char* f()
{
return "Hello";
}

2.
const char* f()
{
string str = "Hello";
return str.c_str();
}
In both cases there is an entry:

.asciz "Hello"

in the assembler files generated.

Does this mean that even in the second case a string literal "Hello" is
present for the entire lifetime of the program?

I agree that std::string str is temporary.

But instead of temporary variable, str.c_str() may be refering to the
string literaral stored in the read only segment of the program!

I don't understand assemble code fully.
Please correct me if I am wrong :)

Regards,
- Kiran

There is a constant string literal, but it's only used to initialize the
string, which stores it's data in dynamic memory.

This is pseudo code, and not exactly the way it's done, but something like
this happens.

std::string str = "Hello";

Okay, first the constructor is called then assignment. The std::string
contains a pointer to the data of where the chars are stored. The
constructor calls new for (most likely in this case) 0 bytes. Then the
assignment says to make it equal to = "Hello"; and the std::string releases
it's allocated memory, then calls new for 5 byte (enough to store 'H' 'e'
'l' 'l' 'o'). It then copies character by character into the dynamically
allocated array (it might copy all 5 chars at once, but same differnce).

Now, to further complicate things, .c_str() doesn't even point to this
memory, because std::string is not null terminated like c-style strings. I
believe when you call c_str() that std::string returns a pointer to
temporary memory (not positive on that, it may be permanent until the
std::string changes). But it points to memory that is now null-terminated,
which means it most likely copies it *again* and gives you a pointer to
that.

You are far removed from the
..asciz "Hello"
storage by a few copies. At least 1. Probably 2.

Easy way to figure that out.

const char[] Hello = "Hello";
std::string str = Hello;

Now, print out the addresses of Hello and str.c_str(). you'll see they're
different.
 
G

Gavin Deane

Jim Langston wrote:

This is pseudo code, and not exactly the way it's done, but something like
this happens.

std::string str = "Hello";

Okay, first the constructor is called then assignment. The std::string
contains a pointer to the data of where the chars are stored. The
constructor calls new for (most likely in this case) 0 bytes. Then the
assignment says to make it equal to = "Hello"; and the std::string releases
it's allocated memory, then calls new for 5 byte (enough to store 'H' 'e'
'l' 'l' 'o'). It then copies character by character into the dynamically
allocated array (it might copy all 5 chars at once, but same differnce).

This is a common misconception. Despite the use of the = sign, there is
no assignment going on here. An unnamed temporary string is constructed
using the constructor that takes a const char*. Then str is initialised
from that unnamed temporary using the std::string *copy constructor*.
In fact, the compiler is allowed to elide the use of the temporary and
construct str directly using the constructor taking a const char*.

None of this affects your explanation of what the OP was seeing, but
this misconception seems to be a cause of confusion between
initialisation and assignment, so I thought it worth mentioning.
Now, to further complicate things, .c_str() doesn't even point to this
memory, because std::string is not null terminated like c-style strings. I
believe when you call c_str() that std::string returns a pointer to
temporary memory (not positive on that, it may be permanent until the
std::string changes).

Yep. The pointer returned by c_str() is valid until you call a
non-const member function for the string (or in the OP's case, the
string is detroyed).

<snip>

Gavin Deane
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top