PDA

View Full Version : return null-terminated string



paneb
04-14-2003, 02:36 PM
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

SThomas
04-14-2003, 04:33 PM
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".

SThomas
04-14-2003, 04:38 PM
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.

Deiussum
04-15-2003, 04:25 AM
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 (&amp;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... http://www.opengl.org/discussion_boards/ubb/smile.gif

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

raverbach
04-15-2003, 05:30 AM
You can also create a function that returns a char* , i think it's the easiest way of doing this

paneb
04-15-2003, 07:10 AM
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 http://www.opengl.org/discussion_boards/ubb/smile.gif i alwasy think i understand pointers, until i hit something where i get stuck..alright, so thanks to you guys for the help http://www.opengl.org/discussion_boards/ubb/smile.gif

Deiussum
04-15-2003, 07:30 AM
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.

Deiussum
04-15-2003, 07:35 AM
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.

Bob
04-15-2003, 08:00 AM
Deiussum, 1 and 3 are also safe, because you're not returning the address of a local variable.

Bob
04-15-2003, 08:11 AM
By the way, why not use strings instead of messing with char *'s?



#include <string>
#include <iostream>

using namespace std;

void getStr(string &amp;str)
{
str = "This is awesome";
}

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

Deiussum
04-15-2003, 08:45 AM
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.

paneb
04-15-2003, 10:37 AM
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

Bob
04-15-2003, 11:19 AM
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.

SThomas
04-15-2003, 01:58 PM
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.

paneb
04-15-2003, 02:41 PM
is that why it always gives me an error when using the string?

SThomas
04-15-2003, 03:30 PM
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? http://www.opengl.org/discussion_boards/ubb/smile.gif

paneb
04-15-2003, 03:31 PM
yea yea, i like string too http://www.opengl.org/discussion_boards/ubb/smile.gif but i just want to understand this whole c-string thing http://www.opengl.org/discussion_boards/ubb/smile.gif

Deiussum
04-16-2003, 04:33 AM
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. http://www.opengl.org/discussion_boards/ubb/smile.gif 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.

paneb
04-16-2003, 04:44 AM
is strcpy standard in C++? or is it for C?

Deiussum
04-16-2003, 05:06 AM
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.

paneb
04-16-2003, 05:09 AM
the thing is that my string is going to be constant in a class, so would an std::string be unnecessary? right now i have it as a pointer, but i thought maybe i should just use a char* with strcpy..it might be simpler

Deiussum
04-16-2003, 05:24 AM
If it's going to be constant, just delcare it as:

const char* str = "Some Value";

You DON'T want to use strcpy to copy a value into a pointer with no memory allocated. The following would not be good.

char* str;
strcpy(str, "Some Value");

strcpy tries to copy from the memory of "Some Value", which is stored in the constant space, to the memory of str, which is a dangling pointer in the code above.

paneb
04-16-2003, 05:29 AM
basically its gonna store the window class name, so i cant assign a value to it..it has to be input by the user..

i could allocate memory on the heap though, right? and then delete the memory within the deconstructor?

[This message has been edited by paneb (edited 04-16-2003).]

paneb
04-16-2003, 06:23 AM
this works:




void set (char ** str)
{
strcpy (*str, "HELLO WORLD");
}

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

set (&amp;str);

cout << str << endl;
delete[] str;
}


it is supposed to right?

SThomas
04-16-2003, 06:56 AM
posted by deiussum:
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.

you can still use std::strings in these situations if you want to. &str[0] (where str is a std::string) will get you a non-const pointer to the data.

SThomas
04-16-2003, 08:07 AM
paneb, what you have there should work fine. with that function though, it's now pointless to pass in a pointer to a pointer, since you're not modifying the pointer itself when you do a strcpy(), you're only modifying the data that the pointer is pointing to. so passing in a regular char* would work fine. ok, this thread is way off topic, so i'm gonna stop posting now. this board is an opengl board, and really shouldn't be used for standard programming questions.

jebus
04-16-2003, 08:25 AM
Originally posted by SThomas:
pass the string by reference, ie use char*&, which will only work in c++ (c doesn't have references)

did i miss something here? references do work with C, i thought...

jebus

paneb
04-16-2003, 08:30 AM
thats why we need an OpenGL board cannot go without a small general programming board..anyways, thanks i got it now.. http://www.opengl.org/discussion_boards/ubb/smile.gif

Deiussum
04-16-2003, 09:23 AM
Originally posted by SThomas:
you can still use std::strings in these situations if you want to. &str[0] (where str is a std::string) will get you a non-const pointer to the data.

Does that throw an exception if not enough space has been allocated in the string yet, though? I would think if you have something like the following would throw an exception:

string s = "";

strcpy(&s[0], "Hello");

I know that when you do concatentation, the string will re-size itself as necessary, but I wouldn't think it would do so in the above case.

Deiussum
04-16-2003, 09:43 AM
Originally posted by jebus:
did i miss something here? references do work with C, i thought...

jebus


Nope. References were added in C++. In C you use a pointer to a pointer.

Anyway, as SThomas said, this is really more of a standard programming question than an OpenGL question. I always seem to get drawn into these for some reason, though. http://www.opengl.org/discussion_boards/ubb/smile.gif

Bob
04-16-2003, 10:01 AM
string::operator[] does not thrown an exception (even if you access an element outside the string, use string::at if you want exceptions), nor will that code thrown any exception related to std::string if strcpy mess up. Of course, an exception is thrown if strcpy tries to access memory outside allocated space, but that would be a regular access violation and has nothing to do with std::string.

Even if you can do that, I suggest you don't. If you use std::string, you should also stick to the C++ standard library only. The C library is not designed to work with std::string's, but the C++ library is. As Deiussum said above, there can be some problems with C API's, but I would consider using char arrays for temporary storage in those cases.

edit: stupid smileys...

[This message has been edited by Bob (edited 04-16-2003).]

Deiussum
04-16-2003, 10:09 AM
Thanks Bob. That's what I suspected. When I said exception, I actually meant an access violation. strcpy was really just an example I used. I could easily have used one of the Win32 API functions, but strcpy was the first thing to come to mind so I used it.

With VB, if you needed to allocate space for a string before passing it to a Win32 API function, you could set the VB string to Space(sizeNeeded), or something like that. The same thing would apply to std::string, but I agree with you that using a char* for temporary storage just seems better in those cases.