我不明白 Go 变量的行为。我要你告诉我。请参阅下面的示例实现。
package main
import (
"fmt"
)
func pointer(ip *Num) {
fmt.Printf("pointer type [%T] : %p\n", &ip, &ip)
}
func pointerpointer(ip **Num) {
fmt.Printf("pointerpointer type [%T] : %p\n", ip, ip)
}
func main() {
pnum := &Num{i: 3}
fmt.Printf("main type [%T] : %p\n", &pnum, &pnum)
pointer(pnum)
pointerpointer(&pnum)
}
type Num struct {
i int
}
https://play.golang.org/p/LxDAgopxeh0
- 结果
main type [**main.Num] : 0x40c138
pointer type [**main.Num] : 0x40c148
pointerpointer type [**main.Num] : 0x40c138
我将 struct Num 指针存储为变量 [pnum]。 this传递给指针函数时可获取的地址与main函数中可获取的地址不同。为什么??
已经证实,通过指针指针函数的引用,可以得到与main函数相同的地址。
最佳答案
pnum
,在 main
中,是一个实际的变量:一个盒子,漂浮在内存中,包含一个 *Num
类型的指针。
0x40c138
+--------+
| *---|--->
+--------+
pnum
指向什么?好吧,Num{i: 3}
创建了一个未命名的变量,它漂浮在内存中的某个地方。我们还没有打印出来。我 modified your Playground example to add one more fmt.Printf
call找出:
main type [**main.Num] : 0x40c138
pnum points to [*main.Num] : 0x40e020
或:
0x40c138 0x40e020
+--------+ +--------+
| *---|---> | i: 3 |
+--------+ +--------+
现在让我们继续:main
调用 pointer
,将存储在 pnum
中的 value 传递给它—— 0x40c138
值,它让计算机找到包含未命名实例的盒子,该实例包含 i=3
。该值存储在某处的内存中。那是什么地方?为什么,这是你打印的数字,因为你打印了&ip
。幸运的是 Playground 是非常确定的,所以它也是我打印的:
pointer type [**main.Num] : 0x40c148
所以我们可以在绘图中添加另一个框:
0x40c138 0x40e020
+--------+ +--------+
|0x40e020|---> | i: 3 |
+--------+ +--------+
^
0x40c148 |
+--------+ |
|0x40e020|---------+
+--------+
现在 pointer
返回,丢弃位于 0x40c148
处的框,其中包含 0x40e020
,您调用 pointerpointer
,传递&pnum
作为一个值。函数 pointerpointer
现在在某个位置运行一个框。那个位置在哪里?我们不知道;我们从不打印它。我们在那个位置打印盒子里的,盒子里的是0x40c138
:
0x40c138 0x40e020
+--------+ +--------+
|0x40e020|---> | i: 3 |
+--------+ +--------+
^
|
????????
+--------+
|0x40c138|
+--------+
我们可以add one more fmt.Printf
to the program to find where this last box actually is in memory再次运行:
pointerpointer's &ip is [***main.Num] : 0x40c150
所以用 0x40c150
替换八个问号。
这里的总体规则很简单:每个变量都存在于某处,除非编译器可以将其优化掉。获取变量的地址,使用&x
,往往会阻止编译器优化变量本身。 &x
的类型 是指向___
的指针,其中空白是x
的类型。 &x
的值 是变量的地址。
在这种情况下,你
- 制作了一个匿名(未命名)
Num
实例, - 获取它的地址(强制编译器将它放入内存中的某个地方),然后
- 将变量
pnum
设置为该值
(都在main
的第一行)。然后你获取了 pnum
本身的地址,这意味着 pnum
也必须存在于内存中的某个地方。那是我们最初画的两个盒子。您在第二行打印了该地址的类型和值。
然后您将存储在 pnum
中的值传递给 func pointer
,后者将值存储在变量中。这样就创建了另一个独立的变量,它有自己的地址。
无需担心函数 pointer
做了什么,然后将 pnum
的地址 传递给 func pointerpointer
。该地址是一个值,pointerpointer
将该值存储在一个变量中。这也创建了另一个单独的变量。
在所有这些过程中,i=3
的匿名Num
的地址从未移动过。 pnum
本身的地址也没有。
关于pointers - 在 Go 中,结构指针不是引用类型吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59041891/