[Golang Advanced] Detailed explanation of pointers

A pointer is a value that represents a memory address. This memory address is often the starting position of the value of another variable stored in memory.

Go's support for pointers is between Java and C / C ++. It does not cancel the direct operation of code on pointers like Java, nor avoids the abuse of pointers in C / C ++. Security and reliability issues.

Pointer address and variable space

The Go language retains the pointer, but it is different from the C language pointer. It is mainly reflected in:

  • Default value: nil.
  • Operator &takes a variable address, *the object through a pointer to access a target.
  • Does not support pointer arithmetic, is not supported by ->operators, with direct .access to the target member.

First look at a piece of code:

package main

import "fmt"

func main(){ 
	var x int = 99
	var p *int = &x

	fmt.Println(p)
}

When we run into var x int = 99when, in memory will generate a space, this space we gave it a name x, at the same time, it also has an address, for example: 0xc00000a0c8when we want to use this space, we can use the address to Visit, you can also use the name we gave it xto visit.

Continues to run var p *int = &xwhen we define a pointer variable p , which pis stored the variable xaddress.

Therefore, the pointer is the address, and the pointer variable is the variable that stores the address.

Then, we change xthe content:

package main

import "fmt"

func main() {
	var x int = 99
	var p *int = &x

	fmt.Println(p)

	x = 100

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)
	
	*p = 999

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)
}

It can be found, xwith the *psame result.

Wherein the *pcalled 解引用or 间接引用.

*p = 999By means of xa variable address, to operate xthe corresponding space.

Whether it is x or not *p , we operate the same space.

Memory layout of stack frame

First, let's look at the memory layout map to 32位an example.

image

Among them, the data area stores the initialized data.

The above code is stored in the stack area generally make()or new()out are stored in the stack area

Next, let's understand a new concept: stack frame .

Stack frame: used to function to provide operating memory, memory to take stackon.

When the function is called, a stack frame is generated; when the function is called, the stack frame is released.

So what is the stack frame used to store?

  • Local variables
  • Formal parameter
  • Memory field description value

Among them, the shape participates in the storage status of local variables is equivalent

When our program runs, it runs first main(), then a stack frame is generated.

When the running var x int = 99time, a space is generated in the stack frame inside.

Similarly, to run var p *int = &xit will also have a space in the stack frame time.

As shown below:

image

Let's add a function and study again.

package main

import "fmt"

func test(m int){
	var y int = 66
	y += m
}

func main() {
	var x int = 99
	var p *int = &x

	fmt.Println(p)

	x = 100

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)

	test(11)

	*p = 999

	fmt.Println("x: ", x)
	fmt.Println("*p: ", *p)
}

, When the operation below test(11), the frame will continue to produce a stack, when the main()stack frame is generated is not over.

image

When test()you run is finished, it will relieve the stack frame.

image

Null pointer and wild pointer

Null pointer : Uninitialized pointer.

var p *int

At this time, if we want to operate on its value *p, we will report an error.

Wild pointer : Initialized by an invalid address space.

var p *int = 0xc00000a0c8

Memory storage of pointer variables

Expression new(T)will create a Ttype of anonymous variable done is to Ttype the new value allocated and cleared a piece of memory space, then this memory address space returned as a result, and this result is a point to this new Ttype value Pointer value, the returned pointer type is *T.

new()Create a memory located on the heap, the default value is a default value of the spatial data types, such as: p := new(int)it *pis 0.

package main

import "fmt"

func main(){
	p := new(int)
	fmt.Println(p)
	fmt.Println(*p)
}

In this case pit is no longer empty pointer or a pointer field.

We simply use the new()functions without worrying about their memory of the life cycle or how to remove it, because the Go language memory management system will help us take care of everything.

Then we change *pthe value:

package main

import "fmt"

func main(){
	p := new(int)
	
	*p = 1000
	
	fmt.Println(p)
	fmt.Println(*p)
}

This time to pay attention, *p = 1000in *pthe fmt.Println(*p)middle of *pis the same as you?

Let's think about it first, and then look at a simple example:

var x int = 10
var y int = 20
x = y

Well, we think about the above code, var y int = 20in ythe x = ymiddle of ythe same is not the same?

Conclusion: not the same

var y int = 20The yrepresentative of the memory space , we generally call this left value ; and x = ythe yrepresentative of the contents of the memory space , we generally call it the right value .

x = y It means that the content of the y corresponding memory space is written to the x memory space.

The variable on the left side of the equal sign represents the memory space pointed to by the variable , which is equivalent to a write operation.

The variable on the right side of the equal sign represents the data value stored in the variable memory space , which is equivalent to a read operation.

After understanding this, let's look at the previous code again.

p := new(int)

*p = 1000

fmt.Println(*p)

So, *p = 1000it is the meaning of 1000 is written to *pmemory go;

fmt.Println(*p)Is the *pdata value stored in the memory space print.

So these two are not the same.

What happens if we don't create it in main ()?

func foo() {
	p := new(int)

	*p = 1000
}

We already said, when run foo()time will produce a stack frame, end of run, release the stack frame.

So at this time, is it pstill there?

pWhere? Stack frame is on the stack, and pbecause it is new()generated, so , so on pdoes not disappear, pthe corresponding memory value does not disappear, so take advantage of this we can achieve transfer address .

For the heap area, we usually think it is unlimited. But the premise of unlimited is that you must apply for use, and release it immediately after use.

Function parameters

Knowing the above, it will be a lot easier to understand pointers as function parameters .

Pass address (reference): Pass the address value as a function parameter.

Passing value (data): copy the value of the actual parameter to the formal parameter.

Regardless of whether the address or value is passed, it is the actual parameter that copies its value to the formal parameter. However, this value may be an address, or it may be data.

Therefore, function parameters are always passed by value.

After understanding the concept, let's look at a classic example:

package main

import "fmt"

func swap(x, y int){
	x, y = y, x
	fmt.Println("swap  x: ", x, "y: ", y)
}

func main(){
	x, y := 10, 20
	swap(x, y)
	fmt.Println("main  x: ", x, "y: ", y)
}

result:

swap  x:  20 y:  10
main  x:  10 y:  20

Let's briefly analyze why it is different.

When first run main(), the system generates a stack area in the stack frame, the stack frame there xand ytwo variables.

When running swap(), the system produces a stack frame on the stack area, the stack frame inside xand ytwo variables.

Operation x, y = y, x, the exchange of swap()the stack frame is generated in the xyvalue. In this case main()there is xyno change.

swap()After the operation is completed, the corresponding stack frame is released, and the x yvalue in the stack frame also disappears.

So, when running fmt.Println("main x: ", x, "y: ", y)these words, its value remains unchanged.

Next we look at the situation when the parameter is an address value.

The core idea of ​​passing addresses is to modify the values ​​in other stack frame spaces in its own stack frame space.

The idea of ​​passing values ​​is: modify the values ​​in your stack frame space in your stack frame space.

Pay attention to understand the difference.

Continue to see the following code:

package main

import "fmt"

func swap2(a, b *int){
	*a, *b = *b, *a
}

func main(){
	x, y := 10, 20
	swap(x, y)
	fmt.Println("main  x: ", x, "y: ", y)
}

result:

main  x:  20 y:  10

Here did not violate 函数传参永远都是值传递the sentence, but this time the value of the address value.

This time, xwith ythe value of the exchange is complete.

Let's analyze this process.

First run main()after creating a stack frame, there are x ytwo variables.

Run swap2()time, also create a stack frame, there are a btwo variables.

Note that at this time, a and b stored value x and y address.

When running to *a, *b = *b, *atime, on the left *arepresents the xmemory address, the right side *brepresents the ycontents of the memory address. So this time, main()is xto be replaced.

So, this is the variable value swap2() in the operation main() .

Now swap2()and then release it does not matter, because main()there's value has been changed.

Guess you like

Origin www.cnblogs.com/BlameKidd/p/12709216.html