這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
原文連結:Understand Go pointers in less than 800 words or your money back
這是一篇面向即將學習 go 語言並且對指標理念或者 go 的指標類型不是很瞭解的程式員的內容
什麼是指標?
簡單解釋,指標就是指向另一段地址的值,課本上是這麼解釋的,但是如果你是一個來自並不討論變數地址開發語言的開發人員,使用指標進行編程將感覺非常的美妙。
我們先換一個話題
什麼是記憶體?
電腦的記憶體 RAM 可以把它想象成一些有序的盒子,一個接一個的排成一排,每一個盒子或者儲存格都被一個唯一的數字標記依次遞增,這個數字就是該儲存格的地址,也就是記憶體的地址。
image.png
每一個儲存格儲存單一的值,如果你知道一個儲存格的地址,那麼你就可以通過地址讀取儲存格中的內容。你也可以向這個儲存格中放置一個值來替代之前的任何值。
這就是關於記憶體的所有內容。CPU 所做的一切操作也都是在讀取或者儲存儲存格中值。
什麼是變數?
編寫一段程式,檢索出值並儲存在地址為 200 的一個塊記憶體中,將其乘以 3,並將結果儲存在地址為 201 的另一塊記憶體中,我們就可以像這樣寫一段虛擬碼:
- 檢索出記憶體位址為 200 的值,並將其儲存在 CPU 中
- 將儲存在 CPU 中的值乘以 3
將 CPU 中儲存的結果,寫入地址為 201 的記憶體塊中
image.png
這是相當早期的編程形式,程式員需要維護一列記憶體位址,需要清楚的知道:誰、什麼時候在使用它以及記憶體中儲存的值是什麼。
很顯然這是非常枯燥又很容易出錯的方式,並且還意味著你要為程式構建過程中記憶體中的每一個可能出現的值分配地址。更糟糕的是這種設計使得你動態為變數分配儲存變得很艱難,你可以想象一下如果編寫一個大型的程式而只能使用全域變數是什麼感受?
為瞭解決這個問題,變數的概念被創造了出來,一個變數僅僅是一個代表記憶體位址的字母數字組合、也可以一個標籤或者一個暱稱 。
現在,我們與其探討記憶體位址,不如說一下變數,變數是我們為記憶體位址賦予的一個更容易理解的名稱,之前的那個程式現在可以這樣的解釋一下:
image.png
- 擷取變數 a 中儲存的值,並將其儲存在 CPU 中
- 將其乘以 3
- 將結果儲存在變數 b 中
這是一個相同的程式,唯一的一個重要改進是我們不在直接關注記憶體位址了,我們也不在需要持續追蹤記憶體位址,而是把這個苦差事交給了編譯器。
現在我們可以像這樣寫程式了:
var a = 6 var b = a * 3
編譯器將確保 a 與 b 的變數被分配唯一的地址,同時保證其值可以在被調用結束之前不被釋放。
到底什麼是指標呢?
截止目前我們知道了記憶體就是一系列有序號的儲存單元,變數就是編譯器為記憶體位址分配的暱稱,那麼指標是什麼呢?
指標就是一個指向另一個記憶體位址變數的值
指標指向變數的記憶體位址,指標就像該變數值的記憶體位址一樣
我們來看一個程式碼片段
func main() { a := 200 b := &a *b++ fmt.Println(a)}
在 main 函數的第一行,我們定義了一個新的變數 a ,並賦值為 200。接下來我們定義了一個變數 b ,並將變數 a 的地址賦值給 b 。我們並不知道 a 的準確儲存地址,但是我們依然可以將 a 的地址儲存在變數 b 中。
image.png
image.png
因為 Go 強型別的特性,第三行代碼也許是最具幹擾性的了,b 包含 a 變數的地址,但是我們想增加儲存在 a 變數中的值。這樣我們必須取值 (Dereference) b ,而是跟隨指標由 b 引用 a。
然後我們將該值加 1 後,儲存回 b 中儲存的記憶體位址上。
最後一行列印了 a 的值,可以看到 a 的值已經增加為了 201
image.png
image.png
結論
假如你是一個來自沒有指標概念或者變數中隱藏了指標的開發語言的開發人員,你需要在形成一個指標與變數關聯關係的模型,總之記住這個規則:
指標是一個指向另一個變數記憶體位址的值