簡體   English   中英

Golang 中的指針地址

[英]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位於內存中某個框中的某個位置,位於沒有名稱的位置( di位於確實具有我們可以用來談論它們的名稱的位置)。 在那個盒子里面是一個指針值。 指針值指向包含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.

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