tags:

views:

609

answers:

5

I read somewhere that if you want a C/C++ function to return a character array (as opposed to std::string), you must return const char* rather than char*. Doing the latter may cause the program to crash.

Would anybody be able to explain whether this is true or not? If it is true, why is returning a char* from a function so dangerous? Thank you.

const char * my_function()
{
    ....
}

void main(void)
{
    char x[] = my_function();
}
+2  A: 

If the char* is allocated on the stack, you return a dangling pointer. Otherwise, so long as it matches the function's prototype and the declaration matches the return value, you should be fine.

Jeff Ober
+4  A: 

What you were told is not true.

Returning a const char * can improve the semantics of a function (i.e. don't mess with what I'm giving you) but returning a char * is perfectly fine.

However, in either case, you must make sure that you return a char * or const char * that was allocated on the heap in my_function (i.e. allocated using malloc or new), otherwise my_function will return, the memory for the [const] char * will be deallocated, and you will be accessing an invalid pointer.

And finally you must remember to free or delete the [const] char * that's been returned to you once you're done with it, or you will leak memory. Aren't C/C++ such great languages?

So, in C, you would have

const char *my_function() {
    const char *my_str = (const char *)malloc(MY_STR_LEN + 1);  // +1 for null terminator.
    /* ... */
    return my_str;
}

int main() {
    const char *my_str = my_function();
    /* ... */
    free(my_str);
    /* ... */
    return 0;
}
Andrew Keeton
That sort of thing is why I like C++'s std::string (even if it is a bad hack job of a string class definition), and smart pointers. You only need to go through that much pain in C.
David Thornley
+6  A: 

If you have a function that returns "string literals" then it must return const char*. These do not need to be allocated on the heap by malloc because they are compiled into a read-only section of the executable itself.

Example:

const char* errstr(int err)
{
    switch(err) {
        case 1: return "error 1";
        case 2: return "error 2";
        case 3: return "error 3";
        case 255: return "error 255 to make this sparse so people don't ask me why I didn't use an array of const char*";
        default: return "unknown error";
    }
}
Zan Lynx
It should also be noted out of completeness that these are interned and other variables may point to the identical location. That shouldn't be a problem if const is respected, though.
Jeff Ober
+1  A: 

Simply changing the return code won't cause a crash. However if the string you return is static (for example return "AString"), you should return const char * to make sure that the compiler detects any attempted modification of that memory, which will likely cause a crash. You can certainly use casts and such to get around the compiler checks, but in that case you'd have to work to make the crash happen.

Graeme Perrow
Thank you. Are you saying that if you return a literal string (e.g. return "This is a test";), the return type must be const char*? What is the danger if the return type is char* instead?
Andy
If the string doesn't get modified, there is no danger, but if the caller attempts to modify the string, the application will crash. If the return type is `const char *`, then the compiler won't let you modify it.
Graeme Perrow
Thank you. That is probably what they are talking about. Another aspect of C I did not know (and three cheers to std::string is great!).
Andy
+2  A: 

Usually, that isn't an issue, but there are things to consider. It's usually a matter of const-correctness, which means keeping track of what you can change and what you can't.

If you're returning a double-quoted string, it's const char *, and treating it like anything else is an invitation for trouble. Changing such a string is undefined behavior, but will usually cause the program to crash or change that string wherever it's referred to.

If you return a character array on the stack (i.e., a called function's local variable), it will go away, and the pointer will point to nothing in particular, probably with bad results at some time.

If the called function is returning something that's already const char *, then changing it to char * requires a cast. Further, if you're actually going to change it, you need to be sure it's changeable. It's usually much better to keep it as const char *.

There's no immediate problem with returning memory allocated with malloc() or new, but you do have the problem of ownership: what function should free()/delete it, when, and what do you do about possible copies? This is where C++'s smart pointers shine.

David Thornley