Never return pointers to variables local to a function

Question:

I don't really understand this concept and have been thinking about it. Clarify that the code works on both windows and ubuntu but I'm not sure it's correct.

#include <stdio.h>

int *funcion ()
{  
    int resultado=9; //variable local de la funcion, al acabar la funcion se pierde el dato
    int *puntero=&resultado; //cuando termine la funcion puntero no sabra a donde apuntar

    return *puntero; //devuelvo el valor de resultado
}

int main(int argc, char *argv[])
{
   int* p=funcion();
   printf("%i",p); //9

   return 0;
}

If I have wrongly commented on the program, please comment on it.

I think that even if the program works it would be incorrect, because once the function finishes the local variables stored in the stack are destroyed, the puntero not know where to point and memory addresses could be overwritten. What do you think?

Answer:

The problem with returning a pointer to a local variable is that, by default, the storage of local variables is on the stack, but the stack is reused over and over each time you call other functions.

Therefore, if you try to access the returned address from outside that function, what you find there is basically unpredictable. If the stack was not reused yet, perhaps the original variable value is still there. If it has been reused there will be another variable. It can even depend on the compiler or the operative (see update below).

One way to solve the above is to put the word static front of the variable declaration. A) Yes:

int *funcion() {
   static int dato;
   dato = 20;
   return &dato;
}

This static modifier causes the compiler to reserve space for this variable in the program's data segment, rather than on the stack. The data segment is where global variables are normally stored, so in a sense this dato variable is as if it were global, only it is not visible from outside the function. As it is not stored on the stack, it has a fixed memory address that can be returned. Also, it is not "cleared" once the function returns (the next time you call that function, the variable will still have what you saved in the previous run, so it can be used to store things like a count of how many times it was call the function, without resorting to global variables).

Using static the behavior is already predictable and the warning that you are returning a pointer to a local variable disappears. However, traps like this can still haunt you:

#include <stdio.h>

int *funcion(int valor) {
   static int dato;
   dato = valor;
   return &dato;
}

int main(){
   int *resultado1;
   int *resultado2;
   resultado1 = funcion(5);
   resultado2 = funcion(10);
   printf("resultado1 contiene %d\n", *resultado1);
   printf("resultado2 contiene %d\n", *resultado2);

   return 0;
}

What would you expect on screen? Maybe it's not what comes out … check it out. If you got it right, you already have it clear 🙂

Update: what happens if I remove static from the above example? A puzzle

What happens depends largely on the compiler and operating system you are testing on. I have tested on three different platforms with the following results.

  • On Ubuntu server 16.04, with gcc 5.4.0 the program "breaks" with a segment violation. This occurs because apparently gcc has decided that if a function returns a pointer to its stack, instead of returning that address, the compiler changes it to 0. That is, the function returns a null pointer. Attempting to use it afterwards to see its value causes the segfault .
  • In OSX (Darwin 17.5.0), with gcc 4.2.1 (Apple version 9.10), the program does not break, and always shows 10 in the first printf() and 0 in the second.
  • On Ubuntu server 14.04 with gcc 4.8.4, the program does not break either and shows 10 in the first printf() and a different number each time (around 32600) in the second printf() .

The behavior of the last two cases deserves an explanation.

When we omit static , the address we return is an address on the stack. However, the stack is an area of ​​memory that is reused. Every time we invoke a function, the parameters passed to it, the return address, and the registers that the compiler decides to stack as part of the input to the function are stored on the stack. On top of that data, space is created for the function's local variables. All of this information makes up what is called a "stack frame." If another function is called from that function, a new frame is created on top of the previous one. When a function returns, its stack frame is "freed", in the sense that the stack pointer goes back to the previous frame.

Let's review the code:

   resultado1 = funcion(5);
   resultado2 = funcion(10);
   printf("resultado1 contiene %d\n", *resultado1);
   printf("resultado2 contiene %d\n", *resultado2);

When we call funcion(5) for the first time, a stack frame is created with the parameter we pass to it, the return address (pointing to the next instruction inside main() ), the local variables of funcion() and the rest. of things that the compiler decides to put. The function copies its parameter (5) to its local variable and returns the address of that local variable, which points to an address within its stack frame. But when the function returns, that frame is "freed". As long as we don't call another function, the old frame is still in memory (because although it is freed, it is not erased), so we could still go to *resultado1 and see a 5 there.

However, when calling funcion(10) the second time, a new stack frame is created that overwrites the previous one, and therefore (and since in this case the stack frame will be identical in structure because I am calling the same function ), in the direction which pointed resultado1 is keep a pointer 10. resultado2 point to the same direction as resultado1 , so either show 10.

That is why, on both OSX and Ubuntu 16.04, the first printf() shows 10 instead of 5. But … why doesn't the second printf show 10 as well?

The reason is that by calling the first printf() we are invoking a function! And so we are rewriting the stack frame again . In the address direccion1 (or direccion2 , they are the same) we will now see whatever the first printf() needed to save in its local variables. Apparently in OSX it is always a zero, while in Ubuntu 16.04 it is a variable number (probably part of some pointer, since the linux loader randomizes the addresses in which the program loads and thus the pointer values ​​are different in each execution ).

Scroll to Top