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 = 99
when, 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: 0xc00000a0c8
when we want to use this space, we can use the address to Visit, you can also use the name we gave it x
to visit.
Continues to run var p *int = &x
when we define a pointer variable p
, which p
is stored the variable x
address.
Therefore, the pointer is the address, and the pointer variable is the variable that stores the address.
Then, we change x
the 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, x
with the *p
same result.
Wherein the *p
called 解引用
or 间接引用
.
*p = 999
By means of x
a variable address, to operate x
the 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.
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 stack
on.
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 = 99
time, a space is generated in the stack frame inside.
Similarly, to run var p *int = &x
it will also have a space in the stack frame time.
As shown below:
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.
When test()
you run is finished, it will relieve the stack frame.
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 T
type of anonymous variable done is to T
type 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 T
type 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 *p
is 0
.
package main
import "fmt"
func main(){
p := new(int)
fmt.Println(p)
fmt.Println(*p)
}
In this case p
it 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 *p
the value:
package main
import "fmt"
func main(){
p := new(int)
*p = 1000
fmt.Println(p)
fmt.Println(*p)
}
This time to pay attention, *p = 1000
in *p
the fmt.Println(*p)
middle of *p
is 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 = 20
in y
the x = y
middle of y
the same is not the same?
Conclusion: not the same
var y int = 20
The y
representative of the memory space , we generally call this left value ; and x = y
the y
representative 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 = 1000
it is the meaning of 1000 is written to *p
memory go;
fmt.Println(*p)
Is the *p
data 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 p
still there?
p
Where? Stack frame is on the stack, and p
because it is new()
generated, so 堆
, so on p
does not disappear, p
the 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 x
and y
two variables.
When running swap()
, the system produces a stack frame on the stack area, the stack frame inside x
and y
two variables.
Operation x, y = y, x
, the exchange of swap()
the stack frame is generated in the xy
value. In this case main()
there is xy
no change.
swap()
After the operation is completed, the corresponding stack frame is released, and the x
y
value 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, x
with y
the value of the exchange is complete.
Let's analyze this process.
First run main()
after creating a stack frame, there are x
y
two variables.
Run swap2()
time, also create a stack frame, there are a
b
two variables.
Note that at this time, a
and b
stored value x
and y
address.
When running to *a, *b = *b, *a
time, on the left *a
represents the x
memory address, the right side *b
represents the y
contents of the memory address. So this time, main()
is x
to 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.