Skip to content
Theory · Pointers · lesson 1 of 8

Pointers: & and *

What you'll learn

What you'll learn:

  • explain what a variable's address and a pointer are;
  • read and change a variable through a pointer with & and *;
  • pass a pointer to a function so it can change your variable;
  • tell a nil pointer from a pointer to zero, and create variables with new.
A street of identical houses, a slip with the address &x and a dotted line from it to the house x

A computer's memory is like a long street of identical cells. Every cell has a number—its address. When you write x := 1, Go picks a cell, puts 1 in it and remembers that the name x means exactly that cell.

Picture a street of identical houses and a slip of paper with the address of one of them. The house is the variable, with a value inside. The slip is the address: it takes you to the house, so you can look inside or change something there. You can hand the slip to someone else, and they will come to the very same house.

Why would you need the slip when you already have the name x? Let's see what plain assignment does:

GO
package main

import "fmt"

func main() {
    x := 1
    y := x
    y = 5
    fmt.Println(x, y) // 1 5
}

Let's walk through the example line by line:

  1. x := 1 builds the house x and puts 1 in it.
  2. y := x does not give the same house a second name. It builds a new house y and copies the value into it.
  3. y = 5 changes only the house y.
  4. fmt.Println(x, y) prints 1 5: writing to y left x alone.

Sometimes you need the opposite: a second way to reach the variable x itself. That is what pointers are for.

The address and the *int type

The & operator in front of a variable's name returns its address—that slip of paper. A pointer is a value that holds the address of a variable. A pointer to an int has the type *int:

GO
x := 1
p := &x
fmt.Println(p == &x) // true

Let's go line by line:

  1. x := 1 is an ordinary variable.
  2. p := &x puts the address of x into p. p has the type *int: "an address where an int lives".
  3. fmt.Println(p == &x) prints true: p holds the same address that &x returns.

What else matters about the pointer type:

  • a pointer to int can hold only the address of an int variable, never the address of a string;
  • the address itself is a number that changes from run to run, so programs almost never print it.

A pointer is a variable like any other: it has a name, a type and a value. After q := p you have two slips with the same address, and writing through q changes the same x.

Reading and writing through *p

The * operator in front of a pointer means "go to the address and take the variable itself". This is called dereferencing. Let's walk through an example line by line:

GO
x := 1
p := &x
*p = 5
fmt.Println(x) // 5
y := *p
x = 7
fmt.Println(y, *p) // 5 7
  1. x := 1 builds the house x and puts 1 in it.
  2. p := &x writes the address of that house on the slip p.
  3. *p = 5 follows the address in p and puts 5 into x itself.
  4. fmt.Println(x) prints 5.
  5. y := *p copies whatever x holds at that moment into a new variable y.
  6. x = 7 changes x, while y lives on its own: it still holds 5.
  7. fmt.Println(y, *p) prints 5 7: *p goes to x afresh every time, so it sees 7.

The diagram shows the moment right after the line *p = 5.

DiagramPointer: a slip with a house's address

*p follows the address and takes x itself. The address is made up: in a real program it changes from run to run.

A slip can be rewritten

Here the slip-of-paper picture needs one caveat: a slip can be rewritten. After p = &y, the same p points to y, and *p = 0 changes y, not x. The house x is still there—the slip now leads to a different house.

Common mistake

The value was written into the pointer itself, without *:

GO
x := 1
p := &x
p = 5 // ✗
fmt.Println(x, *p)
TEXT
cannot use 5 (untyped int constant) as *int value in assignment

The program does not compile: p has the type *int, so it can hold only the address of an int variable, not a number. It is like writing "5" on the slip where the address should be. To store 5 in the variable p points to, write *p = 5.

Key points

  • A pointer holds a variable's address; &x takes the address, and a pointer to int has the type *int.
  • *p is the variable at that address: you read and write through it.
  • y := *p copies the value at that moment, while *p reads the variable afresh every time.
  • A pointer gets an address (p = &y); a value goes in through *p = 5.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way