簡體   English   中英

空結構片的地址

[英]Addresses of slices of empty structs

我有一個關於empty struct的基本問題,試圖在嘗試獲取兩個切片的后備數組元素的地址時得到以下不同的輸出:

a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])

上面的代碼片段返回

&a == &b false
&a[0] == &b[0] true

但是,請考慮以下略有變化的代碼段:

a := make([]struct{}, 10)
b := make([]struct{}, 20)
fmt.Println(a[0], &a[0])
fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])

上面的代碼片段返回

{} &{}
&a == &b false
&a[0] == &b[0] false

有人可以解釋上述差異的原因嗎? 謝謝!

[跟進]進行以下修改:

package main

import "fmt"

type S struct{}

func (s *S) addr() { fmt.Printf("%p\n", s) }

func main() {
    a := make([]S, 10)
    b := make([]S, 20)
    fmt.Println(a[0], &a[0])
    fmt.Println("&a == &b", &a == &b)
    fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])
    //a[0].addr()
    //b[0].addr()
}

仍返回相同的輸出:

{} &{}
&a == &b false
&a[0] == &b[0] false

盡管取消注釋方法調用,但返回

{} &{}
&a == &b false
&a[0] == &b[0] true
0x19583c // ==> [depends upon env]
0x19583c // ==> [depends upon env]

一個空結構基本上是:幾乎沒有。 大小為0:

var s struct{}
fmt.Println(unsafe.Sizeof(s))

返回0

現在,為什么兩個空結構的地址有時相同而有時卻不同,這取決於內部結構。 我們唯一可以得到的線索是從規范

如果結構或數組類型不包含大小大於零的字段(或元素),則其大小為零。 兩個不同的零大小變量在內存中可能具有相同的地址。

另請注意以下代碼,將第一個打印移到最后一個:

fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])
fmt.Println(a[0], &a[0])

輸出:

&a == &b false
&a[0] == &b[0] false
{} &{}

或與您的第二種情況相同。 基本上,這表明編譯器選擇使用其他地址。 給定“可能在內存中具有相同的地址”,您不應依賴於相等性,因為確切的行為取決於go內部結構,並且隨時可能更改。

為了進一步閱讀,我推薦這篇關於空結構的優秀文章

在深入研究之前,請了解根據規范,程序對於大小為零的值產生的地址是否相等或不同,都是正確的,因為規范僅聲明它們可以相同,但不要求它們是相同的。相同。

規格:尺寸和對齊方式保證:

如果結構或數組類型不包含大小大於零的字段(或元素),則其大小為零。 兩個不同的零大小變量在內存中可能具有相同的地址。

因此,您所體驗的是實現細節。 有關決策的更多細節和因素,以下解釋僅對您的具體示例有效並足夠:

在您的第一個示例中,切片的后備數組的地址僅在main()函數內使用,它們不會逸出到堆中。 您打印的只是地址比較的結果。 這些只是bool值,不包括地址值。 因此,編譯器選擇對ab的后備數組使用相同的地址。

在您的第二個示例中,后備數組的地址(更具體地說是后備數組的某些元素的地址)在main()函數外部使用,它們被傳遞到fmt.Println()函數中並在內部使用,因為您也打印這些地址。

我們可以通過將-gcflags '-m'參數傳遞給Go工具來“證明”這一點,要求它打印轉義分析的結果。

在第一個示例中,將代碼保存在play.go ,運行go run -gcflags '-m' play.go命令,輸出為:

./play.go:10:14: "&a == &b" escapes to heap
./play.go:10:29: &a == &b escapes to heap
./play.go:11:14: "&a[0] == &b[0]" escapes to heap
./play.go:11:38: &a[0] == &b[0] escapes to heap
./play.go:8:11: main make([]struct {}, 10) does not escape
./play.go:9:11: main make([]struct {}, 20) does not escape
./play.go:10:26: main &a does not escape
./play.go:10:32: main &b does not escape
./play.go:10:13: main ... argument does not escape
./play.go:11:32: main &a[0] does not escape
./play.go:11:41: main &b[0] does not escape
./play.go:11:13: main ... argument does not escape
&a == &b false
&a[0] == &b[0] true

我們可以看到,地址不會逃脫。

使用第二個示例運行go run -gcflags '-m' play.go ,輸出為:

./play.go:10:15: a[0] escapes to heap
./play.go:10:20: &a[0] escapes to heap
./play.go:10:20: &a[0] escapes to heap
./play.go:8:11: make([]struct {}, 10) escapes to heap
./play.go:11:14: "&a == &b" escapes to heap
./play.go:11:29: &a == &b escapes to heap
./play.go:12:14: "&a[0] == &b[0]" escapes to heap
./play.go:12:38: &a[0] == &b[0] escapes to heap
./play.go:9:11: main make([]struct {}, 20) does not escape
./play.go:10:13: main ... argument does not escape
./play.go:11:26: main &a does not escape
./play.go:11:32: main &b does not escape
./play.go:11:13: main ... argument does not escape
./play.go:12:32: main &a[0] does not escape
./play.go:12:41: main &b[0] does not escape
./play.go:12:13: main ... argument does not escape
{} &{}
&a == &b false
&a[0] == &b[0] false

正如可以看到, a[0] &a[0]逃逸到堆,所以背襯陣列a動態分配,並且因此將具有比的不同的地址b的。

讓我們進一步“證明”這一點。 讓我們修改第二個示例,以使第三個變量c的地址也不會被打印出來,然后將bc進行比較:

a := make([]struct{}, 10)
b := make([]struct{}, 20)
c := make([]struct{}, 30)
fmt.Println(a[0], &a[0])
fmt.Println("&a == &b", &a == &b)
fmt.Println("&a[0] == &b[0]", &a[0] == &b[0])
fmt.Println("&b == &c", &b == &c)
fmt.Println("&b[0] == &c[0]", &b[0] == &c[0])

在此運行go run -gcflags '-m' play.go ,輸出為:

./play.go:11:15: a[0] escapes to heap
./play.go:11:20: &a[0] escapes to heap
./play.go:11:20: &a[0] escapes to heap
./play.go:8:11: make([]struct {}, 10) escapes to heap
./play.go:12:14: "&a == &b" escapes to heap
./play.go:12:29: &a == &b escapes to heap
./play.go:13:14: "&a[0] == &b[0]" escapes to heap
./play.go:13:38: &a[0] == &b[0] escapes to heap
./play.go:14:14: "&b == &c" escapes to heap
./play.go:14:29: &b == &c escapes to heap
./play.go:15:14: "&b[0] == &c[0]" escapes to heap
./play.go:15:38: &b[0] == &c[0] escapes to heap
./play.go:9:11: main make([]struct {}, 20) does not escape
./play.go:10:11: main make([]struct {}, 30) does not escape
./play.go:11:13: main ... argument does not escape
./play.go:12:26: main &a does not escape
./play.go:12:32: main &b does not escape
./play.go:12:13: main ... argument does not escape
./play.go:13:32: main &a[0] does not escape
./play.go:13:41: main &b[0] does not escape
./play.go:13:13: main ... argument does not escape
./play.go:14:26: main &b does not escape
./play.go:14:32: main &c does not escape
./play.go:14:13: main ... argument does not escape
./play.go:15:32: main &b[0] does not escape
./play.go:15:41: main &c[0] does not escape
./play.go:15:13: main ... argument does not escape
{} &{}
&a == &b false
&a[0] == &b[0] false
&b == &c false
&b[0] == &c[0] true

由於只有&a[0]被印刷,但不&b[0]也不&c[0]從而&a[0] == &b[0]將是false ,但&b[0] == &c[0]將是true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM