tags:

views:

421

answers:

7

Coming from a PHP background, I'm used to writing small functions that return a string (or the response from another function) like so:

function get_something(){
    return "foo";
}

However, I'm new to C and am trying to figure how to do some really fundamental things like this.

Can people review the following similar functions and tell me how they differ and which one is the best/cleanest to use?

char *get_foo(){
    char *bar;
    bar = "bar";
    return bar;
}

char *get_foo(){
    char *bar = "bar";
    return bar;
}

char *get_foo(){
    char *bar = NULL;
    bar = "bar";
    return bar;
}

char *get_foo(){
    return "bar";
}

Is there any difference between these functions or is this a style issue?

One other thing. If I have two functions and one calls the other, is this alright to do?

char *get_foo(){
    return "bar";
}

char *get_taz(){
    return get_foo();
}

UPDATE: How would these functions need to change if get_foo() did not return a const char*? What if get_foo() calls another function that has a char* of different lengths?

+3  A: 

Differences like this will usually be optimized out by the compiler anyway ... I would vote for :

char *get_foo(){
    char *bar = "bar";
    return bar;
}

or

const char *get_foo(){
    return "bar";
}

or something along the lines of (but obviously more defensive, and on GNU system):

char *get_foo(){
    return strdup("bar");
}

Depending on future use and expansion of the function. Really, due to optimizations, it is a readability issue, and how you want the string (mutable/not) for future use.

Because you are initializing the variable to a constant in the data of the program. I would do things differently if I were creating a string dynamically.

Aiden Bell
All was going well until you chose to mention strdupa() - which uses alloca() to allocate the space, which means that the last example is simply wrong!
Jonathan Leffler
Remind me, where is "bar" stored and what happens to that memory when the function returns?
Key
Using 'const' in the return type is important.
Jonathan Leffler
@Key in most schemes the string literal "bar" is stored in the .text segment in memory. The "bar" doesn't disappear when the function returns.
Aiden Bell
+6  A: 

The four are equivalent, especially the first three ones - the compiler is likely to compile them to exactly the same code. So I'd go for the last one, for being smaller.

Having said that - you're returning a const char*, not a char*, so this particular code could break everything, depending on how you use it (if it compiles at all, which you can force anyway). The thing is, you're returing a pointer to a string that isn't dynamically allocated, but part of the executable image. So modifying it could be dangerous.

As a more general rule, never return a pointer to stuff allocated on the stack (ie not created using new or malloc) because as soon as the function ends, the scope of that variable also ends, gets destroyed, so you get a pointer to invalid (freed) memory.

ggambett
How would these functions need to change if get_foo() did not return a const char*? What if get_foo() calls another function that has a char* of different lengths?
Adam Plumb
Just to be clear: He returns a char* each time. But the destination the pointer points to is not writable, because it points to a string literal's memory. A string literal in C is an array having type `char[N]` (with `N` being the length +1 for '\0'), so it does well with assigning to a `char*`. But you cannot write to it.
Johannes Schaub - litb
+1  A: 

Like others already have stated, the compiler will produce likely the same code for the alternatives. But: are you forced to use C? Why not use C++ where you can use the std::string class. I haven't declared new char arrays for ages - too error-prone. You don't need to learn/master C before going to C++!

ToastedSoul
Personally, I would pick C over C++ for most tasks where I can choose.
Aiden Bell
Why? C++ includes all of C.
Steven Sudit
@Steven, or the C dialect of C++, doesn't matter. I would pick the C syntax over the C++ syntax.
Aiden Bell
I'll ask why again. For performance, consider that standard C++ strings are typically faster than C strings because they include the length and therefore don't need to scan for the terminator. And when it comes to power of expression, C++ dwarfs C.
Steven Sudit
Could be an embedded processor which doesn't have a C++ compiler for it.
Steve Melnikoff
@Steven: C++ does not include all of C. That's totally incorrect. See Bjarne Stroustrup's FAQ here: http://www.research.att.com/~bs/bs_faq.html#C-is-subset
Anthony Cuozzo
@Steven: Moreover, I've encountered plenty of embedded systems which have a C compiler, but no C++ compiler.
Anthony Cuozzo
@Steven: C++ strings may be faster than dynamically-allocated C strings, but they are certainly not faster than static storage. Required memory allocation is a killer for some high-performance or real-time applications.
Tom
A: 

Is there any difference between these functions or is this a style issue?

There is no difference in terms of output. As mentioned by others, the compiler will likely optimize the code anyway. My preference is to use:

char *get_foo(){
    char *bar = "bar";
    return bar;
}

If your return value gets to be more complex than a simple assignment, it helps to have the intermediate variable if you need to step through the code.

One other thing. If I have two functions and one calls the other, is this alright to do?

This is not a problem as long as you insure that the return types of the two functions are compatible.

Tim Henigan
+1  A: 

I'm always wary of return a pointer to a variable that exist on a lower scope level. When I first learned C some X-teen years ago, I can remember returning a pointer to a variable that was declared with local scope, before I called printf the debugger told me everything was normal but it never printed the right value. What was happening was: The variable was correct BEFORE the printf call, but when you call a function local variables get allocated on the stack, and deallocated upon return, so the variable that I had pointer to existed on the stack BEFORE calling printf and was the memory was reallocated to printf when the printf function was evoked thus overwritting the previous variables.

In your case the example you've given will assign a pointer to the constants table that is loaded as part of the executable and MIGHT be fine, depending on what else the actual program is doing, but I would recomend trying to keep the string at a higher level scope to prevent an easy bug from sneaking into your code as you tweek it. Based on the example you've given, you could probably have a string table allocated at the scope above this call, and just assign the variable instead of calling a function.

I.E.

#define FOO 0

#define BAR 1

#define FOOBAR 2

#define BARFOO 3

char *MyFooStrings[4] = {"Foo","Bar","FooBar","BarFoo"};

// Instead: myFoo = get_foo();

myFoo = MyFooStrings[FOO];

Pete

NoMoreZealots
A: 

UPDATE: How would these functions need to change if get_foo() did not return a const char*? What if get_foo() calls another function that has a char* of different lengths?

get_taz() just has to have a return type that is assignment-compatible with get_foo(). For example, if get_foo() returns an int, then get_taz() has to return something that you can assign an int to - like int, long int, or similar.

A "char* of different lengths" doesn't really mean anything, because a "char *" doesn't really mean "string" - it means "the location of some chars". Whether that location holds three chars or thirty, a "char *" is still a "char *", so this is perfectly OK:

const char *get_zero(void)
{
    return "Zero";
}

const char *get_nonzero(void)
{
    return "The number is non-zero";
}

const char *get_n(int n)
{
    if (n == 0)
    {
        return get_zero();
    }
    else
    {
        return get_nonzero();
    }
}
caf
A: 

First off, some of those will cause your program to crash.

The function:

 char *get_foo(){
    char *bar;
    bar = "bar";
    return bar;
}

Is incorrect C code (it may not crash, but you never know)

char *bar

allocates 1 pointers worth of memory on the stack.

Personally, I would do it like this.

1.

char *get_foo1(void) {
   char *bar;
   bar = malloc(strlen("bar")+1);
   sprintf(bar,"bar");
   return bar;
}

2. Or pass your allocated variable in.

   void get_foo2(char **bar) {
     sprintf(*bar,"bar);
   }
  1. combine 1 and 2, give user options

When working with strings in C, you almost always need to malloc() memory for usage. Unless the length of the strings are known ahead of time or are very small. Additionally, you can use #2 above to avoid memory allocation, like this

int main(int argc, char *argv[]) {
   char bar[4];
   get_foo2(&bar);
}
James
I downvoted, because the code you show at the top is perfectly *valid* C code. There is no reason why it might crash.
Johannes Schaub - litb
downvote b/c of malloc(strlen("bar")+1) and sprintf(bar,"bar"). And char** when char* would have sufficed in get_foo2. Way more complex than necessary.
Tom