Other examples:
Pointer
a gentle introduction to Pointers
When assigning a value to a variable in Go, the value is stored at a particular address in the computer’s memory.
Use the reference operator &
to get this address, like so:
package main
import "fmt"
func main() {
var answer int = 42
fmt.Println(&answer) // prints something like 0x14000122008
}
When using the &
operator on a variable it actually returns a pointer.
In simple terms, a pointer is a variable that hols the memory address of another variable. Think of them as “pointing to” a specific spot in memory.
Like the variables that they point to, pointers in Go also have types. A pointer with the type *int
can only hold the memory address of an int
variable. And a pointer with the type *string
can only hold the memory address of a string
variable. And so on.
package main
import "fmt"
func main() {
var answer int = 42
var answerPtr *int = &answer
fmt.Println(answerPtr) // prints something like 0x1400012c008
}
Use the deference operator *
to read or set the underlying value that a pointer points to. This is often known as indirection.
package main
import "fmt"
func main() {
answer := 42
answerPtr := &answer
fmt.Println(answerPtr) // prints something like 0x14000122008
fmt.Println(*answerPtr) // prints 42
*answerPtr = 99 // use the dereference operator to assign a new value
fmt.Println(answer) // prints 99
}
a one-pager to understanding pointers in Go
package main
import "fmt"
func main() {
// a pointer is a variable that holds the memory address of another variable
var p1 *int
x := 42
y := 40
// to create a pointer to a variable, use the & operator followed by the variable's name
p1 = &x
p2 := &y
// to access the value of the variable pointed to by a pointer, you use the * operator followed by the pointer variable name
*p1 = 100
increment(&y)
increment(p2)
fmt.Println(x)
fmt.Println(p2)
fmt.Println(&p2)
fmt.Println(*p2)
}
// passing pointers to functions
// one common use of pointers is to pass a variable to a function by reference, allowing the function to modify the variable directly.
func increment(p *int) {
*p++
}
package main
import "fmt"
func main() {
var creature string = "shark"
var pointer *string = &creature
fmt.Println("creature = ", creature)
fmt.Println("pointer = ", pointer)
fmt.Println("*pointer = ", *pointer)
fmt.Println("&creature = ", &creature)
// output
// creature = shark
// pointer = 0xc000014260
// *pointer = shark
// &creature = 0xc000014260
var creatureB Creature = Creature{Species: "shark"}
fmt.Printf("1) %+v\n", creatureB)
changeCreature(&creatureB)
fmt.Printf("3) %+v\n", creatureB)
// output
// 1) {Species:shark}
// 2) &{Species:jellyfish}
// 3) {Species:jellyfish}
var creatureC *Creature = &Creature{Species: "shark"}
fmt.Printf("1) %+v\n", creatureC)
changeCreature(creatureC)
fmt.Printf("3) %+v\n", creatureC)
// output
// 1) {Species:shark}
// 2) &{Species:jellyfish}
// 3) &{Species:jellyfish}
creatureD := &Creature{Species: "shark"}
fmt.Printf("1) %+v\n", creatureD)
changeCreature(creatureD)
fmt.Printf("3) %+v\n", creatureD)
// output
// 1) {Species:shark}
// 2) &{Species:jellyfish}
// 3) &{Species:jellyfish}
}
type Creature struct {
Species string
}
func changeCreature(creature *Creature) {
creature.Species = "jellyfish"
fmt.Printf("2) %+v\n", creature)
}
Pointer in function
Function argument passing is value copying, which also applies to pointer arguments. When doubleInt(p)
is called, a copy of pointer p
is created first and then passed to the function doubleInt
. The modification on the copy of the pointer p
in doubleInt
will not reflect on the original pointer p
in the main function.
pointer in function sample
package main
import "fmt"
func doubleInt(x int) {
x = x * 2
}
func main() {
a := 1
doubleInt(a)
fmt.Println(a) // 1
}
package main
import "fmt"
func doubleInt(x *int) {
*x = *x * 2
x = nil // update the pointer x to nil
}
func main() {
a := 1
p := &a
doubleInt(p)
fmt.Println(a) //2 fmt.Println(p == nil) //false
}
NIL pointer
The zero value of a pointer type is nil. if you try to dereference a pointer which points to nothing (nil
), it will cause a runtime panic. With that being said, we should always check if the pointer is nil
before trying to dereference it.
a nill pointer sample
package main
import "fmt"
func main() {
var ptr *int
fmt.Println(ptr) // <nil>
ptrValue := *ptr // panic: runtime error: invalid memory address or nil
fmt.Println(ptrValue)
}
What really is unsafe.Pointer
?
To get a start, let’s examine its definition in the code:
type Pointer *ArbitraryType
Unlike a *int
, which can only point to an int
, or a *bool
, which is restricted to bool values, unsafe.Pointer
has the liberty to point to any arbitrary type, sweet flexibility right?
unsafe.Pointer
is a special kind of pointer that turns off the usual safety rules. When you use it, you’re telling the Go compiler: “I know what I’m doing, so trust me”. Be careful, because you’re going around the normal safety checks.
var a int64 = 10
aPtr := unsafe.Pointer(&a)
b := (*float64)(aPtr)