bodacydo
bodacydo

Reputation: 79329

Returning local data from functions in C and C++ via pointer

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.

Upvotes: 6

Views: 4833

Answers (13)

user1973455
user1973455

Reputation: 1

The way I see it you have three main options because this one is dangerous and utilizes undefined behavior:

replace: char n[10] = "bodacydo!"

with: static char n[10] = "bodacydo!"

This will give undesirable results if you use the same function more than once in row while trying to maintain the values contained therein.

replace:
char n[10] = "bodacydo!"

with:
char *n = new char[10]; *n = "bodacydo!"

With will fix the aforementioned problem, but you will then need to delete the heap memory or start incurring memory leaks.

Or finally:

replace: char n[10] = "bodacydo!";

with: shared_ptr<char> n(new char[10]) = "bodacydo!";

Which relieves you from having to delete the heap memory, but you will then have change the return type and the char *n in main to a shared_prt as well in order to hand off the management of the pointer. If you don't hand it off, the scope of the shared_ptr will end and the value stored in the pointer gets set to NULL.

Upvotes: 0

Samit
Samit

Reputation: 186

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

Upvotes: -2

caf
caf

Reputation: 239041

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

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

Upvotes: 1

MAK
MAK

Reputation: 26586

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;
}

Upvotes: 0

PauliL
PauliL

Reputation: 1269

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.

Upvotes: 0

sth
sth

Reputation: 229593

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.

Upvotes: 2

Mathieu Mah&#233;
Mathieu Mah&#233;

Reputation: 2746

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.

Upvotes: 1

Nils Pipenbrinck
Nils Pipenbrinck

Reputation: 86343

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;
}

Upvotes: 1

Incognito
Incognito

Reputation: 16577

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.

Upvotes: 4

Peter Miehle
Peter Miehle

Reputation: 6070

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);
}

Upvotes: 10

Roy Truelove
Roy Truelove

Reputation: 22456

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!).

Upvotes: 1

Nikolai Fetissov
Nikolai Fetissov

Reputation: 84169

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)

Upvotes: 2

Ned Batchelder
Ned Batchelder

Reputation: 375584

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.

Upvotes: 13

Related Questions