Un tema importante del lenguaje Go es el manejo del concepto de punteros.
Los punteros en el lenguaje Go son esenciales:
Una variable de tipo puntero almacena la dirección de memoria de otra variable que puede ser int, float64, string etc.
Es difícil en un principio entender y ver las ventajas que trae trabajar con punteros pero su uso es fundamental si vamos a trabajar con el lenguaje Go.
La sintaxis para definir una variable de tipo puntero se logra antecediendo el caracter * al nombre de la variable:
var pe *int var pf *float64
Definir dos variables enteras y almacenar valores por asignación. Definir una variable puntero a entero y guardar sucesivamente las direcciones de dichas dos variables y acceder a sus valores.
package main import "fmt" func main() { var valor1 int = 10 var valor2 int = 20 var pe *int pe = &valor1 fmt.Println("Lo apuntado por pe es:", *pe) fmt.Println("La direccion que almacena pe es:", pe) pe = &valor2 fmt.Println("Lo apuntado por pe es:", *pe) fmt.Println("La direccion que almacena pe es:", pe) }
La ejecución de este programa tiene una salida similar a:
Haremos una serie de problemas para entender el concepto de una variable de tipo puntero que en principio no le encontraremos mayores ventajas a como veníamos resolviendo problemas. Lo más conveniente es empezar con problemas muy sencillos para luego ver la verdadera potencia que nos proporcionan las variables de tipo puntero.
En este problema definimos dos variables de tipo entera y las inicializamos con los valores 10 y 20:
var valor1 int = 10 var valor2 int = 20
Es lo mismo si utilizamos la sintaxis implícita para definir las dos variables enteras:
valor1 := 10 valor2 := 20
Nosotros ya hemos visto que si queremos acceder e imprimir el contenido de la variable "valor1" lo hacemos por medio de su nombre:
fmt.Println(valor1)
Hay otra forma de acceder al contenido de la variable "valor1" mediante el concepto de una variable de tipo puntero. Definimos una tercer variable llamada pe (una variable puntero a entero):
var pe *int
La variable "pe" puede almacenar una dirección de memoria, es incorrecto (no podemos asignar valor1 a pe):
pe = valor1
pe y valor1 son variables de distinto tipo, valor1 es una variable int y pe es una variable que apunta a una dirección de memoria donde se almacena un int.
Para almacenar en la variable pe la dirección de memoria donde se almacena valor1 utilizamos el caracter &:
pe = &valor1
La línea anterior la podemos leer como: "en la variable pe almacenamos la dirección de memoria donde se almacena valor1".
Si vemos el contenido de la memoria podemos identificar dos variables "valor1" y "pe":
La variable "valor1" almacena el número entero 10. La variable "valor1" se almacena por ejemplo en la dirección de memoria 1000 (este valor es a modo de ejemplo y la dirección real sucede cuando se ejecuta el programa)
La variable "pe" puede almacenar la dirección de una variable entera, cuando hacemos la asignación:
pe = &valor1
Estamos guardando la dirección 1000 en la variable "pe".
Para imprimir lo apuntado por el puntero pe utilizamos la sintaxis *pe, en nuestro caso accedemos al valor 10 almacenado en valor1:
fmt.Println("Lo apuntado por pe es:", *pe)
Imprimir el contenido de pe puede tener poco sentido ya que veremos la dirección que almacena pe, en el caso que lo hagamos debemos indicar:
fmt.Println("La direccion que almacena pe es:", pe)
Una variable de tipo puntero puede cambiar la dirección que almacena a lo largo de la ejecución del programa, luego si hacemos:
pe = &valor2
Estamos almacenando la dirección de la variable valor2. Si imprimimos lo apuntado por pe tendremos el número 20:
fmt.Println("Lo apuntado por pe es:", *pe)
Definir dos variables enteras y no inicializarlas.
Definir una variable puntero a entero, hacer que apunte sucesivamente a las dos variables enteras definidas previamente y cargue sus contenidos.
Imprimir las dos variables enteras.
package main import "fmt" func main() { var x1, x2 int var pe *int pe = &x1 *pe = 100 pe = &x2 *pe = 200 fmt.Println("Primer variable entera:", x1) fmt.Println("Segunda variable entera:", x2) }
Definimos dos variable enteras y no les cargamos valor (recordemos que se carga un cero por defecto cuando definimos variables de tipo int):
var x1, x2 int
Definimos una variable puntero a entero:
var pe *int
Almacenamos en la variable puntero la dirección de la variable x1:
pe = &x1
Modificamos el contenido de x1 accediendo a su contenido mediante el puntero pe:
*pe = 100
Modificamos el contenido de la variable puntero pe con la dirección de la variable x2:
pe = &x2
Modificamos ahora el contenido de x2 accediendo por medio de puntero pe:
*pe = 200
Imprimimos los contenidos de x1 y x2:
fmt.Println("Primer variable entera:", x1) fmt.Println("Segunda variable entera:", x2)
Entiendo que este programa es más eficiente si directamente cargamos x1 y x2 asignándole las valores 100 y 200, pero tengamos presente que lo más importante es entender el concepto de punteros la la sintaxis para obtener la dirección de una variable (&) y acceder a lo apuntado por medio de (*)
package main import "fmt" func main() { var s1 string = "uno" var s2 string ="dos" var ps *string ps = &s1 fmt.Println(s1) //se imprime: ? *ps = "tres" fmt.Println(s1) //se imprime: ? s1 = "cuatro" fmt.Println(*ps) //se imprime: ? ps = &s2 fmt.Println(*ps) //se imprime: ? }Indicar que valor se imprime en cada llamada a Println
package main import "fmt" func main() { var f int var pe *int pe = &f for *pe = 1; *pe <= 10; *pe = *pe + 1 { fmt.Println(f) //se imprime ? ? ? ? ? ? ? ? ? ? } }Indicar que valor se imprime en cada llamada a Println
#package main import "fmt" func main() { var z1, z2 float64 var pf *float64 pf = &z1 *pf = 10.2 pf = &z2 *pf=20.90 fmt.Println(z1, z2) // Se imprime ? ? }Indicar que valor se imprime en la llamada a Println
programa108.go package main import "fmt" func main() { var s1 string = "uno" var s2 string ="dos" var ps *string ps = &s1 fmt.Println(s1) //se imprime: uno *ps = "tres" fmt.Println(s1) //se imprime: tres s1 = "cuatro" fmt.Println(*ps) //se imprime: cuatro ps = &s2 fmt.Println(*ps) //se imprime: dos } programa109.go package main import "fmt" func main() { var f int var pe *int pe = &f for *pe = 1; *pe <= 10; *pe = *pe + 1 { fmt.Println(f) //se imprime 1 2 3 4 5 6 7 8 9 10 } } //si queremos utilizar el operador ++ con lo apuntado debe ser: *pe++ programa110.go package main import "fmt" func main() { var z1, z2 float64 var pf *float64 pf = &z1 *pf = 10.2 pf = &z2 *pf=20.90 fmt.Println(z1, z2) // Se imprime 20.2 20.9 }