Duda con variable "fuera de alcance"[SOL]

Iniciado por Leber, 25 Noviembre 2010, 16:20 PM

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

Leber

Hola, tengo una duda:

Hable con alguien de un foro y me dijo lo siguiente:

Supongamos el siguiente codigo:

#include <stdio.h>

struct datos {
   int p;
};

void showme(struct datos *dat)
{
   /*
       Segun me explicaron, aqui deberia dar un fallo de segmentacion, ya que la estructura datos solo existe en la pila de la funcion prepara.
       Pero por lo que tengo entendido, las variables de cada funcion solo son desasignadas cuando se sale de ellas, pero no cuando se pasan por parametro a otra funcion.
   */
   printf("%d\n", dat->p);
}

void prepara(void)
{
 struct datos datos;

   /*
       codigo
       */


   datos.p = 5;
   showme(&datos);
   return;
}

int main(void)
{

   prepara();

   return 0;
}


He creido oportuno explicar mi duda en medio del cogido, así quiza se vea más claro.

Mi duda basicamente radica en que:

Cuando se llama a una funcion, tanto los parametros que se le pasan como sus variables estaticas son puestas en su pila. Y que estas variables quedan en esa pila hasta que se sale de dicha funcion.
Esto significa, que aunque yo llame a otra funcion pasandole un parametro de la función en la que estoy, esta variable no tiene que dejar de existir hasta que se salga de la funcion donde se declaro, ¿es eso cierto?

El codigo de ejemplo funciona, pero me dijeron que a veces podría funcionar y a veces no.

Gracias de antemano
"Solo los tontos carecen de preucupaciones." Johann Wolfgang Goethe

do-while

¡Buenas!

Asi a bote pronto, no veo ningun error en el codigo, asi que deberia fucnionar siempre.

Lo de la pila es cierto, primero se ponen los parametro, luego la direccion de retorno y por ultimos las variables locales. Esto cada vez que llamas a una funcion. Por eso se recomienda utilizar la recursion con cautela, para no llenar la pila con todos esos datos cada vez que se llama a la funcion. Y una vez que se regresa de la funcion, los datos "desaparecen" de la pila.

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

Eternal Idol

Hasta justo despues del return; de la funcion prepara la variable datos no sale del ambito y puede ser usada, lo que no podes hacer por ejemplo es retornarla a main o pasarla a un hilo sin esperar dentro de prepara a que el hilo termine.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

Leber

Gracias a ambos por la contestación.

Lo que no acabo de tender es esto, imaginemos el siguiente codigo:
int pon(void)
{
    int i = 2;

    return i;

}

void pon2(void)
{

    char buffer[BUFSIZ];
    int i = 0;

    for(; i < BUFSIZ -1 ; i++)
        buffer[i] = 'a';

}

int
main(int argc, char **argv)
{
    int a = 3;


    a = pon();
    printf("%d\n", a);
    pon2();
    printf("%d\n", a);

    return 0;
}


Segun lo que tengo entendido, al llamar la funcion pon(), se pone en la pila sus parametros, la dirección de retorno y las variables automáticas.
Una vez se sale de la función, el espacio que se guardo para las variables de la función se libera, y entonces según esto no debería poder ver el valor de la variable "i", no?

Quiero decir, estoy devolviendo una variable que cuando salgo de la funcion, se "destruye". No logro comprender por qué puedo pasar su valor.
Llevo algunos días liado con esto, he leido algun que otro artículo acerca de como funciona el stack y creo entenderlo, pero no comprendo este comportamiento.

He intentado crear otra función con un poco mas de almacenamiento, para a ver si así dejaba de ver el valor de "i", pero no.

Muchas gracias de antemano


"Solo los tontos carecen de preucupaciones." Johann Wolfgang Goethe

Littlehorse

En tu ejemplo no ves el valor de la variable i, ves el valor que tenia i y ahora es de a. Las variables locales a la función desaparecen del stack justo luego del return, por tanto no te impide retornar un dato por valor y asignarlo a otra variable en otro contexto de función. Si te impide retornar una referencia a una variable local y trabajar con esa referencia desde otro contexto, puesto que esa referencia apuntaría a un lugar donde los datos útiles podrían ya no existir.

Por ejemplo:


#include <stdio.h>

int* Test1(void)
{
int i=2;

return &i; //Retornando direccion de una variable local (Deberia dar un warning)
}

void Test2()
{
char Buff[20000];
}

int main()
{
int *Ptr;

Ptr=Test1();
//Ptr apunta a la direccion de i. i==2, ya no es valido en este punto. La direccion a la cual apunta Ptr puede contener datos inservibles.
Test2();

printf("%d",*Ptr); //Imprime dato basura
return 0;
}



#include <stdio.h>

int Test1(void)
{
int i=2;
printf("Local Address, i: %x\n",&i);
return i; //Retorno el valor de i.
}

void Test2()
{
char Buff[20000];
}

int main()
{
int Var;

Var=Test1();
//i==2, ya no es valido. En la direccion donde se encontraba i solo hay datos inservibles.
//El valor que tenia i, ahora se encuentra en Var.
Test2();

printf("Local Address, Var: %x\n",&Var);
printf("Dato Var: %d",Var); //Imprime dato correcto
return 0;
}


Saludos
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Leber

Gracias Littlehorse, me has aclarado bastante el tema.


Entonces, si lo he entendido bien sería algo asi:

pila cuando llamas a la funcion:

funciones_automaticas
------------
direccion_a_donde_retornar
------------
parametros_si_hay



pila antes de salir de la funcion:

valor_de_i (2)
-------------
direccion_a_donde_retornar



Si estoy en lo cierto, lo que se copia en la pila antes de salir de la función, es el valor que contiene i, y no la variable i como tal.
Por otro lado, cuando devuelves a i por referencia, lo que estas haciendo en realidad es devolver la dirección de memoria donde se encuentra i, y como esos datos son machacados al salir de la pila, en la dirección de i puede haber cualquier cosa.

Lo que no entiendo muy bien es en que casos se usa ese metodo. He leido bastantes manuales de C, he visto bastante codigo, y jamás he visto devolver a una variable por referencia. Me suena de que en C++ se hace para según que necesidades, pero en C no se donde radica su uso. ¿Quizá era solo a modo de ejemplo?

Gracias =), se me va iluminando la cosa.
"Solo los tontos carecen de preucupaciones." Johann Wolfgang Goethe

Littlehorse

CitarLo que no entiendo muy bien es en que casos se usa ese metodo. He leido bastantes manuales de C, he visto bastante codigo, y jamás he visto devolver a una variable por referencia. Me suena de que en C++ se hace para según que necesidades, pero en C no se donde radica su uso. ¿Quizá era solo a modo de ejemplo?
Te refieres a retornar una dirección? la verdad es que es difícil encasillar un retorno de una dirección en un solo lugar.

Básicamente si tienes una función que retorna una dirección, la idea es mas o menos la misma. La diferencia radica en que en este caso retorne una dirección de una variable local al contexto de una respectiva función, la cual desaparece justo luego del return, mas que nada para ejemplificar el porque te funcionaba lo que mencionaste en primera instancia y el porque esta mal.

En el archivo de cabecera string.h, tenes varias funciones que retornan direcciones. Podes revisarlas y veras un pequeño espectro de para que se puede utilizar, pero la verdad es que se pueden usar con múltiples objetivos, al igual que los punteros se pueden utilizar para infinidad de cosas, lo mismo aplica para el retorno de una dirección, ya que en reglas generales son conceptos que van ligados.

Ten en cuenta que a pesar que el pasaje por referencia y el pasaje por valor son ideas que se encuentran tanto en C como en C++, cuando alguien menciona referencia en C++, puede referirse a algo distinto que un puntero y una dirección.

Saludos
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Leber

#7
Cita de: Littlehorse en  2 Diciembre 2010, 19:50 PM
En el archivo de cabecera string.h, tenes varias funciones que retornan direcciones. Podes revisarlas y veras un pequeño espectro de para que se puede utilizar, pero la verdad es que se pueden usar con múltiples objetivos, al igual que los punteros se pueden utilizar para infinidad de cosas, lo mismo aplica para el retorno de una dirección, ya que en reglas generales son conceptos que van ligados.

He mirado el fichero string.h, pero sinceramente no he podido encontrar lo que decias. Quizás es que yo ya me estoy confundiendo, pero es lo mismo retornar un puntero que una variable por referencia, me refiero a algo asi:

int *ejemplo(void)
{
   int *i = NULL;

   i = (int *)malloc(1*sizeof(int));
   *i = 4;

   return i;

}

int *ejemplo2(void)
{
   int i = NULL;

   i = 4;

   return &i;

}


Se que no es es muy buen ejemplo, ya en el primer ejemplo la variable i se crea en el heap, i en el segundo en el stack de la funcion. Lo que venia a decir es que en ambas funciones estamos retornando direcciones de memoria, no?

Aun así, muchas gracias, me quedaron claros algunos pilares basicos.

Saludos
"Solo los tontos carecen de preucupaciones." Johann Wolfgang Goethe

Littlehorse

#8
CitarQuizás es que yo ya me estoy confundiendo, pero es lo mismo retornar un puntero que una variable por referencia, me refiero a algo asi:
Si. Básicamente es lo mismo en reglas generales, pero puede haber diferencias técnicas dependiendo del caso.

Citar
Se que no es es muy buen ejemplo, ya en el primer ejemplo la variable i se crea en el heap, i en el segundo en el stack de la funcion. Lo que venia a decir es que en ambas funciones estamos retornando direcciones de memoria, no?
La variable i es un puntero que se encuentra en el stack, es decir, luego del return el puntero desaparece, lo que no desaparece es la memoria que reservaste con malloc (que por cierto es un ejemplo de una función que retorna una dirección).

Obviamente el contenido del heap si es accesible desde otro contexto, por lo tanto el contenido que tenga el entero no se pierde, si se pierde el puntero con el cual estabas manejando ese contenido. Si no retornas la dirección, perdes el puntero y también perdes la dirección de la memoria que reservaste, y con esto, la posibilidad de liberar esa memoria.

stack||||heap
  i----->block



El otro caso es diferente, porque retornas una dirección pero de una variable que se encuentra en el stack y ya no es accesible desde otra función. Pero a grandes rasgos, en ambas retornas direcciones.

Saludos!

#Edit:
Para poner codigo:
C
[code=c]codigo[/code]
C++
[code=cpp]codigo[/code]
An expert is a man who has made all the mistakes which can be made, in a very narrow field.

Leber

Gracias Littlehorse, me ha quedado todo bastante claro.
Hace un tiempo me entraron dudas de este estilo, y decidi aprender lenguaje asm para poder debuguear bien una aplicación, ver lo que ocurría paso a paso y estas cosas, pero desgraciadamente todavia no dispongo del tiempo suficiente.

Aun así, me has aclarado bastantes dudas, ahora se un poquito más =).
Pongo [SOL] en el estado, como para decir que esta solventado.

Gracias, y saludos!
"Solo los tontos carecen de preucupaciones." Johann Wolfgang Goethe