Guía mínima de Ada 95

Punteros.

Introducción

     Los tipos puntero representan direcciones de memoria y son un importante mecanismo para llevar a cabo una gestión dinámica de la memoria, así como para crear estructuras de datos dinámicas complejas.

Declaración

    En Ada los punteros se denominan "accesos". Para declarar punteros que puedan referenciar a variables dinámicas se utiliza la palabra reservada access.
    type TPersona is record
        Nombre : string(1..20);
        Apellido1 : string(1..20);
        Apellido2 : string(1..20);
    end record;
    type TP_Persona is access TPersona; --Tipo de los punteros a TPersona dinámicos
    type TP_Integer is access integer; --Tipo de los punteros a integer dinámicos

    Pint1, Pint2 : TP_Integer; --Punteros a integer dinámico
    PPer1, PPer2 : TP_Persona; --Punteros a TPersona dinámicos
    i : integer; --Variable estática de tipo integer
    p : TPersona; --Variable estática de tipo TPersona

    Los punteros se inicializan automáticamente con el valor null. indicando que no referencian a ninguna variable.

Ubicación dinámica

    Para crear una variable dinámica se utiliza la palabra reservada new seguida del tipo de la variable que se quiere crear:
    Pint1 := new integer; --Se crea una variable dinámica de tipo integer
    PPer1 := new TPersona; --Se crea una variable dinámica de tipo TPersona

    Las variables creadas dinámicamente se pueden inicializar en su creación:
    Pint2 := new integer'(35);
    PPer2 := new TPersona'("Pepe      ","Pérez     ","Pérez     ");

    Además, una variable puntero puede ser inicializada en su declaración:
    Pint : TP_Integer := new integer'(35);

Acceso a las variables referenciadas por los punteros

    Para acceder o modificar el contenido de una variable referenciada por un puntero, se utiliza el cualificador all:
    i := Pint2.all; --Se asigna a i el valor de la variable referenciada por Pint2
    PPer1.all := PPer2.all;
      --Se asigna a la variable referenciada por PPer1 el valor
      --de la variable referenciada por PPer2
    Pint1 := Pint2; --Pint1 y Pint2 referencian a la misma variable
    i := Pint1; --ERROR, i es de tipo integer y Pint es de tipo puntero

    Cuando la variable referenciada es un record se puede acceder a cada campo utilizando simplemente el cualificador punto (.):
    p.Nombre := PPer1.Nombre;
    p.Apellido1 := PPer1.all.Apellido1; --El .all es inncesario

    Si la variable referenciada es un array se puede acceder a sus elementos individuales utilizando paréntesis del modo habitual:
    type TArray is array (integer range <>)  of integer;
    type TP_Array is access TArray;
    v : TP_Array := new TArray'(1,2,3,4,5);
    ...
    i := v(3);
 

Liberación de variables dinámicas

    En Ada, una variable dinámica se libera automáticamente cuando finaliza la ejecución de la parte del programa donde está definido el tipo puntero correspondiente. Por tanto, para realizar un control más eficiente de la memoria es necesario emplear operaciones de liberación explícita de las variables creadas dinámicamente cuando ya no sean necesarias. Para ello se necesita, primero, incluir "Unchecked_Deallocation" en la cláusula de contexto:
    with Text_io, Unchecked_Deallocation;

    A continuación, se debe instanciar el procedimiento genérico "Unchecked_Deallocation" para el tipo de dato y tipo de puntero que se quiere liberar:
    procedure Libera_Persona is new Unchecked_Deallocation(TPersona,TP_Persona);

    Ahora, se puede usar Libera_Persona para liberar variables dinámicas de tipo TPersona referenciadas por variables puntero de tipo TP_Persona:
    Libera_Persona(PPer1);

Punteros que apuntan a variables estáticas

    Las variables puntero almacenan direcciones. Todas las variables tienen una dirección; sería lógico querer que en una variable puntero a un tipo T se pueda almacenar la dirección no sólo de variables de tipo T creadas dinámicamente, sino también de variables estáticas de tipo T. En Ada los punteros sólo pueden almacenar direcciones de variables dinámicas, a menos que se declaren "access all":
    type PGen_TPersona is access all TPersona;
        --Las variables de tipo PGen_TPersona pueden referenciar
        --variables de tipo TPersona, tanto estáticas como dinámicas
    PGPer : PGen_TPersona;

    Para que la dirección de una variable estática pueda ser asignada a un puntero, ésta debe  permitirlo en su declaración, utilizando la palabra aliased:
    p1: aliased TPersona;

    La dirección de una variable declarada así puede obtenerse mediante el atributo access:
    PGPer := p1'access; --Asigna a PGPer la dirección de p1

    Se puede imponer restricción de lectura a un puntero, de forma que no se pueda modificar el objeto al que apunta, pudiendo entonces utilizarse para referenciar una constante, o para referenciar una variable de forma segura, con la garantía de que no podrá modificarse a través del puntero:
    type Punt_Const_Int is access constant integer; --Tipo de los punteros a enteros constantes
    ...
    type Punt_Int is access all integer; --Tipo de los punteros a variables integer (estáticos o dinámicos)
    PCI1,PCI2 : Punt_Const_Int;
    Pint3 : Punt_Int;
    i : aliased integer;
    ...
    PCI1 := i'access;
    Pint3 := i'access; --PCI1 y Pint3 son alias de i
    i := 3;
    Pint3.all := 4; --Modifica i
    PCI1.all := 5; --ERROR, no se puede modificar i a través de PCI
    PCI2 := new integer'(9); --Se puede hacer new siempre que se inicialice
 

Declaraciones incompletas de tipos

    Una aplicación frecuente de los punteros es la creación de estructuras de datos dinámicas, por ejemplo listas encadenadas. Este tipo de estructuras están formadas por un conjunto de nodos conectados mediante campos de enlace que son punteros a nodos del mismo tipo. Para estos nodos sería necesaria una definición de tipo tal como:
    type NodoLista is record
        Info: integer;
        Siguiente: PNodoLista;
    end record;

    donde PNodoLista es el tipo de los punteros a objetos de tipo NodoLista. Esto significa que debería existir una definición para PNodoLista:
    type PNodoLista is access NodoLista;

    Esta definición debería estar antes que la de NodoLista, para que sea conocida cuando se declara el campo siguiente de éste; pero entonces NodoLista no se conocería cuando se intenta definir PNodoLista, creándose un círculo vicioso. La solución consiste en empezar con una declaración incompleta de NodoLista que establezca que tal tipo va a existir, pero sin concretarlo, cosa que debe hacerse más adelante en la misma parte del programa. Una declaración incompleta comienza igual que una definición completa, pero termina con un punto y coma (;) donde ésta tiene la palabra is:
    type NodoLista;
    type PNodoLista is access NodoLista;
    type NodoLista is record
        Info: integer;
        Siguiente: PNodoLista;
    end record;



[Anterior][Inicio][Siguiente]