800字徹底理解Go指針
這篇文章是為不熟悉 Go 的指針或指針類型的程序員而準備的。
什么是指針?
簡單點說,指針是指向另一個地址的值。這是教科書上的解釋,但如果你轉(zhuǎn)自一門不用談?wù)撟兞康刂返拈_發(fā)語言時,這個解釋看上去猶如一串楔形文字,難以理解。
讓我們分解一下。
什么是內(nèi)存?
計算機內(nèi)存,即 RAM,可以被看作是一串盒子,一個接一個地排成一行。
每個盒子(或者稱為單元格)都標有一個惟一的數(shù)字,數(shù)字按順序遞增;這是單元格的地址,其所在的內(nèi)存位置。

每一個單元格存儲一個值。如果你知道某個單元格的內(nèi)存地址,就可以訪問該單元格并讀取里面的內(nèi)容?;蛘哂昧硗庖粋€值替換該單元格內(nèi)之前的值。
這都是關(guān)于內(nèi)存的知識,CPU 所做的一切都是為獲取和存儲值到內(nèi)存單元中。
什么是變量?
編寫一段代碼讀取儲存在內(nèi)存地址為 200 的值,將其乘以 3 并將結(jié)果存儲在內(nèi)存地址為 201 的位置,偽代碼流程如下:
- 讀取存儲在內(nèi)存地址為 200 的值,并將其暫存在 CPU 中;
- 將存儲在 CPU 中的值乘以 3;
- 將存儲在 CPU 中的值存入內(nèi)存地址為 201 的位置;

這正是早期程序的編寫方式。程序員將保留一個內(nèi)存位置列表,包括誰使用它、何時使用以及存儲在其中的值表示什么。
很明顯,這很繁瑣而且容易出錯,這也意味著在編寫程序期間,必須給存儲在內(nèi)存中的每一個可能的值分配一個地址。更糟糕的是,這種方式使得在程序運行時動態(tài)地將內(nèi)存分配給變量變得異常困難 -- 試想一下,如果你不得不使用全局變量來編寫大型程序。
為了解決這個問題,創(chuàng)造了變量的概念。變量只是一個由數(shù)字字母組成的、標識存儲位置的假名。
現(xiàn)在,我們不再討論存儲位置,而是討論變量,這是我們?yōu)閮?nèi)存位置提供的方便記憶的名稱。之前的程序現(xiàn)在可以表示為:
- 讀取變量 a 中存儲的值并將其放入 CPU 中;
- 將其乘以 3;
- 將結(jié)果存入變量 b;

這是同一個程序,但有一個重要的改進 — 我們不再需要直接討論內(nèi)存位置,也不再需要跟蹤它們 — 把這些繁重的工作交給編譯器處理。
現(xiàn)在,我們可以像下面這樣寫程序:
- var a = 62
- var b = a * 3
編譯器將確保為變量 a 和 b 分配唯一的內(nèi)存位置,以便根據(jù)需要保存它們的值。
什么是指針?
現(xiàn)在我們已經(jīng)知道,內(nèi)存是一系列編號的單元格,而變量僅僅是標識內(nèi)存位置的昵稱,那指針是什么呢?
指針是指向另一個變量的內(nèi)存位置的值。
指針指向變量的內(nèi)存地址,就像變量標識值的內(nèi)存地址一樣。
一起來看下這段代碼:
- 1func main() {
- 2 a := 200
- 3 b := &a
- 4 *b++
- 5 fmt.Println(a)
- 6}
第二行代碼聲明了變量 a 且賦值 200。

接著,聲明了變量 b 并將變量 a 的地址賦值給它。記住,我們不知道變量 a 存儲的確切地址,但是我們?nèi)匀豢梢詫?a 的地址存儲在 b 中。

第四行代碼是最難理解的。變量 b 存儲的是變量 a 的地址,但我們又想將 a 的值加一。為了達到這個目的,必須使用解引用,通過 b 獲得 a 的值。

然后將值加一,并將結(jié)果存儲在 b 指向的內(nèi)存位置上,即變量 a 所在的內(nèi)存位置。

最后一行代碼打印的就是 a 的值,也是加一之后的值 201。
總結(jié)
如果你之前使用的語言沒有指針的概念或者每個變量都隱含指針,不要驚慌,理解變量與指針之間的關(guān)系需要時間與實踐,請記住這條規(guī)則:
指針是指向另一個變量的內(nèi)存位置的值。