指针 pointers
Under the hood
Definition
A pointer is a variable that stores the memory address of another variable. This means that a pointer "points to" the location of where the data is stored NOT the actual data itself.
Pointers hold the memory address of a value. A pointer's zero value is nil
.
The *
syntax defines a pointer:
var p *int
The &
operator generates a pointer to its operand.
myString := "hello"
myStringPtr = &myString // type -> *string
The *
dereferences a pointer to gain access to the value
fmt.Println(*myStringPtr) // read myString through the pointer
*myStringPtr = "world" // set myString through the pointer
Why are pointers useful ?
Pointers allow us to manipulate data in memory directly, without making copies or duplicating data. This can make programs more efficient and allow us to do things that would be difficult or impossible without them.
User case
- 你想将 primitif 变量传入函数,并通过函数修改变量的同时影响到函数外的值
- 你希望拥有更高的性能,避免 primitif 变量的复制行为
Just because you can doesn't mean you should
We're doing this exercise to understand that pionters can be used in this way. That said, pionters can be very dangerous. It's generally a better idea to have your functions accept non-pointers and return new values rather thant mutating pointer inputs.
// This func is not safe operation
func removeProfanity(message *string) {
messageVal := *message
messageVal = strings.ReplaceAll(messageVal, "dang", "****")
messageVal = strings.ReplaceAll(messageVal, "shoot", "****")
messageVal = strings.ReplaceAll(messageVal, "heck", "****")
*message = messageVal
}
Nil pointers
Pointers can be very dangerous.
If a pointer points to nothing (the zero value of the pointer type) then dereferencing it will cause a runtime error (a panic) that crashes the program. Generally speaking, whenever you're dealing with pointers you should check if it's nil
before trying to dereference it.
func removeProfanity(message *string) error {
if message == nil {
return erros.New("invalid input")
}
messageVal := *message
messageVal = strings.ReplaceAll(messageVal, "dang", "****")
messageVal = strings.ReplaceAll(messageVal, "shoot", "****")
messageVal = strings.ReplaceAll(messageVal, "heck", "****")
*message = messageVal
}
Pointer receivers
A receiver type on a method can be a pointer.
Methods with pointer receivers can modify the value to which the receiver points. Since methods often need to modify their receiver, pointer receivers are more common than value receivers.
type car struct {
color string
}
func (c *car) setColor (color string) {
c.color = color
}
func main() {
c := car{
color: "white"
}
c.setColor("blue")
fmt.Println(c.color)
// prints "blue"
}
Non-pointer receiver
type car struct {
color string
}
func (c car) setColor (color string) {
c.color = color
}
func main() {
c := car{
color: "white"
}
c.setColor("blue")
fmt.Println(c.color)
// prints "white"
}
Questions
- which is more widely used in Go? Pointer receivers
Pointer receiver code
Methods with pointer receivers don't require that a pointer is used to call the method. The pointer will automatically be derived from the value.
type circle struct {
x int
y int
radius int
}
func (c *circle) grow() {
c.radius *= 2
}
func main() {
c := circle{
x: 1,
y: 2,
radius: 4,
}
// notice c is not a pointer in the calling function
// but the method still gains access to a pointer to c
c.grow()
fmt.Println(c.radius)
// prints 8
}