0

x is local variable and should go out of scope after an execution of fun() is over.
Its address is made available via a returned pointer and a global pointer p, which points to something which is not valid anymore. But still, output printed is 5.
Why?

#include <stdio.h> 

int *p = NULL;

int *fun() { 
    int x = 5;  
    p = &x;
    return p; 
} 

// Driver Code 
int main() { 
    printf("%d", *(fun())); 
    return 0; 
} 
chqrlie
  • 131,814
  • 10
  • 121
  • 189
Steve
  • 27
  • 4
  • 1
    Its an undefined behaviour. – Eraklon Jan 17 '21 at 13:01
  • 1
    `p` points to a memory location. That memory location has not yet been overwritten. – larsks Jan 17 '21 at 13:01
  • Hi Steve, I did some editing on your post and got carried away a little. I still think it describes the code you show and is what you mean. Please double check. In case I broke what you mean please accept my apology and feel free to undo. – Yunnosch Jan 17 '21 at 13:12
  • 1
    compile with -Werror -O3 (-O2 or -Os) Then your problem solved. Will not compile :) https://godbolt.org/z/ssEE7P – 0___________ Jan 17 '21 at 13:18
  • @P__JsupportswomeninPoland I teeter between "Cool, that achieves what OP was expecting." and "No, that will hide the problem and/or teach the wrong lesson that C will tell you all the things you foul up, if you configure it strictly." But if you create an answer with that I promise to upvote. And I suspect you'd get the "accept" tick from Steve. – Yunnosch Jan 17 '21 at 13:22
  • 1
    @Yunnosch if someone is saying that UB works, the there is not hope for him :). – 0___________ Jan 17 '21 at 13:27
  • @P__JsupportswomeninPoland Was not me! ;-) If you mean somebody else then I agree. – Yunnosch Jan 17 '21 at 13:33

3 Answers3

4

It is out of scope.
Still accessing it is not valid and causes undefined behaviour.
Just because you happen to read a "recognisable" value from there does not mean it is not out of scope.

Anything, including but not restricted to calling another function with local variables might/will change the value you read there. That does however not mean that as long as you do not do those things you are allowed to use/read/write.

Yunnosch
  • 26,130
  • 9
  • 42
  • 54
2

I don't know the implementation details of the compiler, but I guess it's because the stack space that holds x is still not erased by some other variables, because you only have this one function fun() invoked once.

Please compare the following two pieces of code

  1. The following code outputs 5\n4\n

In this code, each printf invocation causes the function result flushed to screen. Similarly, the stack space that holds x is still not erased by some other variables, so you can see the variable value being printed out.

#include<stdio.h>
int *p = NULL;
int *fun(int y)
{
    int x = y;
    p= &x;
    return p;
}

// Driver Code 
int main()
{
    printf("%d\n", *(fun(5)));
    printf("%d\n", *(fun(4)));
    return 0;
}
  1. The following code outputs 4\n168558721\n

In this code, you can change line 15 to print *r first. You'll still get 4. You'll never get 5. When the translated instructions from line 14 to line 15 are executed, pointer q and pointer r must have pointed to the same space. As we are invoking the same function, so the stack frame for line 13 and line 14 should have same structure, thus x is allocated under the same address.

#include<stdio.h>
int *p = NULL;
int *fun(int y)
{
    int x = y;
    p= &x;
    return p;
}

// Driver Code 
int main()
{
    int *q = fun(5); // line 13
    int *r = fun(4); // line 14
    printf("%d\n", *q); // line 15
    printf("%d\n", *r);
    return 0;
} 
Jeremy
  • 86
  • 6
0

Local variables are allocated in the stack. When you call fun() from main() the stack appears as something like:

+---------------+ <---- Stack pointer  
|   local var x |  
+---------------+ <---- Address of 'x'  
| Return addr   |  
|  in main()    |  
+---------------+  
|Local vars of  |  
|   main()      |  
+---------------+  
|     ...       |  
+---------------+  

When you go back to main(), the local variables, returned address and parameters are popped from the stack. But the stack is not cleared (by the way, it would consume too much CPU!). So, only the stack pointer moves:

+---------------+
|   local var x |
+---------------+ <---- Address of 'x'
| Return addr   |
|  in main()    |
+---------------+ <---- Stack pointer moved with the pops
|Local vars of  |
|   main()      |
+---------------+
|     ...       |
+---------------+

Everything which is above the stack pointer is considered invalid even if it is not cleared. So, it is why that you are lucky enough to get the value of x in the main() function.

But let's say that you call another function right after fun():

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

int *p = NULL;

void fun2()
{
  int var = 18;
  int var2 = 43;

  printf("fun2() called, var@%p=%d, var2@%p=%d\n", &var, var, &var2, var2);
}

int *fun() 
{ 
  int x = 5;  
  p= &x;
  return p; 
} 

// Driver Code 
int main(int argc, char *argv[]) 
{ 
  int *px;

  px = fun();
  printf("x@%p=%d\n", px, *px);
  if (argc != 1) {
    fun2();
  }
  printf("x@%p=%d\n", px, *px); 
  return 0; 
} 

When the program does not call fun2(), it behaves as yours but I added the display of the address of x:

$ gcc try.c -o try
$ ./try
x@0x7ffd5beb5f04=5

When the program is passed any parameter, we call fun2() after fun() and we display x before and after the call to fun2():

$ ./try any_param
x@0x7ffeadacc084=5
fun2() called, var@0x7ffeadacc080=18, var2@0x7ffeadacc084=43
x@0x7ffeadacc084=43

We can see that the value of x is changed to 43 after the call to fun2() because, the local variable var2 in fun2() has been put at the same place as x when fun() was running. Hence, the same address 0x7ffeadacc084 in the stack and of course the new value 43 of x which is in fact the value of var2.

Here is how the stack looks like after calling fun2() (the former data of _fun() have been overwritten by the data of fun2()):

+---------------+
|local var var  |
+---------------+ <---- Address of 'var' = 0x7ffeadacc080
|local var var2 |
+---------------+ <---- Address of 'var2' = 0x7ffeadacc084
| Return addr   |
|  in main()    |
+---------------+ <---- Stack pointer moved with the pops
|Local vars of  |
|   main()      |
+---------------+
|     ...       |
+---------------+

PS: The stack grows from the high to low addresses. Hence, var is located at an address lower than the address of var2.

Rachid K.
  • 4,490
  • 3
  • 11
  • 30