Читать книгу Programación en Go - Mario Macías Lloret - Страница 24

Оглавление

Capítulo 4

APUNTADORES

La memoria de un ordenador podría abstraerse, como si fuera un conjunto de cajones colocados en una gigantesca estantería. Cada cajón tiene un número identificativo único, que permite al ordenador referirse a él a la hora de ir a buscar o guardar datos. Dicho número identificativo se conoce como “dirección de memoria”.

Los apuntadores (o punteros) son variables que no guardan valores útiles como tales, sino las direcciones de memoria donde se encuentran dichos valores. Su nombre hace referencia al hecho de que no guardan una variable sino que “apuntan” a su dirección.

En Go, los apuntadores tienen un tipo asociado, es decir, solo pueden apuntar a variables de un tipo en concreto: un puntero a int solo podrá guardar la dirección de memoria de una variable del tipo int, un puntero a bool solo podrá guardar la dirección de memoria de un bool, etc.

4.1 DEFINICIÓN DE UN APUNTADOR

Un apuntador se define como una variable, añadiendo un asterisco delante del tipo de datos al que este apuntador puede apuntar:

var pi *int // apuntador a ints var pb *bool // apuntador a bools

4.2 LA REFERENCIA A nil

Los apuntadores definidos en la sección anterior no han sido inicializados a ningún valor. Apuntan al valor nil, que podría interpretarse como “a ninguna parte”.

El valor nil se puede utilizar tanto para reiniciar el valor de un apuntador “a nada” como para comprobar si un apuntador apunta a algún lugar válido:


Cuando un apuntador hace referencia a la dirección nil, este no se puede utilizar para leer o modificar valores apuntados, ya que dicho valor apuntado no existe. Si tratáramos de hacerlo, el sistema de memoria segura de Go abortaría la ejecución del programa, mostrando un error similar al siguiente:

panic: runtime error: invalid memory address or nil pointer dere- ference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x49169f]

4.3 APUNTANDO HACIA UNA VARIABLE

El operador ampersand (&) delante de una variable retorna la dirección de memoria de esta. Este valor se puede asignar directamente a un puntero:

i := 10 var p *int p = &i

En el ejemplo anterior, el apuntador p apuntará a la variable i (Figura 4.1). El código anterior se puede abreviar de la siguiente manera:

i := 10 p := &i

Ya que &i retorna un valor del tipo *int, p será declarado como *int (apuntador a int) y desde el principio apuntará al int i.


Figura 4.1 Apuntador “p” apuntando a la dirección de memoria de “i” (esquema visual).

4.4 LEYENDO O MODIFICANDO EL VALOR APUNTADO

A través de un apuntador, se puede leer o modificar el valor de la variable apuntada. El operador asterisco * delante del nombre del apuntador nos permitirá acceder al valor de la variable apuntada, tanto para su lectura como para su modificación:

i := 10 p := &i a := *p *p = 21

En el ejemplo anterior, donde el apuntador p apunta a la variable i, operando a través de este apuntador se está leyendo y modificando el valor de la variable i. La tercera línea (a := *p) tendría un resultado equivalente a a := i, y la cuarta línea (*p = 21) tendría un resultado equivalente a i = 21. La Figura 4.2 esquematiza la evolución de la memoria según las anteriores instrucciones se van ejecutando: qué variables se van creando y qué valores toman en cada momento.


Figura 4.2 Uso del operador asterisco sobre punteros (esquema visual).

Viéndolo desde el punto de vista del ejemplo anterior, podría parecer que los apuntadores son un paso intermedio innecesario. Más adelante, este libro muestra la verdadera utilidad de los apuntadores cuando se usan para recorrer complejas estructuras de datos, para referirse a alguna de sus partes o para intercambiar datos con “funciones”.

4.5 VALORES VERSUS REFERENCIAS

Las variables de Go, por defecto, son operadas “por valor”. Esto significa que:

• El operador de asignación = copia el valor de la derecha hacia el valor de la variable de la izquierda. Tras la instrucción a = b, a y b serán dos variables con el mismo valor, pero no son la misma variable: ocupan zonas distintas de la memoria y la modificación de una no afecta al valor anterior de la otra.

• El operador de igualdad == entre dos variables resulta en true si ambas variables tienen un valor igual, aunque sean dos variables distintas.

Los punteros de Go nos permitirán operar “por referencia”, es decir:

• El operador de asignación = apunta (o referencia) el apuntador de la izquierda hacia el apuntador de la derecha. Tras la instrucción p = &i, el apuntador p apunta hacia la misma dirección de memoria donde está i. Cualquier modificación de p afectará a tal valor y a cómo se accede a través de i.

• El operador de igualdad == entre dos apuntadores resulta en true si ambos apuntan a la misma dirección de memoria, o en false si apuntan a direcciones distintas (aunque esas direcciones contengan el mismo valor). Si se quisiera comparar la igualdad de dos valores apuntados por los apuntadores p1 y p2, se debería usar el operador asterisco para obtener el valor de ambos y, así, poder comparar valores en vez de direcciones:



El ejemplo anterior mostraría la siguiente salida estándar:

p1 y p2 apuntan a direcciones distintas p1 y p2 apuntan a valores iguales

Ya que p1 y p2 apuntan a dos variables con el mismo valor, pero en distintas zonas de memoria (Figura 4.3), tan solo mostraría el mensaje p1 y p2 apuntan a valores iguales, ya que apuntan a dos variables iguales, pero que no son la misma.


Figura 4.3 p1 y p2 apuntan a valores iguales, pero distintas variables (esquema visual).

La primera comprobación sería “por referencia”, ya que se comparan direcciones de memoria; mientras que la segunda comprobación sería “por valor”, ya que se comparan dos valores concretos.

Programación en Go

Подняться наверх