Skip to content

Punteros en Go

Go conserva los punteros, garantizando hasta cierto punto el rendimiento, al mismo tiempo que limita su uso para mejorar el GC y la seguridad.

Creación

Hay dos operadores comunes para punteros, uno es el operador de dirección &, y el otro es el operador de desreferencia *. Al obtener la dirección de una variable, se devuelve un puntero del tipo correspondiente, por ejemplo:

go
func main() {
   num := 2
   p := &num
   fmt.Println(p)
}

El puntero almacena la dirección de la variable num

0xc00001c088

El operador de desreferencia tiene dos usos, el primero es acceder al elemento apuntado por el puntero, es decir, desreferenciar, por ejemplo

go
func main() {
  num := 2
  p := &num
  rawNum := *p
  fmt.Println(rawNum)
}

p es un puntero, al desreferenciar un tipo puntero se puede acceder al elemento apuntado. Otro uso es declarar un puntero, por ejemplo:

go
func main() {
   var numPtr *int
   fmt.Println(numPtr)
}
<nil>

*int significa que el tipo de esta variable es un puntero de tipo int, sin embargo, un puntero no solo se debe declarar, también se debe inicializar, se le debe asignar memoria, de lo contrario es un puntero nulo y no se puede usar normalmente. Se puede usar el operador de dirección para asignar la dirección de otra variable a este puntero, o usar la función incorporada new para asignar memoria manualmente, por ejemplo:

go
func main() {
   var numPtr *int
   numPtr = new(int)
   fmt.Println(numPtr)
}

Más comúnmente se usa la declaración corta de variable

go
func main() {
   numPtr := new(int)
   fmt.Println(numPtr)
}

La función new solo tiene un parámetro que es el tipo, y devuelve un puntero del tipo correspondiente. La función asignará memoria para ese puntero, y el puntero apunta al valor cero del tipo correspondiente, por ejemplo:

go
func main() {
   fmt.Println(*new(string))
   fmt.Println(*new(int))
   fmt.Println(*new([5]int))
   fmt.Println(*new([]float64))
}

0
[0 0 0 0 0]
[]

Prohibición de aritmética de punteros

En Go no se soporta la aritmética de punteros, es decir, los punteros no pueden desplazarse. Primero veamos un código en C++:

cpp
int main() {
    int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    int *p = &arr[0];
    cout << &arr << endl
         << p << endl
         << p + 1 << endl
         << &arr[1] << endl;
}
0x31d99ff880
0x31d99ff880
0x31d99ff884
0x31d99ff884

Se puede ver que la dirección del array es consistente con la dirección del primer elemento del número, y después de la operación de suma uno al puntero, el elemento apuntado es el segundo elemento del array. Los arrays en Go también son así, pero la diferencia es que los punteros no pueden desplazarse, por ejemplo

go
func main() {
   arr := [5]int{0, 1, 2, 3, 4}
   p := &arr
   println(&arr[0])
   println(p)
   // intentando hacer aritmética de punteros
   p++
   fmt.Println(p)
}

Este programa no podrá compilar, el error es

main.go:10:2: invalid operation: p++ (non-numeric type *[5]int)

TIP

La biblioteca estándar unsafe proporciona muchas operaciones para programación de bajo nivel, incluyendo aritmética de punteros, ve a biblioteca estándar-unsafe para conocer los detalles.

new y make

En las secciones anteriores se han mencionado muchas veces las funciones incorporadas new y make, ambas son similares pero tienen diferencias, repasemos a continuación.

go
func new(Type) *Type
  • El valor de retorno es un puntero de tipo
  • El parámetro recibido es el tipo
  • Se usa exclusivamente para asignar espacio de memoria a punteros
go
func make(t Type, size ...IntegerType) Type
  • El valor de retorno es un valor, no un puntero
  • El primer parámetro recibido es el tipo, los parámetros de longitud variable son diferentes según el tipo pasado
  • Se usa exclusivamente para asignar memoria a slices, mapas y canales

A continuación algunos ejemplos:

go
new(int) // puntero de int
new(string) // puntero de string
new([]int) // puntero de slice de enteros
make([]int, 10, 100) // slice de enteros con longitud 10 y capacidad 100
make(map[string]int, 10) // mapa con capacidad 10
make(chan int, 10) // canal con tamaño de búfer 10

Golang editado por www.golangdev.cn