views:

172

answers:

12

I have argument with my friend. He says that I can return a pointer to local data from a function. This is not what I have learned but I can't find a counterargument for him to prove my knowledge.

Here is illustrated case:

char *name() {
    char n[10] = "bodacydo!";
    return n;
}

And it's used as:

int main() {
    char *n = name();
    printf("%s\n", n);
}

He says this is perfectly OK because after a program calls name, it returns a pointer to n, and right after that it just prints it. Nothing else happens in the program meanwhile, because it's single threaded and execution is serial.

I can't find a counter-argument. I would never write code like that, but he's stubborn and says this is completely ok. If I was his boss, I would fire him for being a stubborn idiot, but I can't find a counter argument.

Another example:

int *number() {
    int n = 5;
    return &n;
}

int main() {
    int *a = number();
    int b = 9;
    int c = *a * b;
    printf("%d\n", c);
}

I will send him this link after I get some good answers, so he at least learns something.

Thanks, Boda Cydo.

ps. (we're good buddies so calling him an idiot is not offensive)

+4  A: 

As soon as the scope of the function ends i.e after the closing brace } of function, memory allocated(on stack) for all the local variables will be left. So, returning pointer to some memory which is no longer valid invokes undefined behavior. Also you can say that local variable lifetime is ended when the function finished execution.

Also more details you can read HERE.

Incognito
This isn't quite right: the memory in question is on the stack, so it isn't released, it's left just as it was, but it will be overwritten in subsequent function calls.
Ned Batchelder
I said it is released by compiler, meaning that compiler will not keep it for the purpose. Anyway let me update it.
Incognito
+9  A: 

Your friend is wrong.

name is returning a pointer to the call stack. Once you invoke printf, there's no telling how that stack will be overwritten before the data at the pointer is accessed. It may work on his compiler and machine, but it won't work on all of them.

Your friend claims that after name returns, "nothing happens except printing it". printf is itself another function call, with who knows how much complexity inside it. A great deal is happening before the data is printed.

Also, code is never finished, it will be amended and added to. Code the "does nothing" now will do something once it's changed, and your closely-reasoned trick will fall apart.

Returning a pointer to local data is a recipe for disaster.

Ned Batchelder
+1 for "recipe for disaster"
Norman Ramsey
+2  A: 

My counter-arguments would be:

  • it's never OK to write code with undefined behavior,
  • how long before somebody else uses that function in different context,
  • the language provides facilities to do the same thing legally (and possibly more efficiently)
Nikolai N Fetissov
these arguments don't work for a stubborn person!!!!!!he would just say "it works on my compiler, i don't care (or know) out ub" and continues writing his code.as long as code works, he just writes it his way. :(if i could show a crashing example, that would perhaps teach him.
bodacydo
Here's a good example I'd try to construct. Arrange for the stack to have pointers to the `puts()` function and the string "format /y c:", then call a function that uses the function pointer with the char pointer as its argument. Now, find a way to 'corrupt' (I use that word loosely since the contents are undefined anyway) the stack in between so that the pointer to `puts()` gets replaced by the address of `system()`, and give it to your friend. :-)
R..
R.. - that is probably the best example. I am going to obfuscate it a little and give it to him. That will teach him good lesson.
bodacydo
If you want to be nice (and lazy) you could skip figuring out how to get the address of system to overwrite the function pointer, and just give him the code and dare him to run it. If he chickens out when he sees "format /y c:" you've made your point. :-)
R..
+1  A: 

You are correct - n lives on the stack and so could go away as soon as the function returns.

Your friend's code might work only because the memory location that n is pointing to has not been corrupted (yet!).

Roy Truelove
He must be lucky. i am sending him this whole thread. Peter's example perfect shows that it doesn't work.
bodacydo
+5  A: 

you will get a problem, when you call another function between name() and printf(), which itself uses the stack

char *fun(char *what) {
   char res[10];
   strncpy(res, what, 9);
   return res;
}

main() {
  char *r1 = fun("bla");
  char *r2 = fun("blubber");
  printf("'%s' is bla and '%s' is blubber", r1, r2);
}
Peter Miehle
i have no compiler at hand, so i do not know what this will result, but i hope it is 'blubber' is bla and 'blubber' is blubber.
Peter Miehle
Confirmed under gcc
cobbal
also interesting to note: on one machine I get `'blubber' is bla and 'blubber' is blubber` on my mac I get `'' is bla and '' is blubber`
cobbal
Peter: When you don't have a compiler handy, try an online one such a s http://ideone.com (which gives your expected result).
Jeanne Pindar
+2  A: 

It's undefined behavior and the value could easily be destroyed before it is actually printed. printf(), which is just a normal function, could use some local variables or call other functions before the string is actually printed. Since these actions use the stack they could easily corrupt the value.

If the code happens to print the correct value depends on the implementation of printf() and how function calls work on the compiler/platform you are using (which parameters/addresses/variables are put where on the stack,...). Even if the code happens to "work" on your machine with certain compiler settings it's far from sure that it will work anywhere else or under slightly different border conditions.

sth
+1  A: 

As the others have already pointed out it is not illegal to do this, but a bad idea because the returned data resides on the non-used part of the stack and may get overridden at any time by other function calls.

Here is a counter-example that crashes on my system if compiled with optimizations turned on:

char * name ()
{
  char n[] = "Hello World";
  return n;
}

void test (char * arg)
{
  // msg and arg will reside roughly at the same memory location.
  // so changing msg will change arg as well:
  char msg[100];

  // this will override whatever arg points to.
  strcpy (msg, "Logging: ");

  // here we access the overridden data. A bad idea!
  strcat (msg, arg);

  strcat (msg, "\n");
  printf (msg);
}

int main ()
{
  char * n =  name();
  test (n);
  return 0;
}
Nils Pipenbrinck
A: 

gcc : main.c: In function ‘name’: main.c:4: warning: function returns address of local variable

Wherever it could been done like that (but it's not sexy code :p) :

char *name()
{
  static char n[10] = "bodacydo!";
  return n;
}

int main()
{
    char *n = name();

    printf("%s\n", n);
}

Warning it's not thread safe.

A: 

Returning pointer to local variable is aways wrong, even if it appears to work in some rare situation.

A local (automatic) variable can be allocated either from stack or from registers.

  • If it is allocated from stack, it will be overwritten as soon as next function call (such as printf) is executed or if an interrupt occurs.
  • If the variable is allocated from a register, it is not even possible to have a pointer pointing to it.

Even if the application is "single threaded", the interrupts may use the stack. In order to be relatively safe, you should disable the interrupts. But it is not possible to disable the NMI (Non Maskable Interrupt), so you can never be safe.

PauliL
A: 

While it is true that you cannot return pointers to local stack variables declared inside a function, you can however allocate memory inside a function using malloc and then return a pointer to that block. Maybe this is what your friend meant?

#include<stdio.h>
#include<string.h>
#include<stdlib.h> 

char* getstr(){
    char* ret=malloc(sizeof(char)*15);
    strcpy(ret,"Hello World");
    return ret;
}
int main(){
    char* answer=getstr();
    printf("%s\n", answer);
    free(answer);
    return 0;
}
MAK
No, he meant exactly what i told. What you pasted is what every programmer should do. :) He's an idiot and doesn't know that.
bodacydo
+1  A: 

You're right, your friend is wrong. Here's a simple counterexample:

char *n = name();
printf("(%d): %s\n", 1, n);
caf
A: 

If we take the code segment u gave....

char *name() {
    char n[10] = "bodacydo!";
    return n;
}
int main() {
    char *n = name();
    printf("%s\n", n);
}

Its okay to use that local var in printf() in main 'coz here we are using a string literal which again isn't something local to name().

But now lets look at a slightly different code

class SomeClass {
    int *i;
public:
    SomeClass() {
        i = new int();
        *i = 23;
    }
    ~SomeClass() {
        delete i;
        i = NULL;
    }
    void print() {
        printf("%d", *i);
    }
};
SomeClass *name() {
    SomeClass s;
    return &s;
}
int main() {
    SomeClass *n = name();
    n->print();
}

In this case when the name() function returns SomeClass destructor would be called and the member var i would have be deallocated and set to NULL.

So when we call print() in main even though since the mem pointed by n isn't overwritten (i am assuming that) the print call will crash when it tried to de-reference a NULL pointer.

So in a way ur code segment will most likely not fail but will most likely fail if the objects deconstructor is doing some resource deinitialization and we are using it afterwards.

Hope it helps

Samit