为什么所有嵌套的struct对象在打印时都具有相同的地址?

时间:2017-09-20 04:19:58

标签: pointers go

我想在a := b中找出a是否是func pass_by_value(a some_struct)

等结构的不同副本

但我发现我不理解印刷声明。

考虑这个go playground

    nested_level2 := test_assign_nested_level2{}
    nested_level1 := test_assign_nested{nested_level2}
    top_level := test_assign{nested_level1}

    assign := top_level
    fmt.Println("top_level address")
    fmt.Printf("%p", &top_level)
    fmt.Println(" ")
    fmt.Println("1 level address")
    fmt.Printf("%p", &top_level.Level1)
    fmt.Println(" ")
    fmt.Println("2 level address")
    fmt.Printf("%p", &top_level.Level1.Level_2)
    fmt.Println("------------------------")

    fmt.Println("assign top_level address")
    fmt.Printf("%p", &assign)
    fmt.Println(" ")
    fmt.Println("1 level address")
    fmt.Printf("%p", &assign.Level1)
    fmt.Println(" ")
    fmt.Println("2 level address")
    fmt.Printf("%p", &assign.Level1.Level_2)

以上的输出是

top_level address
0x10410020 
1 level address
0x10410020 
2 level address
0x10410020 
assign top_level address
0x10410024 
1 level address
0x10410024 
2 level address
0x10410024

我希望输出类似于

    fmt.Println("top_level address")
    fmt.Printf("%p", &top_level)
    fmt.Println(" ")
    fmt.Println("1 level address")
    fmt.Printf("%p", &nested_level1)
    fmt.Println(" ")
    fmt.Println("2 level address")
    fmt.Printf("%p", &nested_level2)
    fmt.Println(" ")
    fmt.Println(" ------------------------------- ")

其中

top_level address
0x421152280 
1 level address
0x421152270 
2 level address
0x421152260 

每个结构都有不同的地址。但似乎子结构与父结构具有相同的地址。

为什么结构中的所有嵌套元素都具有相同的地址?

:=实际上是否会递归复制新的struct?像印刷声明所示? (即。:=将返回一个全新的struct副本,其每个字段内容也是递归的全新副本)

2 个答案:

答案 0 :(得分:3)

Go中的类型不是围绕其他事物的autonomes包装器,而只是两件事:类型允许附加方法和类型在内存中提供布局。对于这里的问题,将方法附加到类型的能力是无关紧要的。让我们来看看这个问题的中性表述:

type A int64
type B { a A }
type C { b B }

这声明了三种具有以下内存布局的类型:

  • 类型A的内存布局为int64(即8个字节)。
  • 类型B具有单个A的内存布局,即int64,因此为8个字节。
  • 类型C具有单个B的内存布局,即单个A,即int64,因此为8个字节。

关于内存布局的类型B是 a"包装"在A附近,至少包装器绝对没有增加任何东西。从纯粹的内存布局角度来看,定义类型B将是无用的(但它允许将不同的方法附加到B而不是A)。

现在应该清楚c C的地址是其8个字节中的第一个的地址,这与c.b的地址相同,该地址与c.b.a相同。对C的任何赋值只是一个机器字的副本(在64位体系结构上)。

如果你定义type D { a A; b B }它会变得更有趣,因为D现在是16个字节长。 (向D添加更多内容甚至可能会因填充而留下漏洞。)但D仍然没有为内存中彼此相邻的A和B提供任何内容(期望新的方法集)。

答案 1 :(得分:1)

我的猜测是你正在寻找这样的东西:

package main

import "fmt"

// Nesting: type T2 struct{ F3 int }
type T2 struct{ F3 int }

// Nesting: type T1 struct{ { F2 struct{ F3 int } }
type T1 struct{ F2 T2 }

// Nesting: type T0 struct{ F1 struct{ F2 struct{ F3 int } } }
type T0 struct{ F1 T1 }

func main() {
    t2 := T2{F3: 42}
    fmt.Printf(
        "%p %p %d\n",
        // 0xc4200120d0 0xc4200120d0 42
        &t2, &t2.F3, t2.F3,
    )

    t1 := T1{F2: t2}
    fmt.Printf(
        "%p %p %p %d\n",
        // 0xc4200120f0 0xc4200120f0 0xc4200120f0 42
        &t1, &t1.F2, &t1.F2.F3, t1.F2.F3,
    )

    t0 := T0{F1: t1}
    fmt.Printf(
        "%p %p %p %p %d\n",
        // 0xc420012100 0xc420012100 0xc420012100 0xc420012100 42
        &t0, &t0.F1, &t0.F1.F2, &t0.F1.F2.F3, t0.F1.F2.F3,
    )
}

输出:

0xc4200120d0 0xc4200120d0 42
0xc4200120f0 0xc4200120f0 0xc4200120f0 42
0xc420012100 0xc420012100 0xc420012100 0xc420012100 42

T0

type T0 struct{ F1 T1 }

type T1 struct{ F2 T2 }

type T2 struct{ F3 int }

相当于

type T0 struct {
    F1 struct {
        F2 struct {
            F3 int
        }
    }
}

T0F1F2F3具有相同的地址。

在新示例中,T0

type T2 struct{ F3A, F3B int }

type T1 struct{ F2 T2 }

type T0 struct{ F1 T1 }

相当于

type T0 struct {
    F1 struct {
        F2 struct {
            F3A int
            F3B int
        }
    }
}

T0F1F2F3A具有相同的地址。 F3AF3B有不同的地址。