return null-terminated string

hey guys, icant seem to ge this simple thing going:

void getStr ( char * str)
{
char* s = “This is awesome”;

str = s;
}

void main ()
{
char * ret = new char[20];

memset (ret, ‘0’, 20);

getStr (ret);
cout << ret << endl;
delete ret;
}

this doesn’t work…i only get a string of 20 ‘0’…whats wrong? i just want to return a pointer to a string (not actually returning it with ->> char * getStr…)
thanks

are you familiar with passing by value versus passing by reference? basically, you’re passing the pointer into the function by value. the function creates its own local copy of the pointer, and modifies that. back in main, however, the pointer is unmodified. you can do one of two things: pass the string by reference, ie use char*&, which will only work in c++ (c doesn’t have references), or pass a pointer to a pointer to a string, ie a char**. for more info, google on “pass by value/reference”.

i should add that if you set the “ret” variable to the “This is awesome” string and try to delete it, you’re going to get a memory access error. that string will be created/deleted for you automatically by the compiler. you shouldn’t mess with it.

Not only that, but the char* s that is created in getStr will be deleted upon exit of the function. You should either do a strcpy in that function or change your code to something like so:

//Pass in a pointer to a pointer.
// A reference to a pointer would work as well.

void getStr ( char ** str)
{
//Allocate memory here
char* s = new char[20];
//stcpy the data into the allocated string
strcpy(s, “This is awesome”);
//Dereference the pointer to the pointer to set it to point to s
*str = s;
}
void main ()
{
char * ret; //Don’t allocate here
memset (ret, ‘0’, 20); // Pass in pointer to ret
getStr (&ret);// Pass in pointer to ret
cout << ret << endl;
// Ret points to memory dynamically allocated in getStr so it’s safe to delete
delete ret;
}

Personally, I think I’d prefer the following method:

void getStr ( char * str)
{
//stcpy the data into the string passed in
strcpy(str, “This is awesome”);
}
void main ()
{
char ret[20]; //Just allocate on the stack here
memset (ret, ‘0’, 20);
getStr (ret);
cout << ret << endl;
}

Ugh. After about 20 edits, I think I got what I wanted now…

[This message has been edited by Deiussum (edited 04-15-2003).]

You can also create a function that returns a char* , i think it’s the easiest way of doing this

well, yes indeed i could just return a char*, but in certain cases it helps to just pass the pointer…plus, its nice to know i alwasy think i understand pointers, until i hit something where i get stuck…alright, so thanks to you guys for the help

Originally posted by raverbach:
You can also create a function that returns a char* , i think it’s the easiest way of doing this

Again, though, you need to be sure you know where the memory is allocated. For instance take the following functions…

char* Function1()
{
return “SomeValue”;
}

char* Function2()
{
char val[20];

strcpy(val, "SomeValue");

return val;

}

char* Function3()
{
char* val = “SomeValue”;

return val;

}

char* Function4()
{
char* val = new char[20];

strcpy(val, "SomeValue");

return val;

}

char* Function5(char* val)
{
strcpy(val, “SomeValue”);

return val;

}

The only ones there that are “safe” to use are 4 and 5, and 5 is only safe if the memory for val was allocated before being passed to the function.

All of the others will allocate memory on the stack for that function call, and as soon as that function exits, that memory goes away. Trying to access it later will likely throw an exception, or worse yet, write to some other memory that messes up some other calculation. The later of those possibilities can be very difficult to track down in large amounts of code.

One other quick note about string literals. They should be thought of as constants because they are essentially stored in the same space. Take the following code as an example:

char* str = “Hello”;
strcpy(str, “World”);

You will most likely get an exception error trying to execute the strcpy above because you are esentially trying to write to memory that was flagged as constant.

Deiussum, 1 and 3 are also safe, because you’re not returning the address of a local variable.

By the way, why not use strings instead of messing with char *'s?

#include
#include

using namespace std;

void getStr(string &str)
{
str = “This is awesome”;
}

int main(int, char **)
{
string ret;
getStr(ret);
cout << ret << endl;
}

Originally posted by Bob:
Deiussum, 1 and 3 are also safe, because you’re not returning the address of a local variable.

That’s true. They’d be safe to read from, but not to write to because they point to the space for string literals.

Bob: i think its good to understand the use of char*, and it helps a lot understanding any kind of pointers…thats just me…its hard sometimes…i only use strings when i have to do some manipulations on the string

I agree that they are good to learn how pointers and memory works, but that’s about it. Personally, I see no reason to use char pointers for strings over std::string, but that’s a matter of taste.

posted by deiussum:
Not only that, but the char* s that is created in getStr will be deleted upon exit of the function.

no reasonable compiler will create the string on the stack. the executable that gets produced usually has a segment reserved for initialized data, and this is where stuff like numeric constants and string literals get stored. it would be pretty poor of a compiler to allocate data for constants on the stack, since the data will never change, and therefore you only ever need one copy. i tested with visual c++, and it definitely doesn’t allocate the data on the stack.

char * ret = new char[20];
memset (ret, '0', 20);

one last thing to note. the string that gets created above is missing the null terminator.

is that why it always gives me an error when using the string?

posted by paneb:
is that why it always gives me an error when using the string?

i imagine so. almost all of the c string functions operate on null terminated strings. so if you’re missing the null terminator, then you’re probably going to accidentally access memory that doesn’t belong to you. see why everyone likes std::strings?

yea yea, i like string too but i just want to understand this whole c-string thing

Originally posted by SThomas:
no reasonable compiler will create the string on the stack. the executable that gets produced usually has a segment reserved for initialized data, and this is where stuff like numeric constants and string literals get stored. it would be pretty poor of a compiler to allocate data for constants on the stack, since the data will never change, and therefore you only ever need one copy. i tested with visual c++, and it definitely doesn’t allocate the data on the stack.

For the cases in my examples that use string literals, that’s true. But the case that uses a char array, the memory WILL be allocated on the stack. As Bob already noted, those 2 cases would be safe to read from, but they would not be safe to write to for the fact you noted here. They are basically stored with the constants.

For the memset, I didn’t notice that he was copying ‘0’ instead of 0 into his array. Good catch. Although, if you do a strcpy of a string literal into the array, it will end up being null terminated, anyway.

I also tend to use string objects over char* for strings. Sometimes they get to be a bit cumbersome, though. For instance if you are working with a C api that takes in a char* that needs to be written to. Some of the Win32 API functions for retrieving registry settings, for example. Since string::c_str() returns a const char*, it can’t be written to by these APIs, so you need to use a regular char* in these cases anyway.

is strcpy standard in C++? or is it for C?

It’s in one of the standard C libraries, but can be used in C++. If you use a C++ std::string object, you can instead just use the = operator.