viernes, 7 de diciembre de 2018

Punteros en funciones en Go

Cuando trabajamos con funciones, los parámetros habituales reciben valores. En sí, los parámetros son variables definidas dentro del bloque de código de la función, por lo que su visibilidad y ámbito se refieren exclusivamente a la función (ver capítulo "Visibilidad de datos y funciones en go").

En el siguiente ejemplo, definimos la variable contador, con el valor 1. A continuación, llama la función incrementar(), enviando el valor de la variable contador. Esta función, recibe el valor y lo asigna al parámetro o variable valor. Ambas variables son diferentes, están definidas en diferentes bloques de código, apuntan a diferentes direcciones de memoria. La función, incrementa el valor de la variable valor, pero no afecta ni puede hacer nada con la variable contador.

package main

import "fmt"

func main() {
   var contador int = 1

   // llama a la funcion pasando el valor
   incrementar(contador) // 1
   fmt.Println("contador = ", contador)
}

// El parametro valor recibe el valor de contador
// valor apunta a una direccion de memoria distinta
// de contador. Por tanto, ambas variables
// apuntan a diferentes direcciones y son diferentes
func incrementar(valor int) {
   valor++ // Se asigna a la memoria de valor
}

En el siguiente ejemplo vamos a usar punteros:

package main

import "fmt"

func main() {
   var contador int = 1

   // llama a la funcion pasando la direccion
   // de memoria de la variable contador
   incr(&contador)
   fmt.Println("contador = ", contador) // 2
}

// El parametro valor es un puntero que recibe
// la direccion de memoria de la variable contador
func incr(valor *int) {
   // valor apunta a &contador
   // *valor asigna el valor a la direccion memoria
   *valor++ // Se asigna a la memoria de contador
}

En este caso, el parámetro de la función es un puntero, y recibe una dirección de memoria a la que referenciar. Cuando se llama a la función, se pasa la indirección de la variable contador. Por tanto, si el parámetro apunta a la dirección de memoria de la variable contador, las operaciones del puntero afectarán a dicha dirección de memoria, y, por tanto, también a la variable contador. En la función utilizamos el puntero para incrementar el valor actual, lo que se ve reflejado también en la variable contador.

Enlaces de interés

Punteros en Go

El concepto de punteros es heredado del lenguaje C, y es muy útil para determinados escenarios, tales como las funciones o las estructuras.

Una variable es un nombre que utilizamos para referirnos a un valor o dato. Dicho valor o dato se almacena en alguna posición de la memoria del ordenador.

Podemos conocer la dirección de memoria de una variable utilizando el operador de indirección & (ampersand):

var x int = 10
fmt.Println("x=", x)   // 10
fmt.Println("&x=", &x) // 0xc0000120b0

Un puntero es, básicamente, una variable que apunta a la dirección de memoria de otra variable. Por tanto, mediante un puntero podemos operar directamente sobre esa dirección de memoria y, por consiguiente, sobre el valor de la primera variable.

package main

import "fmt"

func main() {

   var x int = 10

   fmt.Println("x=", x)   // 10
   fmt.Println("&x apunta a ", &x) // 0xc0000120b0 

   // ptrx es un puntero que apunta a
   // la direccion de memoria de x
   ptrx := &x

   // Almacena en la direccion de memoria a
   // la que apunta ptrx el valor 15
   *ptrx = 15 // Idem a x=15

   fmt.Println("x=", x)   // 15

   fmt.Println("ptrx apunta a ", ptrx) // 0xc0000120b0
   fmt.Println("*ptrx vale ", *ptrx)   // 15
   fmt.Println("&x apunta a ", &x)     // 0xc0000120b0
}

El operador puntero * (asterisco), hace referencia a una dirección de memoria. Cuando se utiliza para asignar un valor, este valor es asignado a la dirección de memoria a la que apunta el puntero, que es la misma dirección de memoria que la variable direccionada mediante el operador de indirección. Es decir, no se asigna el valor a la variable, si no a la dirección de memoria.

Enlaces de interés

martes, 4 de diciembre de 2018

Comandos del compilador de Go

Compilador de Go

Una vez instalado el paquete de Go en tu máquina, es hora de conocer el compilador y los útiles comandos que nos proporciona. Para contar con estas ventajas hemos de abrir la terminal o consola que tengas en tu sistema operativo.

Sintaxis

Puedes invocar al compilador de Go mediante go. Su sintaxis es la siguiente:

$ go <comando> [argumentos]

Versión del compilador

$ go version
go version go1.11.1 windows/amd64

Nota: La información mostrada se corresponderá con la versión que hayas instalado.

Ayuda del compilador

Para obtener ayuda sobre los posibles comandos del compilador de Go:

$ go help

Para obtener ayuda detallada sobre un comando específico del compilador de Go:

$ go help <comando>

Por ejemplo:

$ go help version
usage: go version

Version prints the Go version, as reported by runtime.Version.

Información del entorno de Go

Para obtener obtener los valores del entorno de Go en nuestra máquina:

$ go env

Mostrará las variables de entorno y sus valores, incluyendo GOPATH, la cual apunta al directorio o carpeta donde está instalado el compilador de Go.

Compilación de un programa

Un compilador lee el código de nuestra aplicación, chequea posibles errores, resuelve dependencias (paquetes y librerías) y, si todo es correcto, transforma el código Go en código máquina, generando un código binario que entiende el ordenador y puede ejecutar.

Para compilar usamos el siguiente comando:

$ go build <[path/]archivo.go>

El resultado será un archivo ejecutable.

Ejecución de un programa

En el anterior comando (go build), compilamos el programa, obteniendo el archivo ejecutable, el cual, después, hemos de ejecutar para correr el programa.

Podemos automatizar este proceso mediante:

$ go run <[path/]archivo.go>

Este comando compila el programa, obteniendo un ejecutable en un directorio o carpeta temporal, y, automáticamente, lanza dicho programa.

Reformateo del código

El compìlador de Go posee un comando muy interesante, el cual permite reformatear el código. Esto permite, de forma automática, organizar el código por llaves, sangrías o tabulaciones, líneas en blanco, etc... de forma elegante.

Para reformatear un archivo de código:

$ go fmt <[path/]archivo.go>

Si deseamos reformatear el código de uno o varios paquetes:

$ go fmt [paquetes]

Descarga e instalación de paquetes

Cuando desarrollamos aplicaciones podemos utilizar paquetes de terceros. Para llevar a cabo esto, podemos instalar e instalar dichos paquetes desde un repositorio en internet, normalmente de Git. Esto se consigue con el siguiente comando:

$ go get [paquetes]

Puede ocurrir que un paquete pueda, a su vez, utilizar otros paquetes, por lo que tiene una dependencia. El comando anterior, también resuelve estas dependencias y las instala.

Los paquetes se instalan en el directorio pkg, dentro de la ruta donde apunte la variable de entorno GOPATH.

Actualización de paquetes

Los paquetes en Go, al igual que en otros lenguajes de programación, van evolucionando. Aparecen nuevas funcionalidades, se corrigen errores, se optimizan...

Para actualizar un paquete ya instalado a la última versión disponible:

$ go doc fix [paquetes]

Testeo de paquetes

Para probar paquetes y comprobar su estado, utilizaremos el siguiente comando:

$ go test [paquetes]

Listado de paquetes

Para visualizar la lista de paquetes de paquetes instalados

$ go list

Documentación de paquetes

Un paquete en Go es un conjunto de librerías de funciones o utilidades que extienden las posibilidades del lenguaje. El propio lenguaje Go ofrece un conjunto de paquetes estándar que conforman el lenguaje, pero también podemos crear nuestros propios paquetes y usar paquetes de terceros.

Los paquetes se suelen documentar, y podemos acceder a su documentacion mediante el siguiente comando:

$ go doc <paquete>

Por ejemplo:

$ go doc fmt

Un paquete puede contener múltiples funciones. Si queremos obtener documentación específica de una determinada función, podemos especificar el nombre del paquete, seguido de un punto y del nombre de la función. En el siguiente ejemplo obtendremos la documentación de la función Println() del paquete fmt:

$ go doc fmt.Println
func Println(a ...interface{}) (n int, err error)
    Println formats using the default formats for its operands and writes to
    standard output. Spaces are always added between operands and a newline is
    appended. It returns the number of bytes written and any write error
    encountered.

Enlaces de interés