Every variable is stored in one or more contiguous memory locations, called addresses.
A pointer is a variable that holds the location where a value is stored in memory. In other words, it contains that variable’s address. If you’ve taken computer-science courses, you might have seen a graphic to represent how variables are stored in memory. A representation of the following two variables would look something like Figure 5-1:
var x int32 = 10
var y bool = true
Variables in Memory
Figure 5-1. Storing two variables in memory
Different types of variables can take up different amounts of memory, which is called the size of the variable. In this example, you have two variables, x, which is a 32-bit integer, and y, which is a Boolean. Storing a 32-bit int requires 4 bytes, so the value for x is stored in 4 bytes, starting at address 1 and ending at address 4.
A Boolean requires only a single byte: you need only a bit to represent true or false, but the smallest amount of memory that can be independently addressed is a byte. Thus, the value for y is stored in one byte at address 5, with true represented by the value 1. Figure 5-2 demonstrates how the following pointers are stored in memory:
var x int32 = 10
var y bool = true
pointerX := &x
pointerY := &y
var pointerZ *string
Pointers in Memory
Figure 5-2. Storing pointers in memory
Because a pointer’s value is just the address of another variable, all pointers, no matter what type of variable they point to, have the exact same size. The examples in this topic use 4-byte pointers, but computers with 64-bit CPUs use 8 bytes for a pointer. Our pointer to x, pointerX, is stored at location 6 and has the value 1, the address of x. Similarly, our pointer to y, pointerY, is stored at location 10 and has the value 5, the address of y. The last pointer, pointerZ, is stored at location 14 and has the value 0, because it doesn’t point to anything.
The zero value for a pointer is nil. You’ve seen nil a few times before, as the zero value for slices, maps, and functions. All these types are implemented with pointers. As I covered in the related discussion, nil is an untyped identifier that represents the lack of a value for certain types. Unlike NULL in C, nil is not another name for 0; you can’t convert it back and forth with a number.
Warning
As alluded to in the related discussion, nil is a value defined in the universe block, so it can be shadowed. Never name a variable or function nil unless you are trying to trick your coworker and are unconcerned about your annual review.
Go’s pointer syntax is partially borrowed from C and C++. Since Go has a garbage collector, most memory-management pain is removed. Furthermore, some tricks that you can do with pointers in C and C++, including pointer arithmetic, are not allowed in Go.
Note
The Go standard library does have an unsafe package that lets you do some low-level operations on data structures. While C uses pointer manipulation for common operations, it is exceedingly rare for Go developers to use unsafe. You’ll take a quick look at it in the related discussion.
The ampersand, &, is the address operator. It precedes a value type and returns the address where the value is stored:
x := "hello"
pointerToX := &x
The asterisk, *, is the indirection operator. It precedes a variable of +pointer_ type and returns the pointed-to value. This is called dereferencing:
x := 10
pointerToX := &x
fmt.Println(pointerToX) // prints a memory address
fmt.Println(*pointerToX) // prints 10
z := 5 + *pointerToX
fmt.Println(z) // prints 15
Before dereferencing a pointer, you must make sure that the pointer is non-nil. Your program will panic if you attempt to dereference a nil pointer:
var x *int
fmt.Println(x == nil) // prints true
fmt.Println(*x) // panics
A pointer type is a type that represents a pointer. It is written with an asterisk, *, before a type name. A pointer type can be based on any type:
x := 10
var pointerToX *int
pointerToX = &x
Just as variables can have pointer types, individual fields in a struct can be pointer types as well:
type person struct {
FirstName string
MiddleName *string
LastName string
}