Punteros

13. Problema con el ámbito de una variable

El siguiente código compila sin problemas pero está mal.


#include<iostaream>
using namespace std;
double* asignar(void);//Prototipo int main(){ double* pMain; pMain=asignar(); *pMain=1.0; return 0; } double* asignar(void){ double variableLocal; //esta variable solo existe dentro de función asignar. return &variableLocal; //regreso esta variableLocal a main PASANDO LA DIRECCIÓN (VER QUE ESTA &) //pero al abandonar esta funcion esa DIRECCION ES LIBERADA!! }

Si compilamos lo va a hacer con éxito, pero obtendríamos Warning:

¿Donde está el problema?

El problema aquí es que variableLocal está definida solo en el ámbito de la función asignar().

Por ello, en el momento en que la dirección de variableLocal es retornada a main(), la misma apunta a una variable que ya no existe. La memoria que variableLocal solía ocupar probablemente ya esté siendo usada por otro proceso.

Esto es un error muy común. Desafortunadamente, este error no causa que el programa se detenga. De hecho, el programa funcionará la mayoría de las veces. El programa seguirá funcionando mientras la memoria que ocupaba variableLocal no sea reutilizada.

Este tipo de problemas intermitentes son los más difíciles de detectar y solucionar.

Solución: utilizar el montón o Heap (new)

El problema del ámbito es debido a que C++ devuelve la memoria local antes de que el programa finalice. Lo que se necesita es un bloque de memoria controlado por el programador.

Utilizar memoria mientras se considere necesario y no  devolverla porque C++ se pensó que era una buena idea.

Estos bloques de memoria se denomina heap (montón o pila). La memoria del montón se solicita con el comando new seguido del tipo de objeto que se desea guardar en esa memoria. El caso anterior se podría rescribir de la siguiente manera:

double* asignar(void);//Prototipo
    int main(){
    double* asignar();
    *pMain=1.0;
    delete pMain;//Libero la memoria con delete
    pMain=0;
    return 0;
}

double* asignar(void){
    double* pLocal = new double; //solicito memoria con new
    return pLocal;
}

A pesar de que pLocal deja de existir cuando se retorna a main(), la memoria a la que apuntaba sigue perteneciendo al proceso, esto por que se solicitó la memoria con new.

La memoria solicitada con new no vuelve al montón hasta que se lo haga de manera explícita con el comando delete.

En el ejemplo anterior se almacena una valor double en el puntero devuelto por la función asignar(). Cuando ya no se utiliza la memoria se la devuelve al montón en main() a pesar de haber sido solicitada en otro ámbito. Por último se "anula" el puntero. Esto no es necesario pero puede resultar útil ya que acusaría error si accidentalmente queremos volver a usar *pMain después del delete.

Hay que destacar que delete devuelve la dirección de memoria, sin importar con que puntero se apunte a ella o en qué ámbito nos encontremos. 

La memoria solicitada con new es válida y accesible en todo el programa/proceso hasta que termine el programa o proceso o que se libere con delete.