0
#include<stdio.h>

int* foo() {
  int a = 5;
  return &a;
}

void bar() {
  int a = 7;
}

void foobar() {
    int a = 97;
}

int main() {
    int* p = foo();
    bar();
    foobar();
    printf("%d", *p);

    return 0;
}

I can't understand the concept behind this behaviour. Why is the output always the value of local variable a in foobar function?

paulsm4
  • 114,292
  • 17
  • 138
  • 190
nktsg
  • 143
  • 1
  • 9
  • 2
    Undefined behaviour. – Zeta Aug 22 '15 at 20:06
  • because in foo() you're returning a pointer to a local variable in the scope of foo() so the behavior is undefined... – dacuna Aug 22 '15 at 20:10
  • You could try printing the local variables in `bar()` and `foobar()`. The chances are, the compiler has optimized the no-op functions into empty function bodies. But trying to print via the value returned by `foo()` is undefined behaviour; anything could happen. – Jonathan Leffler Aug 22 '15 at 20:11
  • For the record, this would become defined behavior IF you changed `int a = 5` in `foo()` to `int *a = malloc( sizeof ( a ) ); *a = 5` and then added the requisite `free(p)` after `printf` in `main`. – tonysdg Aug 22 '15 at 20:12
  • Just how many dups of this are there? – Martin James Aug 22 '15 at 20:18
  • @MartinJames No idea. Why not vote to close (for a good one)? – user2864740 Aug 22 '15 at 20:19
  • possible duplicate of [returning a local variable from function in C](http://stackoverflow.com/questions/4824342/returning-a-local-variable-from-function-in-c) – Martin James Aug 22 '15 at 20:27
  • Found one with Google. – Martin James Aug 22 '15 at 20:27

4 Answers4

4

You cannot do this:

int* foo() {
  int a = 5;
  return &a;  /* variable "a" is out of scope once "foo()" returns */
}

This is "undefined behavior". The result could be different from environment to environment, compiler to compiler, or even run to run. But it's always "garbage".

paulsm4
  • 114,292
  • 17
  • 138
  • 190
3

This is undefined behaviour since you return a pointer to a local variable, which is invalid outside its function scope. So this point in memory might be reused as it seems to be here. Do never return pointers to local variables!

PhilMasteG
  • 3,095
  • 1
  • 20
  • 27
2

The program has undefined bahaviour because pointer p is initialized by the address of the local variable of function foo

int* p = foo();

After exiting the function the local variable is destroyed and the pointer is invalid.

The reason for your ptogram always outputs the value of the local variable of function foobar is that it seems the functions use the same stack frame when they are called. So their local variables are placed at the same address in the stack.

If you will change function foo the following way

int* foo() {
  static int a = 5;
  return &a;
}

then the program will ouput the value of the local variable of the function that has static storage duration.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
1

When a function is called the compiler provides code to prepare stack, save current stack pointer and make space for local (automatic) variables. This commonly known as function prologue. The layout of stack after prologue is more or less:

+-----------------------------+
|      Parameters if any      |
+-----------------------------+
|      Return address         |
+-----------------------------+
| copy of ESP (stack pointer) |
+-----------------------------+
| Local variables begin here  |
+-----------------------------+
|   ...                       |

Now if you have 3 functions that will develop the same layout:

      foo()                 bar()                foobar()
+----------------+    +----------------+    +----------------+
| Return address |    | Return address |    | Return address |
+----------------+    +----------------+    +----------------+
|      ESP       |    |      ESP       |    |      ESP       |
+----------------+    +----------------+    +----------------+
|     int a      |    |     int a      |    |     int a      |
+----------------+    +----------------+    +----------------+
|      ...       |    |      ...       |    |      ...       |

If you have got the address of the variable a in the first function foo() the same address will be reused when you will call bar() and foobar(). Accessing a after the call you will find the last value written there by by foobar().

If you change your functions this way:

#include<stdio.h>

int* foo() {
  int a = 5;
  return &a;
}

int* bar() {
  int a;
  int b = 7;
  return &b;
}

int* foobar() {
  int a;
  int b;
  int c = 97;
  return &c;
}

int main() {
    int* p1 = foo();
    int* p2 = bar();
    int* p3 = foobar();
    printf("%d %d %d", *p1, *p2, *p3);

    return 0;
}

Surprisingly you will read all values. The situation is now:

      foo()                 bar()                foobar()
+----------------+    +----------------+    +----------------+
| Return address |    | Return address |    | Return address |
+----------------+    +----------------+    +----------------+
|      ESP       |    |      ESP       |    |      ESP       |
+----------------+    +----------------+    +----------------+
|     int a      |    |     int a      |    |     int a      |
+----------------+    +----------------+    +----------------+
|      ...       |    |     int b      |    |     int b      |
+----------------+    +----------------+    +----------------+
|      ...       |    |      ...       |    |     int c      |
+----------------+    +----------------+    +----------------+
|      ...       |    |      ...       |    |      ...       |

BTW this go under the big family of undefined behaviors, and most important is an error. The lifespan of an automatic variable is limited to its scope, and must not be used outside.

Anyway the behavior of values on the stack is generally stable because the memory manager preserves the data of stack pages (that's why doesn't make sense to use uninitialized local variables as random values), but in future architectural designs the MM can discard unused memory and don't save it making effectively undefined the contents of these memory locations.

Frankie_C
  • 4,764
  • 1
  • 13
  • 30