-1

I have a question about returning a local variable from a function by address. I understand that there is a lifetime for every local variable. But when I return an array and straight print for example the first and second cell I still get the values. But when I add only one line it is already destroyed. The question is why this does not happen right once the function closes.

int* f1();
int main(){
   int *p=f1(); 
   printf("%d %d\n",p[0],p[1]);
   return 0;
}
int* f1(){  
   int arr[4]={1,2,3,4};    
   return arr;
}

output: 1 2

int* f1();
int main(){
   int *p=f1();
   printf("hey\n"); // **add this line**
   printf("%d %d\n",p[0],p[1]);
   return 0;
}
int* f1(){  
   int arr[4]={1,2,3,4};    
   return arr;
}

output: 1 11788742452

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 3
    You can't return a pointer to a local variable. The local variable ceases to exist as soon as the function is finished. What you see is _undefined bahaviour_. You've seen a leftover that can be overwritten at any moment. In the second program you can see that the added printf has overwritten the leftover – Jabberwocky Jul 22 '21 at 15:41
  • 1
    As you wrote, the array is no longer valid. You were so unlucky to get the result you wanted. Sh**t happens. Undefined behaviour has no guarantees: the result can be right, wrong, or break the sky. Typically, you'll get the wanted result *if there is no reason why the memory was overwritten*, which could be any number of things. – Weather Vane Jul 22 '21 at 15:42
  • I know I must not return a pointer of a local variable, the question is why when I return and print straight the first and second cell I still get the normal values, But when I add a line of sample print and then print the value of the first and second cell I get junk values. @Jabberwocky – Roni Jack Vituli Jul 22 '21 at 15:44
  • 1
    In one case the memory was reused, in the other it wasn't (yet). Side note: if you run a red light, you don't always get squashed by a truck. – Weather Vane Jul 22 '21 at 15:45
  • @רוניג'קויטולי refresh the browser and read my updated comment – Jabberwocky Jul 22 '21 at 15:46
  • @רוניג'קויטולי read this: https://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope. It's fun to read and very informative – Jabberwocky Jul 22 '21 at 15:47
  • It's also discussed in [Why isn't the local variable going out of scope?](https://stackoverflow.com/questions/65760864/why-isnt-the-local-variable-going-out-of-scope) – Weather Vane Jul 22 '21 at 15:53
  • See also [Pointer to local variable?](https://stackoverflow.com/q/4570366/15168) – Jonathan Leffler Jul 22 '21 at 16:05

3 Answers3

0

The array arr declared in the function f1

int* f1(){  
   int arr[4]={1,2,3,4};    
   return arr;
}

has automatic storage duration. It means that after exiting the function it will not be alive. So the returned from the function pointer will be invalid and dereferencing the pointer in main invokes undefined behavior.

You could return a pointer to the first element of the array if it had static storage duration as for example

int* f1(){  
   static int arr[4]={1,2,3,4};    
   return arr;
}

Another approach is to allocate the array dynamically within the function like

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

//...

int* f1(){
    int *arr = malloc( sizeof( int[4] ) );  

    if ( arr != NULL )
    {
        memcpy( arr, ( int[] ) { 1, 2, 3, 4 }, sizeof( int[4] ) );
    }
   
    return arr;
}

In this case you will need free the allocated array in main when it will not be used any more as

int *p=f1();
//...
free( p );
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

Here is what happens if you write similar code in assembly language:

  • main calls f1.
  • f1 reserves some space on the stack (by adjusting the stack pointer) and puts 1, 2, 3, and 4 there.
  • f1 puts the address of that space in the register used to return a value.
  • f1 pops the stack (by adjusting the stack pointer the opposite way), which means the memory is no longer reserved.
  • f1 returns to main.
  • main uses the return value to load data from memory. Since nothing has changed the memory, this works.
  • main prepares to call printf and passes it the data it loaded.
  • Calling printf and executing printf use the stack. This changes the data there.
  • printf returns to main.
  • When main uses the earlier return value to load data from memory after printf has been called, the memory in the stack area has been changed. So the load gets the new changed data, not the old data from f1.

When you use C for this code, the compiler follows rules that are specified abstractly in the C standard. If it just mechanically generated assembly code such as the above, you would see the behavior described above. However, the C standard defines an abstract lifetime of the array in f1 and says that lifetime ends when the function returns. It further says the value of a pointer becomes indeterminate when the lifetime of the object it points to ends. As the compiler compiles your code, it seeks to optimize the code it generates. Instead of mechanically generating assembly, it tries to find the “best” code that performs the defined behavior of your program. If you use a pointer with indeterminate value or try to access an object whose lifetime has ended, there is no defined behavior of your program, so optimization by the compiler can produce results that are unexpected to new programmers.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
-1

Clearing memory actually means that the program no longer have control over that part of the memory. Its now at operating system's disposal. OS can use that memory to do anything.
So in this case,

int* f1();
int main(){
   int *p=f1(); 
   printf("%d %d\n",p[0],p[1]);
   return 0;
}
int* f1(){  
   int arr[4]={1,2,3,4};    
   return arr;
}

After the execution of the function f1() ends, the OS gets back the control over the memory of arr. And it can assign that memory to some other program. Unfortunately, the os assigned no task in that memory during that little time period. Thats why it was not overwritten and you could get the info that was in that memory.
Memory is used, released and then overwritten.

In the second case, OS used that memory to run printf() statement or some other thing. So that memory was overwritten.
Formal term is: memory use and release.

Daoist Paul
  • 133
  • 8