Pointers
Every variable lives somewhere in memory, and that place is its address. A pointer is a value that holds an address: it points to a variable. Through a pointer you can read the variable it points to, and change it.
1package main2 3import "fmt"4 5func main() {6 price := 1007 p := &price8 fmt.Println(*p)9 *p = 8010 fmt.Println(price)11}
100 80
| Code | Meaning |
|---|---|
&price | The address of price. |
p := &price | p points to price. |
*p | The variable p points to: you can read it and assign to it. |
*p = 80 changes price itself. Press Step and see p drawn as an arrow pointing at price. The address itself is a long number that can change from run to run, so this course never shows it.
Pointer types
A pointer to an int has the type *int; a pointer to a string is a *string. The zero value of a pointer is nil: it points to nothing.
1package main2 3import "fmt"4 5func main() {6 count := 37 var p *int8 fmt.Println(p == nil)9 p = &count10 fmt.Printf("%T %d\n", p, *p)11 q := new(int)12 *q = 713 fmt.Println(*q)14}
true *int 3 7
new(int) makes a new int set to zero and gives back a pointer to it. You won't need it often: & is usually enough.
Functions get a copy
As lesson 5 showed, a function's parameters are copies. Change one inside the function and the caller's variable stays the same:
1package main2 3import "fmt"4 5func addBonus(salary int) {6 salary += 5007 fmt.Println("Inside:", salary)8}9 10func main() {11 pay := 300012 addBonus(pay)13 fmt.Println("Outside:", pay)14}
Inside: 3500 Outside: 3000
Passing a pointer
To let a function change the caller's variable, pass in the variable's address. When the function changes it through the pointer, it changes the one outside:
1package main2 3import "fmt"4 5func addBonus(salary *int) {6 *salary += 5007}8 9func main() {10 pay := 300011 addBonus(&pay)12 fmt.Println(pay)13}
3500
Press Step and go into addBonus: salary is an arrow pointing at pay in main. Compare it with the program before, where salary is a new box holding 3000.
What does this show?
1package main2 3import "fmt"4 5func double(n *int) {6 *n = *n * 27}8 9func main() {10 a := 511 b := a12 double(&b)13 fmt.Println(a, b)14}
b := a copies 5 into b; they are two different variables. double gets the address of b, so only b becomes 10.
Pointers to structs
Pointers most often point to structs. To get a field through a struct pointer, just write a.Balance; Go turns it into (*a).Balance for you:
1package main2 3import "fmt"4 5type Account struct {6 Owner string7 Balance int8}9 10func deposit(a *Account, amount int) {11 a.Balance += amount12}13 14func main() {15 acc := Account{Owner: "Maya", Balance: 100}16 deposit(&acc, 50)17 fmt.Println(acc.Balance)18 p := &Account{Owner: "Leo"}19 deposit(p, 30)20 fmt.Println(p.Owner, p.Balance)21 fmt.Println(p)22}
150
Leo 30
&{Leo 30}
&Account{…} makes a new struct and gives you a pointer to it. When fmt shows a struct pointer, it puts an & in front.
nil pointers
A nil pointer points to nothing. Read or write through it and the program panics:
1package main2 3import "fmt"4 5type Account struct {6 Owner string7 Balance int8}9 10func main() {11 var acc *Account12 fmt.Println(acc == nil)13 acc.Balance = 10014 fmt.Println("Never gets here")15}
true
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x?]
goroutine 1 [running]:
main.main(...)
/tmp/sandbox/prog.go:13 +0x1d
exit status 2What happened: This pointer is nil: there is nothing to read or write through it.
invalid memory address or nil pointer dereference means: the program tried to reach a variable through a nil pointer, which doesn't point to anything. Press "See how it went wrong" and it stops on line 13. When a pointer might be nil, check first with if acc != nil.
When to use a pointer
- When a function needs to change the caller's variable or struct.
- When a struct is big and you don't want to copy it on every call.
- When you want
nilto mean "nothing yet".
Slices and maps don't need a pointer for a function to change their elements: they already share their underlying data with the caller (lessons 7 and 8).
1package main2 3import "fmt"4 5func discount(prices []int) {6 for i := range prices {7 prices[i] = prices[i] * 9 / 108 }9}10 11func main() {12 prices := []int{100, 50}13 discount(prices)14 fmt.Println(prices)15}
[90 45]
But if the function makes the slice longer with append, the caller won't see that: in that case, have the function return the new slice.
Practice
Write the code, then press Check. A hint costs nothing; the answer is there when you need it.
Swap two numbers with pointers
✓Finish swap so it swaps the values of the two variables the pointers point to. The program swaps two pairs.
2 1 70 30
1package main2 3import "fmt"4 5func swap(a, b *int) {6}7 8func main() {9 x, y := 1, 210 swap(&x, &y)11 fmt.Println(x, y)12 m, n := 30, 7013 swap(&m, &n)14 fmt.Println(m, n)15}
Put a product on sale
✓Finish applyDiscount so it lowers the product's price by percent% through the pointer (percent 10 means 10% off). The program puts two products on sale.
Desk lamp 180 Mug 60
1package main2 3import "fmt"4 5type Product struct {6 Name string7 Price int8}9 10func applyDiscount(p *Product, percent int) {11}12 13func main() {14 lamp := Product{Name: "Desk lamp", Price: 200}15 mug := Product{Name: "Mug", Price: 75}16 applyDiscount(&lamp, 10)17 applyDiscount(&mug, 20)18 fmt.Println(lamp.Name, lamp.Price)19 fmt.Println(mug.Name, mug.Price)20}
Fix it: a pointer with no account
✓This program panics. Make acc point to an account that belongs to Maya with a balance of 0, and the program shows the balance after two deposits.
Maya 100 Maya 150
1package main2 3import "fmt"4 5type Account struct {6 Owner string7 Balance int8}9 10func deposit(a *Account, amount int) {11 a.Balance += amount12}13 14func main() {15 var acc *Account16 deposit(acc, 100)17 fmt.Println(acc.Owner, acc.Balance)18 deposit(acc, 50)19 fmt.Println(acc.Owner, acc.Balance)20}
What you learned
&xis the address ofx;p := &xmakesppoint tox, andphas the type*int.*pis the variableppoints to, for reading and changing; the zero value of a pointer isnil.- Parameters are copies; to change the caller's variable, pass its address
f(&x)and change it with*p. - With a struct pointer, write
p.Balancefor a field;&Account{…}makes a struct and gives you its pointer. - Reading or writing through a
nilpointer panics:nil pointer dereference. - Slices and maps let a function change their elements without a pointer.