Memo for Golang learning

Pointer

Use var var_name *var-type to define pointer type, * marks this var is a pointer of a type.

var p *int
var fp *float32

The value &p will be a pointer’s memery address.

p := 42
fmt.Printf("Pointer memory address: %x\n", &p  )

To give pointer a memory value.

i := 42  
var p *int
p = &i
// var p *int = &i
fmt.Printf("Pointer memory address: %x\n", &p  )

Check out following example to better understand this:

func main() {
	i,l := 42,50
	fmt.Printf("l's memory address: %x\n", &l  )
	fmt.Printf("i's memory address: %x\n", &i  )
    var p *int = &i // set p to be i's memory address, it's same as define p:= &i
    fmt.Printf("print i's memory address: %x\n", p  )
    fmt.Printf("print i's memory address's memory address: %x\n", &p  )
	x := &p
    fmt.Printf("print x value: %x\n", x  ) // this should equals to &p
    fmt.Printf("print &p's memory address: %x\n", &x  )
    fmt.Printf("p's Actual value: %d\n", *p  )  // print this p's acutal value which is i's value
    *p = 21  // reset *p(i) to be 21
    fmt.Printf("p's Actual value: %d\n", *p  )  // The * operator denotes the pointer's underlying value, p is a pointer &i, changing *p is actually changing i's value
    fmt.Printf("i's Actual value: %d\n", i  )
}

output:

l's memory address: c000126008
i's memory address: c000126000
print i's memory address: c000126000
print i's memory address's memory address: c000120020
print x value: c000120020
print &p's memory address: c000120028
p's Actual value: 42
p's Actual value: 21
i's Actual value: 21

Receiver and Method

func (t test) TestValue() {} this type of func is called receiver method. (t test) is its value receiver. When use a pointer to be receiver, it’s called pointer receiver func (t *test) TestValue() {}. Regular value func use predefined var as input and use its copy to run, but leave the original var no changed. On the other hand, Pointer recevier func will change the orignal var as it acutally interact with vars.

package main

import (
	"fmt"
	"math"
)

type Vertex struct {
	X, Y float64
}

func (v Vertex) Abs() float64 {
    fmt.Println(v.X*v.X + v.Y*v.Y)
	return math.Sqrt(v.X*v.X + v.Y*v.Y)
}

func (v *Vertex) Scale(f float64) {
	v.X = v.X * f
	fmt.Println(v.X)
	v.Y = v.Y * f
	fmt.Println(v.Y)
}

func main() {
	v := Vertex{3, 4}
	v.Scale(10)
	fmt.Println(v.Abs())
}

output:

30
40
2500
50

if we change func (v *Vertex) Scale(f float64) to be func (v Vertex) Scale(f float64):

30
40
25
5

Compare the resulats we can see that with * it modify the original Vertex{3, 4} in Scale, or did nothing if we remove *. As a convenience, Go interprets the statement v.Scale(5) as (&v).Scale(5) since the Scale method has a pointer receiver.

This code can also be written in regualr func, and have the same result:

func Scale(v *Vertex, f float64) {
	v.X = v.X * f
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	Scale(&v, 10)
	fmt.Println(Abs(v))
}

Note that we have &v in Scale(&v, 10) here. In fact it says use Vertex memory address as Scale input var, and in the Scale func it says v is a pointer in type Vertex, any action inside this func against v performs based on its pointer value.

Following example shows how confusing this method can be:

func Scale(v *Vertex, f float64) {
    fmt.Println(v)    //&{3 4}
	fmt.Println(v.X)  //3
	v.X = v.X * f
	fmt.Println(v.X)
	v.Y = v.Y * f
}

func main() {
	v := Vertex{3, 4}
	fmt.Println(v.X)   //3
	fmt.Println(&v)    //&{3,4}
	fmt.Println(&v.X)  //0xc00010a000
	fmt.Println(&v.Y)  //0xc00010a008
	Scale(&v, 10)      //30
	fmt.Println(Abs(v))//50
}