Borrowing where variables exist, shepherds laugh with pointers, go lang 1.18 introductory refined tutorial, from Bai Ding to Hongru, go lang type pointer (Pointer) using EP05

What does the pointer mean? A pointer is a variable that stores the address of another variable in memory. A variable is a convenient placeholder for referencing a computer's memory address. A pointer variable can point to the memory address of any value and it can point to the memory address of that value. By analogy, the pointer is the catalog in a book and occupies the pages of the book. It can either get the contents of a chapter from the catalog or point to the number of pages (addresses) of a specific chapter.

Pointer declaration

Declares a pointer, *T is the type of pointer variable that points to a value of type T:

var var_name *var-type

var-type is pointer type, var_ The name is the pointer variable name and the * sign is used to specify that the variable is used as a pointer.

For example:

var ip *int        /* Point to Integer*/  
var fp *float32    /* Point to Floating Point */

Previously, we used the &keyword to get the address of a variable in memory. In fact, the object is a pointer:

package main  
  
import "fmt"  
  
func main() {  
	var a int = 20 /* Declare actual variables */  
	var ip *int    /* Declare pointer variables */  
  
	ip = &a /* Storage address of pointer variable */  
  
	fmt.Printf("a The address of the variable is: %x\n", &a)  
  
	/* Storage address of pointer variable */  
	fmt.Printf("ip Storage address of variable: %x\n", ip)  
  
	/* Access values using pointers */  
	fmt.Printf("*ip Value of variable: %d\n", *ip)  
}

Thus, the type of pointer variable is *Type, which points to a variable of type Type. The most important feature of pointer variable is that it stores the memory address of an actual variable and operates indirectly by recording the address of a variable.

&Keyword gets its memory address from a variable.

*Keyword If to the left of the assignment operation value, refers to the variable that the pointer points to;* If the keyword is to the right of the assignment operator, it refers to taking the value of a variable from a pointer variable, also known as the dereference of a pointer.

Pointers are also of different types:

package main  
  
import "fmt"  
  
func main() {  
	mystr := "Character string"  
	myint := 1  
	mybool := false  
	myfloat := 3.2  
	fmt.Printf("type of &mystr is :%T\n", &mystr)  
	fmt.Printf("type of &myint is :%T\n", &myint)  
	fmt.Printf("type of &mybool is :%T\n", &mybool)  
	fmt.Printf("type of &myfloat is :%T\n", &myfloat)  
}

Program returns:

type of &mystr is :*string  
type of &myint is :*int  
type of &mybool is :*bool  
type of &myfloat is :*float64

In fact, the type of pointer is actually the basic type of the variable it refers to, and the two types are identical.

Null pointer

The Go lang null pointer is nil when a pointer is defined and not assigned to any variable. The nil pointer is also known as a null pointer. Conceptually, nil, like null, None, nil, and NULL in other languages, refers to zero or null values. A pointer variable is usually abbreviated as ptr:

if(ptr != nil)     /* ptr Not a null pointer */  
if(ptr == nil)    /* ptr Is a null pointer */

Specific examples:

package main  
  
import "fmt"  
  
func main() {  
	x := "Character string"  
	var ptr *string  
	fmt.Println("ptr is ", ptr)  
	ptr = &x  
	fmt.Println("ptr is ", ptr)  
}

Program returns:

ptr is  <nil>  
ptr is  0xc00003c250

However, it is important to note that null values of pointers, like null values of variables, need to be judged as identical or non-identical, rather than using the is keyword like Python to compare specific addresses in memory.

Pointer operation

Getting a pointer means accessing the value of the variable that the pointer points to. The grammar is: *a

package main  
  
import (  
	"fmt"  
)  
  
func main() {  
	b := 255  
	a := &b  
	fmt.Println("address of b is", a)  
	fmt.Println("value of b is", *a)  
}

Program returns:

address of b is 0xc000014088  
value of b is 255

Normally, we can change the value of a variable by a second assignment, and now we can do that by a pointer:

package main  
  
import (  
	"fmt"  
)  
  
func main() {  
	b := 255  
	a := &b  
	fmt.Println("address of b is", a)  
	fmt.Println("value of b is", *a)  
	*a++  
	fmt.Println("new value of b is", b)  
}

Program returns:

address of b is 0xc0000aa058  
value of b is 255  
new value of b is 256

The value here needs to be incremented by the pointer to modify the value of variable b.

At the same time, pointers can also be used during the pass-through process:

package main  
  
import (  
	"fmt"  
)  
  
func change(val *int) {  
	*val = 55  
}  
func main() {  
	a := 58  
	fmt.Println("value of a before function call is", a)  
	b := &a  
	change(b)  
	fmt.Println("value of a after function call is", a)  
}

Program returns:

value of a before function call is 58  
value of a after function call is 55

As we all know, int is value-type data. If passed through a variable to a method scope, the operation within the method scope is actually another object, such as:

package main  
  
import (  
	"fmt"  
)  
  
func change(val int) {  
	val = 55  
}  
func main() {  
  
	b := 58  
	change(b)  
	fmt.Println("value of a after function call is", b)  
}

Return:

value of a after function call is 58

However, if the pointer is used during the parameter transfer, the object of a variable is passed into the method, and the memory address variable is actually modified within the method, so that the value of the value type object can be changed accordingly, saving additional memory request space.

Suppose we want to make some modifications to the array within the method and the caller can see the changes to the array within the method. One way is to pass a pointer to the array to the method:

package main  
  
import (  
	"fmt"  
)  
  
func modify(arr *[3]int) {  
	(*arr)[0] = 90  
}  
  
func main() {  
	a := [3]int{89, 90, 91}  
	modify(&a)  
	fmt.Println(a)  
}

Program returns:

[90 90 91]

Although you can pass a pointer to an array as an argument to a method and modify it, that doesn't mean we have to do that, because slices can also be used:

package main  
  
import (  
	"fmt"  
)  
  
func modify(sls []int) {  
	sls[0] = 90  
}  
  
func main() {  
	a := [3]int{89, 90, 91}  
	modify(a[:])  
	fmt.Println(a)  
}

Program returns:

[90 90 91]

Because slices are reference types like pointers, if we want to change the value of an array through a function, we can pass the slice of the array as a parameter to the function, or the pointer of the array as a parameter to the function, which is obviously easier to use.

In addition, a pointer can have a pointer, that is, it can also point to the memory address where another pointer is located:

package main  
  
import "fmt"  
  
func main() {  
  
	var a int  
	var ptr *int  
	var pptr **int  
  
	a = 3000  
  
	/* Pointer ptr address */  
	ptr = &a  
  
	/* Point to pointer ptr address */  
	pptr = &ptr  
  
	/* Get the value of pptr */  
	fmt.Printf("variable a = %d\n", a)  
	fmt.Printf("Pointer variable *ptr = %d\n", *ptr)  
	fmt.Printf("Pointer variable to pointer **pptr = %d\n", **pptr)  
}

Program returns:

variable a = 3000  
Pointer variable *ptr = 3000  
Pointer variable to pointer **pptr = 3000

Here the pointer pptr points to the pointer ptr, which executes the variable a

When we change the pointer value of the pointer:

package main  
  
import "fmt"  
  
func main() {  
  
	var a int  
	var ptr *int  
	var pptr **int  
  
	a = 3000  
  
	/* Pointer ptr address */  
	ptr = &a  
  
	/* Point to pointer ptr address */  
	pptr = &ptr  
  
	/* Get the value of pptr */  
	fmt.Printf("variable a = %d\n", a)  
	fmt.Printf("Pointer variable *ptr = %d\n", *ptr)  
	fmt.Printf("Pointer variable to pointer **pptr = %d\n", **pptr)  
  
	**pptr = 200  
  
	fmt.Printf("variable a = %d\n", a)  
	fmt.Printf("Pointer variable *ptr = %d\n", *ptr)  
	fmt.Printf("Pointer variable to pointer **pptr = %d\n", **pptr)  
}

Program returns:

variable a = 3000  
Pointer variable *ptr = 3000  
Pointer variable to pointer **pptr = 3000  
variable a = 200  
Pointer variable *ptr = 200  
Pointer variable to pointer **pptr = 200

You can see that a chain reaction has occurred, with changes in both the start and end directions, which can be described as pulling the trigger all over your body. In fact, pointer manipulation is faster than repeated assignments.

epilogue

In short, many compiled languages have pointers in fact, c/c++ is a real pointer, while Java is a reference to a pointer, which can be understood as pointer values that cannot be manipulated and pointer operations are not allowed. The real problem is, go lang is a new and trendy programming language for the "next era". Why not just implement "reference" like Java, but must give the real concept of "pointer"?

In the Go lang official website documentation, you can get some insight:

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.
Document address: https://go.dev/ref/spec#Calls

At first glance, the designers of go Lang have "perfectionist obsessive-compulsive disorder" in the grammar design of go lang. Method parameter is absolute value transfer. In Go lang, method parameter only has one way of value transfer, there is no reference transfer. Therefore, there must be a clear pointer type to ensure that the object can be modified before value transfer.

Python has also made a compromise here, passing references using variable data types, but go lang, as a straight steel man, prefers to add more complex pointer logic and implement value transfer logic thoroughly, so that the pointer can be used in the right place to increase program speed and memory consumption.

Tags: Go Back-end programming language

Posted by ccb on Fri, 16 Sep 2022 21:52:50 +0300