![](/img/trans.png)
[英]Why are addresses of pointers to a WaitGroup different in this golang snippet?
[英]Addresses of pointers in Golang
我的問題很簡單,但我相信它隱藏了 Go 變量初始化的重要特征。
如果我們有兩個變量
i := 5
d := &i
我們想打印它們
fmt.Printf("The address of i value is %d\n", &i)
fmt.Printf("The value of d is %d\n", d)
fmt.Printf("The address of d is %d", &d)
輸出將類似於
The address of i value is 824633835664
The value of d is 824633835664
The address of d is 824633778224
所以看起來&i
返回其值5
的地址。 明白了。 不明白的是&d
返回給我們什么? 變量i
的地址?
那么是不是這樣實現的,值有自己的地址,變量(值內存地址的別名)在內存中也有自己的地址? 否則&d
將返回給我們5
的地址
Go在這里很傳統。 您可以將系統的整體內存想象成一個容納較小盒子的大盒子。 每個較小的盒子也可以裝一些東西。
聲明一個變量:
var i int
告訴 Go 系統它應該在某處分配一個小盒子——你無法控制在哪里,因為這取決於 Go 系統——它足夠大以容納一些int
值。 由於我們還沒有將任何值放入框中,Go 用零預填充它:
i:
+-----+
| 0 |
+-----+
如果我們將i
設置為5
,則將 5 放入框中。 使用i := 5
是告訴 Go 編譯器的一種速記方式:在某處創建變量i
並將 5全部放在一行中,這樣我們得到:
i:
+-----+
| 5 |
+-----+
所以你現在有了這個設置,因為你結合了聲明和賦值,編譯器很容易避免任何浪費:它不必先將i
設置為零,然后將i
設置為 5。(編譯器可以當然,即使您沒有使用簡短聲明形式,也可以避免浪費精力,但是我們人類不必考慮這一點更好:簡短聲明形式讓我們可以將其視為create i
已經設置為 5 。)
我們還有:
var d *int
它告訴 Go 系統分配一個足夠大的盒子來容納*int
值。 這可能與int
值的大小相同,或者更大或更小。 我將把它畫成兩倍大,這在您的系統上可能是也可能不是:
d:
+-----------+
| nil |
+-----------+
如果我們還沒有設置d
,Go 會用適當的零值填充它,即nil
。
如果我們現在將d
設置為指向i
,這會更改d
使其指向i
:
d: i:
+-----------+ +-----+
| *-----------> | 5 |
+-----------+ +-----+
在這里,您使用d := &i
來聲明d
,然后也將d
設置為&i
,這樣我們現在就有了這種情況(沒有浪費任何精力先將d
設置為 nil,然后也將nil
替換為&i
的值) .
您對fmt.Printf
前兩次調用fmt.Printf
傳遞值&i
,然后傳遞存儲在d
的值,這也是&i
,因此這兩個值打印出來的相同。 (注意%d
通常不是打印指針值的好方法,盡管fmt
的 Go 包規范說它可以工作。通常指針最好以十六進制打印,對於現代計算機; %p
格式這樣做。)
您的最后一次調用傳遞了值&d
。 我們還沒有為此繪制一個內存盒,但是為了傳遞值,Go 必須將它塞進某個盒子中的某個地方。 1我們現在可以畫出來:
unnamed:
+-----------+
| * |
+-----|-----+
|
d: v i:
+-----------+ +-----+
| *-----------> | 5 |
+-----------+ +-----+
因此,值&d
位於內存中某個框中的某個位置,位於沒有名稱的位置( d
和i
位於確實具有我們可以用來談論它們的名稱的位置)。 在那個盒子里面是一個指針值。 指針值指向包含d
的框。
對fmt.Printf
的最后一次調用將這個指針值——這個&d
值,存儲在未命名的框中——發送到fmt.Printf
以便fmt.Printf
可以打印它。 這個值當然必須與存儲在d
盒中的值不同,因為d
指向i
。 所以最終的打印值是不同的。
該程序總體上有一組可能的正確答案。 我們不知道要打印的三個值是什么,但我們知道的是,第一和第二個值將是相同的,第三打印的值不匹配的第2位。 如果沒有發生這種情況,則您使用的系統沒有正確實現 Go 語言。 2
1如果有一種不涉及使用內存盒的快速傳遞值的方法,則允許 Go 這樣做,只要您無法在正常語言規則內分辨出 Go 正在這樣做。 但同樣適用於普通變量!
Go 編譯器和運行時系統只需要產生正確的答案,不管它應用多少編譯時魔法來產生正確的答案。 此外,“正確答案”僅針對可能程序的某些子集定義。
2這可能有點誇大其詞。 我不確定 Go 是否禁止在內存中移動對象的壓縮垃圾收集器,更新所有指針。 實際現有的 Go 系統沒有壓縮收集器,但可以通過使用這樣的代碼來判斷這一點,這至少有點令人懷疑。 如果 Go確實允許壓縮收集器,則指針的打印值可能會更改,因此打印的前兩個值可能不同。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.