Skip to content

Pointers

Go retains pointers, which ensures performance to some extent, while also limiting the use of pointers for better GC and safety considerations.

Creation

There are two commonly used operators for pointers: the address-of operator & and the dereference operator *. Taking the address of a variable returns a pointer of the corresponding type. For example:

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

The pointer stores the address of variable num:

0xc00001c088

The dereference operator has two uses. The first is to access the element pointed to by the pointer, i.e., dereferencing. For example:

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

p is a pointer. Dereferencing the pointer type allows access to the element pointed to by the pointer. Another use is to declare a pointer:

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

*int means the variable's type is a pointer to int. However, a pointer cannot just be declared; it also needs to be initialized. You need to allocate memory for it, otherwise it's a nil pointer and cannot be used properly. Either use the address-of operator to assign another variable's address to this pointer, or use the built-in function new to manually allocate:

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

Or use short variable declaration:

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

The new function takes only one parameter, which is a type, and returns a pointer of the corresponding type. The function allocates memory for this pointer, and the pointer points to the zero value of the corresponding type:

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]
[]

No Pointer Arithmetic

Pointer arithmetic is not supported in Go, meaning pointers cannot be offset. Let's look at some C++ code first:

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

You can see that the array's address is the same as the first element's address, and after adding one to the pointer, it points to the second element of the array. Arrays in Go are the same, but the difference is that pointers cannot be offset:

go
func main() {
   arr := [5]int{0, 1, 2, 3, 4}
   p := &arr
   println(&arr[0])
   println(p)
   // Attempting pointer arithmetic
   p++
   fmt.Println(p)
}

This program will not compile, with the following error:

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

TIP

The standard library unsafe provides many operations for low-level programming, including pointer arithmetic. Visit Standard Library - unsafe for details.

new and make

In the previous sections, the built-in functions new and make have been mentioned many times. They are somewhat similar but also different. Let's review:

go
func new(Type) *Type
  • Return value is a type pointer
  • Parameter is a type
  • Specifically for allocating memory space for pointers
go
func make(t Type, size ...IntegerType) Type
  • Return value is a value, not a pointer
  • First parameter is a type, variadic parameters differ depending on the type passed
  • Specifically for allocating memory for slices, maps, and channels.

Here are some examples:

go
new(int) // int pointer
new(string) // string pointer
new([]int) // slice of ints pointer
make([]int, 10, 100) // slice of ints with length 10 and capacity 100
make(map[string]int, 10) // map with capacity 10
make(chan int, 10) // channel with buffer size 10

Golang by www.golangdev.cn edit