Session 6 · Pointers#
Watch the class#
The big idea#
Every variable lives at an address in memory. A pointer is a variable that holds such an address. Why care? Because Go copies values when you pass them to a function. If you want the function to change the original, give it the address instead of a copy.
Everyday example: a document. Emailing a photocopy means edits stay on the copy. Sending the link to the shared file means everyone edits the same document. A pointer is the link.
In plain words
&x means “the address of x”. *p means “the thing p points to”. *int is the type “pointer
to an int”. A pointer that points nowhere is nil.
Reach for it when:
a function must change its argument
a struct is large and copying it each call is wasteful
you need “no value” (a nil pointer) as a possible answer
Address and value#
basics.go
package main
import "fmt"
func main() {
x := 10
p := &x // p holds the address of x
fmt.Println(x, *p)
fmt.Println(p == &x)
*p = 20 // change x through p
fmt.Println(x)
var nothing *int
fmt.Println(nothing == nil)
}
10 10
true
20
true
Pointers and functions#
functions.go
package main
import "fmt"
type User struct{ Name string }
func renameCopy(u User) { u.Name = "Changed" }
func renameOriginal(u *User) { u.Name = "Changed" } // same as (*u).Name
func doubleCopy(n int) { n = n * 2 }
func doubleOriginal(n *int) { *n = *n * 2 }
func main() {
n := 5
doubleCopy(n)
fmt.Println(n)
doubleOriginal(&n)
fmt.Println(n)
u := User{Name: "Syed"}
renameCopy(u)
fmt.Println(u.Name)
renameOriginal(&u)
fmt.Println(u.Name)
// new gives a pointer to a zero value
q := new(int)
*q = 7
fmt.Println(*q)
}
5
10
Syed
Changed
7
doubleCopy(n)received its ownn, so main’snstayed 5.doubleOriginal(&n)followed the address.For structs Go lets you write
u.Nameon a pointer; it means(*u).Nameautomatically.new(int)returns a pointer to a fresh zeroint.Slices, maps and channels already contain a reference inside, so you rarely need a pointer to one.
nil pointers#
nil_panic.go
package main
import "fmt"
type User struct{ Name string }
func greet(u *User) string {
if u == nil {
return "hello, stranger"
}
return "hello, " + u.Name
}
func main() {
fmt.Println(greet(&User{"Asha"}))
fmt.Println(greet(nil))
defer func() { fmt.Println("recovered:", recover()) }()
var u *User
fmt.Println(u.Name) // nil pointer dereference
}
hello, Asha
hello, stranger
recovered: runtime error: invalid memory address or nil pointer dereference
Reading through a nil pointer crashes the program. Check if p == nil before you use a pointer
you did not create yourself. The recover() here is only to show the message; normal code does not need it.
Common mistakes#
Dereferencing nil. The classic “invalid memory address” panic.
Expecting a function to change a plain argument. Without a pointer it works on a copy.
Pointers to everything. Use pointers when you need to change something or to avoid copying big values; small values are fine to copy.
Returning a pointer to a loop variable in old Go (before 1.22) shared one variable; modern Go makes a new one per iteration.
Confusing
*in a type and*in an expression. Inp *intit builds a type; in*p = 3it follows the pointer.
Hands-on exercises#
Exercise 1 · Swap. Write swap(a, b *int) and use it on two variables.
Solution
package main
import "fmt"
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
x, y := 1, 2
swap(&x, &y)
fmt.Println(x, y)
}
2 1
Exercise 2 · Birthday. Write birthday(p *Person) that adds one to Age.
Solution
package main
import "fmt"
type Person struct {
Name string
Age int
}
func birthday(p *Person) {
p.Age++
}
func main() {
p := Person{"Asha", 28}
birthday(&p)
birthday(&p)
fmt.Println(p)
}
{Asha 30}
Exercise 3 · Optional value. Write find(names []string, target string) *string that returns a
pointer to the matching element or nil, and print a message for both outcomes.
Solution
package main
import "fmt"
func find(names []string, target string) *string {
for i := range names {
if names[i] == target {
return &names[i]
}
}
return nil
}
func main() {
names := []string{"Asha", "Ravi"}
for _, t := range []string{"Ravi", "Zoe"} {
if p := find(names, t); p != nil {
fmt.Println("found", *p)
} else {
fmt.Println(t, "not found")
}
}
}
found Ravi
Zoe not found
Return &names[i], not &n from a range loop copy.