No devolver nunca punteros a variables locales a una función en c

Iniciado por eduu15, 11 Abril 2018, 00:35 AM

0 Miembros y 1 Visitante están viendo este tema.

eduu15

No entiendo muy bien este concepto y he estado pensando acerca del tema.
CitarAclarar que el codigo funciona pero no estoy seguro de que sea correcto



#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;
}


Si he comentado mal el programa por favor comentenlo, yo creo que aunque funcione seria incorrecto, porque una vez acabe la funcion, puntero no sabra a donde apuntar y podrian sobreescribirse direcciones de memoria. Ustedes que opinan?

Yuki

Efectivamente la variable "p" apunta a la pila que puede ser sobreescrita.

Es más, creo que si establecieras un par de argumentos más a "printf" el valor seria reemplazado.

eduu15

Acabo de hacer la prueba y no se esta sobreescibiendo probado en windows y ubuntu curioso el tema

Yuki

Estuve depurando y resulta que tu función retorna el valor "9" entero, no un puntero a la variable.

El ejemplo correcto seria el siguiente:

#include <stdio.h>

int funcion ()
{
    int resultado=9; //variable local de la funcion, al acabar la funcion se pierde el dato
    return (int)&resultado;
}

int main(int argc, char *argv[])
{
    int* p = (int*)funcion();
    printf("%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,%i",1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,*p); //9

    return 0;
}


Que de todas formas no funciona debido a la manera que tiene el compilador de generar el código.

¿Como lo hace? Básicamente preserva TODA la memoria necesaria en la pila para empujar los argumentos al arrancar el main, esto evita que se corrompa la pila al invocar funciones privadas y de tipo CDECL.

Esto es con DevC++.


MAFUS

Como te han dicho no has devuelto el puntero sino el valor al que apunta. Eso ha pasado por añadir el asterisco a la variable del return. Todavía tienes ahí lío con los operadores de punteros.

Ahora cuidado porque ese valor devuelto ha sido recogido por el puntero p de main y eso quiere decir que ahora apunta a la dirección 9. El programa ha funcionado porque en printf le has pedido la dirección a la que apunta, de haber querido acceder a su contenido el S.O. te habría detenido.

Respondiendo a tu pregunta: cuando se llama a una función previamente se forma un marco de esa función en la pila dónde se encuentran los argumentos, la dirección de retorno y todas las variables locales y el puntero de pila se mueve al final de todo. Cuando una función termina se devuelve el puntero de pila al inicio de todo eso, eso es al final de la función llamante, por esa razón esa memoria que hay después ya no es accesible.

Aunque eso último es mentira. Si devuelves un puntero a una variable local sigues apuntando en esa dirección de memoria que el sistema ya no puede tocar. Es posible y se te permite trabajar allí y mientras no llames a otra función esa memoria no se va a tocar y te parecerá válido lo que hagas. En el momento que llames a otra función harás que la pila aloje datos allí perdiendo todo loque hayas hecho.

Y sí, si me pasas ese puntero a la función puede corromperse a sí misma pues tendrá un puntero que apunta a una zona indeterminada de su marco.

eduu15

#6
Entonces la conclusion es que aunque el SO te lo permita mejor no hacerlo

Aunque la primera funcion no tenga sentido ya que devuelve un int no un puntero a int esta parte

return *puntero; es como si hiciese return resultado no? estaria desreferenciando el puntero

MAFUS

Así es, dereferencias el puntero antes de devolverlo.
Pero lo dicho. Lo recibe un puntero así que a partir de ese momento estás apuntando a la dirección 9 de la memoria.