为了账号安全,请及时绑定邮箱和手机立即绑定

在Go中,结构体指针不是引用类型吗?

在Go中,结构体指针不是引用类型吗?

Go
慕雪6442864 2023-08-07 14:47:59
我不明白 Go 变量的行为。我要你告诉我。请参阅下面的示例实现。package mainimport (    "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] : 0x40c138pointer type [**main.Num] : 0x40c148pointerpointer type [**main.Num] : 0x40c138我将 struct Num 指针存储为变量 [pnum]。将其传递给指针函数时可以获取的地址与在主函数中可以获取的地址不同。为什么??已经证实,通过引用pointer指针作为pointer指针函数,可以获得与main函数相同的地址。
查看完整描述

3 回答

?
小怪兽爱吃肉

TA贡献1852条经验 获得超1个赞

pnum中的main是一个实际变量:一个浮动在内存中的盒子,包含类型为 的指针*Num。


 0x40c138

+--------+

|    *---|--->

+--------+

指向什么pnum?好吧,Num{i: 3}创建了一个未命名的变量,漂浮在内存中的某个地方。我们还没有打印出来。我修改了您的 Playground 示例,添加了一个fmt.Printf调用来找出答案:


main type [**main.Num] : 0x40c138

pnum points to [*main.Num] : 0x40e020

或者:


 0x40c138       0x40e020

+--------+     +--------+

|    *---|---> |  i: 3  |

+--------+     +--------+

现在让我们继续:main调用,将存储在中的值pointer传递给它—该值让计算机找到包含未命名实例的盒子。该值存储在内存中的某个位置。那是在什么地方?为什么,这是您打印的数字,因为您打印了。幸运的是,Playground 具有很强的确定性,所以它也是我打印的:pnum0x40c138i=3&ip


pointer type [**main.Num] : 0x40c148

所以我们可以在绘图中添加另一个框:


 0x40c138       0x40e020

+--------+     +--------+

|0x40e020|---> |  i: 3  |

+--------+     +--------+

                   ^

 0x40c148          |

+--------+         |

|0x40e020|---------+

+--------+

现在返回,丢弃保存 的pointer框,然后调用,作为值传递。函数现在在某个位置与一个框一起运行。那个位置在哪里?我们不知道;我们从不打印它。我们确实在该位置打印了盒子里的东西,盒子里的东西是:0x40c1480x40e020pointerpointer&pnumpointerpointer0x40c138


 0x40c138       0x40e020

+--------+     +--------+

|0x40e020|---> |  i: 3  |

+--------+     +--------+

    ^

    |

 ????????

+--------+

|0x40c138|

+--------+

我们可以在程序中再添加一个,fmt.Printf以查找最后一个框在内存中的实际位置,然后再次运行:


pointerpointer's &ip is [***main.Num] : 0x40c150

因此将八个问号替换为0x40c150。

这里的总体规则很简单:每个变量都在某个地方存在,除非编译器可以优化它。 使用 获取变量的地址&x往往会阻止编译器优化变量本身。的类型,其中&x空白pointer to ___是 的类型x的值是变量&x的地址。

在这种情况下,你

  • 创建了一个匿名(未命名)Num实例,

  • 获取它的地址(强制编译器将其放入内存中的某个位置),并且

  • 将变量设置pnum为该值

(全部在第一行main)。然后你获取了它本身的地址pnum,这意味着它pnum也必须存在于内存中的某个地方。这是我们最初画的两个盒子。您在第二行打印了该地址的类型和值。

然后,您将存储在的值传递给pnumfunc pointer后者将该值存储在变量中。这样就创建了另一个单独的变量,它有自己的地址。

不用担心函数pointer做了什么,然后您将地址传递pnumfunc pointerpointer。该地址是一个值,并将pointerpointer该值存储在变量中。这也创建了另一个单独的变量。

在这整个过程中,匿名者的地址从未动Numi=3。地址pnum本身也没有。



查看完整回答
反对 回复 2023-08-07
?
慕标5832272

TA贡献1966条经验 获得超4个赞

简短的回答是:

指针通过值传递。

该函数pointer接收内存地址的副本。它将副本存储在某个地方(可能是它自己的堆栈或其他东西)。

内存地址只是一个数字。

如果您定义一个数字main并获取其地址,然后将该数字传递给另一个函数并获取地址,您是否应该期望两个地址相同?


查看完整回答
反对 回复 2023-08-07
?
慕婉清6462132

TA贡献1804条经验 获得超2个赞

在Go中,结构体指针不是引用类型吗?

不,只是因为Go没有 引用 类型。

有几种类型具有引用语义,切片是最突出的例子,但即使切片也是值类型而不是引用类型。

Go 中的指针是普通值和真正的机器级地址。当您传递内存地址时,不会发生“引用内容”。如果将内存地址存储在变量中,则可以获取该变量的地址。再次没有类似的参考。在 Go 中它的值一直向下。

基本上就像C中的一样。


查看完整回答
反对 回复 2023-08-07
  • 3 回答
  • 0 关注
  • 131 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信