Golang has pointers. The Go language's support for pointers is between the Java language and the C/C language. It neither cancels the code's ability to directly operate pointers like Java, nor does it avoid the abuse of pointers in C/C. Security and reliability issues.
The operating environment of this tutorial: windows10 system, GO 1.11.2, thinkpad t480 computer.
A pointer is a value that represents a certain memory address. This memory address is often the starting position of the value of another variable stored in memory.
Go language retains pointers, but they are different from C language pointers. Mainly reflected in:
Default value: nil
Operator&
Takes the variable address, *
accesses the target object through the pointer.
does not support pointer arithmetic and does not support the ->
operator. Directly use .
to access the target member.
Let’s look at a piece of code first:
1 2 3 4 5 6 7 8 9 |
|
When we run to var x int = 99
, a space will be generated in the memory , we named this space x
, and at the same time, it also has an address, for example: 0xc00000a0c8
, when we want to use this space, we can use Address to access, or you can use the name we gave it x
to access.
Continue running to var p *int = &x
, we defined a pointer variable p
, this p
stores the address of the variable x
.
So, The pointer is the address, and the pointer variable is the variable that stores the address.
Next, we change the content of x
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
We can find that x
has the same result as *p
of.
Among them, *p
is called dereference
or indirect reference
.
*p = 999
uses the address of the x
variable to operate the space corresponding to x
.
Whether it is x
or *p
, we are all operating in the same space.
First of all, let’s take a look at the memory layout diagram, taking 32-bit
as an example.
Among them, data area saves the initialized data.
The above codes are stored in stack area. Generally, what comes out of make()
or new()
is stored in the heap area
Next, let’s learn about a new Concept: Stack frame.
Stack frame: used to provide memory space for function to run, and the memory is taken from stack
.
When a function is called, a stack frame is generated; when the function call ends, the stack frame is released.
So what is the stack frame used to store?
Among them, Formal participation in local variable storage The status is the same as
When our program is running, it first runs main()
, and a stack frame is generated.
When running to ## When #var x int = 99, a space will be generated in the stack frame.
var p *int = &x, there will also be a space in the stack frame. A space is generated in it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
test(11), a stack frame will continue to be generated. At this time, the stack frame generated by
main() has not ended yet.
test() finishes running, this stack frame will be released.
Null pointer: An uninitialized pointer.
1 |
|
*p, an error will be reported.
Wild pointer: Initialized by an invalid address space.
1 |
|
new(T) will create an anonymous variable of type
T. What it does is allocate and clear a memory space for a new value of type
T, and then set the The address is returned as a result, and this result is a pointer value pointing to this new
T type value. The returned pointer type is
*T.
new()
创建的内存空间位于heap上, 空间的默认值为数据类型的默认值. 如: p := new(int)
则 *p
为 0
.
1 2 3 4 |
|
这时 p
就不再是空指针或者野指针.
我们只需使用 new()
函数, 无需担心其内存的生命周期或者怎样将其删除, 因为Go语言的内存管理系统会帮我们打理一切.
接着我们改一下*p
的值:
1 2 3 4 5 6 7 |
|
这个时候注意了, *p = 1000
中的 *p
与 fmt.Println(*p)
中的 *p
是一样的吗?
大家先思考一下, 然后先来看一个简单的例子:
1 |
|
好, 大家思考一下上面代码中, var y int = 20
中的 y
与 x = y
中的 y
一样不一样?
结论: 不一样
var y int = 20
中的 y
代表的是内存空间, 我们一般把这样的称之为左值; 而 x = y
中的 y
代表的是内存空间中的内容, 我们一般称之为右值.
x = y
表示的是把 y
对应的内存空间的内容写到x内存空间中.
等号左边的变量代表变量所指向的内存空间, 相当于写操作.
等号右边的变量代表变量内存空间存储的数据值, 相当于读操作.
在了解了这个之后, 我们再来看一下之前的代码.
1 |
|
所以, *p = 1000
的意思是把1000写到 *p
的内存中去;
fmt.Println(*p)
是把 *p
的内存空间中存储的数据值打印出来.
所以这两者是不一样的.
如果我们不在main()创建会怎样?
1 2 3 4 |
|
我们上面已经说过了, 当运行 foo()
时会产生一个栈帧, 运行结束, 释放栈帧.
那么这个时候, p
还在不在?
p
在哪? 栈帧是在栈上, 而 p
因为是 new()
生成的, 所以在 堆
上. 所以, p
没有消失, p
对应的内存值也没有消失, 所以利用这个我们可以实现传地址.
对于堆区, 我们通常认为它是无限的. 但是无限的前提是必须申请完使用, 使用完后立即释放.
明白了上面的内容, 我们再去了解指针作为函数参数就会容易很多.
传地址(引用): 将地址值作为函数参数传递.
传值(数据): 将实参的值拷贝一份给形参.
无论是传地址还是传值, 都是实参将自己的值拷贝一份给形参.只不过这个值有可能是地址, 有可能是数据.
所以, 函数传参永远都是值传递.
了解了概念之后, 我们来看一个经典的例子:
1 2 3 4 5 6 |
|
结果:
1 |
|
我们先来简单分析一下为什么不一样.
首先当运行 main()
时, 系统在栈区产生一个栈帧, 该栈帧里有 x
和 y
两个变量.
当运行 swap()
时, 系统在栈区产生一个栈帧, 该栈帧里面有 x
和 y
两个变量.
运行 x, y = y, x
后, 交换 swap()
产生的栈帧里的 xy
值. 这时 main()
里的 xy
没有变.
swap()
运行完毕后, 对应的栈帧释放, 栈帧里的x
y
值也随之消失.
所以, 当运行 fmt.Println("main x: ", x, "y: ", y)
这句话时, 其值依然没有变.
接下来我们看一下参数为地址值时的情况.
传地址的核心思想是: 在自己的栈帧空间中修改其它栈帧空间中的值.
而传值的思想是: 在自己的栈帧空间中修改自己栈帧空间中的值.
注意理解其中的差别.
继续看以下这段代码:
1 2 3 4 5 |
|
结果:
1 |
|
这里并没有违反 函数传参永远都是值传递
这句话, 只不过这个时候这个值为地址值.
这个时候, x
与 y
的值就完成了交换.
我们来分析一下这个过程.
首先运行 main()
后创建一个栈帧, 里面有 x
y
两个变量.
运行 swap2()
时, 同样创建一个栈帧, 里面有 a
b
两个变量.
Note that at this time, the values stored in a
and b
are x
and y
The address of .
When running to *a, *b = *b, *a
, the left The *a
represents the memory address of x
, and the *b
on the right represents the content of the memory address of y
. So at this time , x
in main()
is replaced.
So, This is in swap2()
Operation in main()
The variable value in
Nowswap2()
It doesn’t matter if it is released again , because the value in main()
has been changed.
Recommended learning: Golang tutorial
The above is the detailed content of Does golang have pointers?. For more information, please follow other related articles on the PHP Chinese website!