nktsg
nktsg

Reputation: 143

C program stack

#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?

Upvotes: 0

Views: 91

Answers (4)

Frankie_C
Frankie_C

Reputation: 4877

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.

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 311088

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.

Upvotes: 2

paulsm4
paulsm4

Reputation: 121809

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".

Upvotes: 3

PhilMasteG
PhilMasteG

Reputation: 3185

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!

Upvotes: 3

Related Questions