2008-11-08 8 views
20

Estoy empezando de nuevo con C++ y estaba pensando en el alcance de las variables. Si tengo una variable dentro de una función y luego devuelvo esa variable, ¿la variable no estará "muerta" cuando se devuelve porque el alcance en el que se encontraba ha finalizado?Alcance y valores devueltos en C++

He intentado esto con una función que devuelve una cadena y funcionó. ¿Alguien puede explicar esto? O al menos dirígeme a algún lugar que pueda explicarme esto por favor.

Gracias

Respuesta

37

Cuando la función termina, los siguientes pasos ocurren:

  • valor de retorno de la función es copia en el marcador de posición que estaba poner en la pila para este fin.

  • Todo después del marco de la pila se quita el puntero. Esto destruye todas las variables locales y argumentos.

  • El valor de retorno se extrae de la pila y se asigna como el valor de la función. Si el valor de la función no está asignado a nada, no se realiza ninguna asignación y se pierde el valor .

  • La dirección de la siguiente instrucción para ejecutar se extrae de la pila, y la CPU reanuda la ejecución en esa instrucción.

The stack and the heap

+0

¡Es un buen artículo! Lo marqué como favorito para compartirlo con personas que aprendieron sobre la pila :) –

+3

El estándar C++ no tiene noción de una pila. – fredoverflow

+2

@CMS Tengo curiosidad por saber si hay una sección en el estándar de C++ que admita esta afirmación. Por lo que yo sé, esta información sobre la pila generalmente es parte de la convención de llamadas de la arquitectura de CPU específica y no tiene que ser la misma para cada plataforma disponible. – MKroehnert

2

La variable local se copia en el valor de retorno. Los constructores de copia se llaman para clases no triviales.

Si devuelve un puntero o una referencia a una variable local, tendrá problemas, tal como lo sugirió su intuición.

3

Cuando devuelve un valor, se realiza una copia. El alcance de la variable local finaliza, pero se realiza una copia y se devuelve a la función de llamada. Ejemplo:

int funcB() { 
    int j = 12; 
    return j; 
} 

void A() { 
    int i; 
    i = funcB(); 
} 

El valor de j (12) se copia y se volvió a i lo que voy a recibir el valor de 12.

3

Realmente depende de qué tipo de variable que se está devolviendo. Si devuelve una primitiva, se devuelve mediante copia, no por referencia, de modo que el valor se copia en la parte superior de la pila (o, más a menudo, se coloca en un registro) donde la función llamante puede obtenerlo. Si asigna un objeto o memoria en el montón y devuelve un puntero, entonces no muere porque está en el montón, no en la pila. Sin embargo, si asigna algo en la pila y lo devuelve, sería algo malo. Por ejemplo, cualquiera de estos sería muy malo:

int *myBadAddingFunction(int a, int b) 
{ 
    int result; 

    result = a + b; 
    return &result; // this is very bad and the result is undefined 
} 

char *myOtherBadFunction() 
{ 
    char myString[256]; 

    strcpy(myString, "This is my string!"); 
    return myString; // also allocated on the stack, also bad 
} 
+0

De hecho, estoy regresando una cadena .. no es exactamente una matriz, pero estoy declarando que en la función de este modo: funcnam cadena (parms ...) { prueba de la cuerda; cadena de retorno; } Y lo está haciendo bien – AntonioCS

+0

Si se trata de un objeto de cadena real, funciona bien porque se asigna en el montón, no en la pila. En el ejemplo anterior, myString no es realmente un objeto, solo una matriz de caracteres estándar. El artículo Stack and Heap de CMS es muy bueno, deberías leerlo :) –

3

Sólo por un poco más de una explicación orientada memoria de modelo: cuando una función es llamada, un espacio temporal se hace para la función de poner sus variables locales, llamado marco. Cuando la función (llamada) devuelve su valor, pone el valor de retorno en el marco de la función que lo llamó (llamador), y luego se destruye el marco invocador.

La parte "el marco está destruido" es la razón por la que no puede devolver punteros o referencias a variables locales de las funciones. Un puntero es efectivamente una ubicación de memoria, por lo que devolver la ubicación de memoria de una variable local (por definición: una variable dentro del marco) se vuelve incorrecta una vez que se destruye el marco. Como el marco invocable se destruye tan pronto como devuelve su valor, cualquier puntero o referencia a una variable local es inmediatamente incorrecto.

0

Esto depende del tipo de artículo devuelto. Si regresa por valor, se realiza una nueva copia de la variable para devolverla a la persona que llama. Me adelgaza caso de que no necesita preocuparse de duración del objeto, pero puede que tenga que preocuparse por los costos de los objetos de copia (pero por favor no lo hacen de forma prematura optimze - corrección es mucho más importante):

std::string someFunc(std::string& const s) 
{ 
    return s + "copy"; 
} 

Si el función devuelve una referencia, luego debe tener cuidado con lo que está devolviendo porque su vida útil debe extenderse más allá de la duración de la función y la persona que llama no necesariamente podrá delete si está usando new para crear el objeto:

std::string& someFunc2(std::string const& s) 
{ 
    return s + "reference to a copy"; // this is bad - the temp object created will 
             // be destroyed after the expression the 
             // function call is in finishes. 
             // Some, but not all, compilers will warn 
             // about this. 
} 

Por supuesto, los indicadores de devolución tendrán consideraciones de por vida similares.

Cuestiones relacionadas