Gestión de memoria en C. Parte III: Puntero.


En los artículos anteriores de esta serie hemos hecho un repaso y descripción de los diferentes contenedores de datos definidos en el lenguaje de programación C. Por un lado, los tipos de datos básicos y su representación en los mapas de memoria de los dispositivos. Por otro, diferentes formas de agrupar estos tipos de datos.

En este artículo vamos a profundizar la representación de todos estos bloques en los mapas de memoria. Esta representación se gestiona con direcciones de memoria. Lo que denominamos punteros.

Un puntero es por tanto una variable que contiene en su valor la dirección de la memoria donde empieza a guardarse el contenido de una variable. Sea de un tipo básico o un conjunto de datos. Se almacena en hexadecimal y su tamaño depende de la arquitectura de la memoria del Sistema Operativo (como las arquitecturas de 32 y 64 bytes de los ordenadores).

En una arquitectura de direccionamientos de longitud 4 bytes (16 bytes por línea de datos), la declaración en lenguaje C de un puntero de tipo entero, estamos definiendo un nuevo contenedor de datos cuyo contenido será tratado como una dirección de memoria. El tipo de dato definido indica la cantidad de bytes que debe utilizar el compilador para obtener el valor.

Las referencias se indican añadiendo el caracter & delante del identificador de la variable a la que se desea referencias. Esta acción indica al compilador que debe trabajar con la dirección de memoria y no con el contenido de ese identificador.

La asignación de una referencia a una variable ya declarada sería como sigue.


Declaración y asignación de un puntero entero
Declaración y asignación de un puntero entero

Pero si se modifica el tipo, en este caso que el valor es solo un byte no sufriría modificaciones

Declaración y asignación de un puntero char
Declaración y asignación de un puntero char

Es muy muy importante entender que, al trabajar con punteros, no se trabaja con valores, sino con las direcciones de memoria. Con el mapeado. Y que, si se reasigna directamente el valor de una variable al puntero, se está modificando dicha dirección a zonas posiblemente inaccesibles de memoria.

Si se incrementa o decrementa el valor de un puntero, el efecto es como si se desplazara una casilla de inicio a la izquierda o la derecha en el mapa de memoria. Por eso nos podemos desplazar directamente por un array mediante punteros.

Desplazamiento mediante punteros por un array entero (tamaño del tipo 4 bytes)

Incluso arrays de diferentes dimensiones.

Mapeado de una matriz o array de 2 dimensiones

También podemos hacer punteros que referencien a otros punteros o punteros dobles. Esto lo indicamos incrementando el número de asteriscos en la declaración.

Doble puntero
Doble puntero

Por otro lado, cuando queremos acceder a los datos que están contenidos en la memoria o modificarlos, nos referimos a ellos incluyendo un asterisco justo delante del identificador del puntero.


Acceso al contenido de un puntero
Acceso al contenido de un puntero

Es recomendable encerrar el identificador entre paréntesis para indicar si un desplazamiento posterior debe aplicarse al contenido o a la propia dirección de memoria. No es lo mismo *ptr + 1 que *(ptr + 1).

También podemos manejar las direcciones como numerales hexadecimales para imprimir valores y posiciones de memoria como se ve en el siguiente ejemplo de uso de printf con diferentes situaciones.

Impresión de punteros con printf




Referencias:

  • Material de clase preparado para las asignaturas de Programación impartidas en los grados de UFV

Deja un comentario