Pointers
By Syncfusion®, Inc. · Last updated
Go pointers: memory addresses, the & and * operators, changing values through pointers, nil pointers, pass by value vs pointer parameters, and struct pointers.
What Are Pointers?
Pointer = a variable that stores the memory address of another variable.
- A normal variable holds a value, like
70. - A pointer holds where a value lives, like
0xc000012080. - Through the pointer, you can read or change that value.
Simple lesson: a pointer is an arrow that points to another variable's box.
A Real-World Example
Think of a student's locker at school.
- The locker holds books.
- The locker number, like "B-12", tells you where it is.
- If you give a friend your locker number, they can go and put a book inside.
- You did not give them the books. You gave them the address.
Now the mapping:
- The locker = a variable.
- The books inside = its value.
- The locker number = its memory address.
- A note with the locker number = a pointer.
Go has pointers, but it keeps them safe:
- no pointer arithmetic (you cannot do
p + 1) - memory is freed automatically by the garbage collector
- most of the time, you use pointers to let a function change a value, or to avoid copying big structs
What You Will Learn
In this lesson, you will learn:
- What pointers and memory addresses are, and why pointers are used.
- The address operator
&and the dereference operator*. - How to read and change values through a pointer, and what a nil pointer is.
- How pass by value works, and how pointer parameters change original values.
- How pointers work with structs.
Pointer Fundamentals
Memory Addresses
Memory = the computer's storage for running programs, like a long row of numbered lockers.
Memory address = the number of one locker. It is usually printed in hexadecimal, like 0xc000012080.
Print an address
package main
import "fmt"
func main() {
marks := 70
fmt.Println(marks)
fmt.Println(&marks)
}
&marksgives the address ofmarks.- The address is different on each run and each computer.
Pointer Variables
Pointer type = *T, meaning "a pointer to a T".
*int= a pointer to anint.*string= a pointer to astring.- The zero value of every pointer is
nil.
Why Pointers Are Used
- Let a function change a variable that belongs to the caller.
- Avoid copying a big value, like a struct with many fields.
- Share one value between many parts of a program.
- Say "no value" with
nil, for something that is optional.
Simple lesson: a pointer of type *T stores the address of a T.
Pointer Operators
Step through the picture. Watch the arrow and the box.
Address Operator (&)
& = "the address of". &x gives a pointer to x.
Dereference Operator (*)
Dereference = follow the pointer to the value it points to.
* = "the value at". *p gives the value p points to.
The two pointer operators
& and *
package main
import "fmt"
func main() {
marks := 70
p := &marks
fmt.Println(*p)
*p = 85
fmt.Println(marks)
fmt.Println(p == &marks)
}
70
85
true
p := &marksmakesppoint tomarks.*p = 85changesmarks, through the pointer.p == &marksis true: both hold the same address.
Simple lesson: & makes a pointer, and * follows it to the value.
Working with Pointers
Dereferencing Pointers
Two pointers can point to the same box.
Two pointers, one box
package main
import "fmt"
func main() {
score := 10
a := &score
b := &score
*a += 5
*b += 5
fmt.Println(score, *a, *b)
}
20 20 20
- Both
aandbpoint toscore. - Changing through either one changes the same box.
Modifying Values Through Pointers
You have seen *p = 85. Go also has new, which makes a fresh box that you reach only through a pointer.
new(T) = make a new zero-value T and give back a pointer to it.
new()
package main
import "fmt"
func main() {
count := new(int)
fmt.Println(*count)
*count = 3
*count++
fmt.Println(*count)
}
0
4
new(int)makes a box holding0, and returns*int.- This box has no name. You reach it only through
count.
Nil Pointers
Nil pointer = a pointer that points to nothing.
Check for nil first
package main
import "fmt"
func main() {
var p *int
fmt.Println(p == nil)
if p != nil {
fmt.Println(*p)
} else {
fmt.Println("p points to nothing")
}
}
true
p points to nothing
Following a nil pointer
package main
import "fmt"
func main() {
var p *int
fmt.Println(*p)
}
- Following a nil pointer stops the program.
- The message is
panic: runtime error: invalid memory address or nil pointer dereference. - Always check
p != nilwhen a pointer might be empty.
A pointer you declare with var p *int is nil until you point it somewhere.
Give it an address first: p = &x or p = new(int).
No Pointer Arithmetic
- In C, you can do
p + 1to move to the next box. - Go does not allow this. It prevents a whole class of memory bugs.
Simple lesson: change values with *p = ..., make pointers with & or new, and check for nil before following one.
Pointers in Functions
Pass by Value
Pass by value = a function gets a copy of each argument. See Functions.
Copy vs pointer
Pointer Parameters
Pointer parameter = a parameter of type *T. The caller passes an address with &.
Swap two values
package main
import "fmt"
func swap(a, b *string) {
*a, *b = *b, *a
}
func main() {
first, second := "Asha", "Ravi"
swap(&first, &second)
fmt.Println(first, second)
}
Ravi Asha
Updating Original Values
- Use a pointer parameter when the function's job is to change the caller's value.
- Otherwise, prefer returning a new value. It is easier to read.
- Slices and maps already share their data, so you rarely need a pointer to them.
Easy way to remember
- Function needs to change your variable → pass
&x, take*T. - Function only reads it → pass
x.
Simple lesson: Go passes copies, so pass a pointer when a function must change the original.
Pointers and Structs
Struct = a group of named fields, like a student record. You will learn structs fully in Structs and Methods.
Struct Pointers
A pointer to a struct
package main
import "fmt"
type Student struct {
Name string
Marks int
}
func main() {
s := Student{Name: "Meena", Marks: 80}
p := &s
p.Marks = 88
fmt.Println(s.Marks)
q := &Student{Name: "Kabir", Marks: 75}
fmt.Println(q.Name, q.Marks)
}
88
Kabir 75
&Student{...}makes a struct and gives a pointer to it in one step.
Accessing Struct Fields Through Pointers
- With a pointer
pto a struct, writep.Marks. - Go reads it as
(*p).Marksfor you. - This automatic step is why struct pointers are easy to use.
A function that updates a struct
package main
import "fmt"
type Student struct {
Name string
Marks int
}
func addGrace(s *Student, n int) {
s.Marks += n
}
func main() {
asha := Student{"Asha", 32}
addGrace(&asha, 3)
fmt.Println(asha)
}
{Asha 35}
- Without the pointer,
addGracewould change only a copy ofasha.
Simple lesson: use &Struct{...} and p.Field to work with struct pointers. Go follows the pointer for you.
TL;DR
- Pointer = holds the address of a value. Type
*T. Zero valuenil. &x= the address ofx.*p= the value atp.*p = v= change the value through the pointer.new(T)= a new zeroT, returned as*T.- Nil pointer = points to nothing. Following it panics. Check
p != nil. - Pass by value = functions get copies. Pass
&xto let them changex. - Struct pointer =
p := &Student{...}, thenp.Marks(Go adds the*). - No pointer arithmetic in Go.
Common mistakes
| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
var p *int then *p = 5 | A pointer comes with a box | A new pointer is nil | A nil pointer panic. Use new(int) or &x. |
addBonus(marks) with a *int parameter | Go converts it | Pass the address: &marks | cannot use marks (variable of type int) as *int |
p = 85 to change the value | = goes through the pointer | Use *p = 85 | A type error. |
| Changing a parameter to change the caller | Parameters are shared | They are copies | The caller's value does not change. |
p + 1 to move to the next value | Go has pointer arithmetic | It does not | A build error. |
| Pointers to slices and maps everywhere | They are always needed | Slices and maps already share data | Harder code for no gain. |
Not checking nil | Pointers are always set | They may be nil | A crash at runtime. |
Check Yourself
Exercises
What to Remember
- A pointer stores an address. Its type is
*T, and its zero value isnil. &xgives a pointer.*pfollows it.- Functions get copies. Pass
&xwhen a function must changex. - Check
p != nilbefore following a pointer that might be empty. - With a struct pointer,
p.Fieldworks. Go adds the*for you.
Next: Structs and Methods. You will learn how to group related values into your own types, and give them behaviour.