mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-10-23 15:01:48 +00:00
ch2-03 review
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
### 2.3.2 指針
|
||||
### 2.3.2. 指針
|
||||
|
||||
一個變量對應一個保存了一個值的內存空間. 變量在聲明語句創建時綁定一個名字, 比如 x, 但是還有很多變量始終以表達式方式引入, 例如 x[i] 或 x.f. 所有這些表達式都讀取一個變量的值, 除非它們是齣現在賦值語句的左邊, 這種時候是給變量賦予一個新值.
|
||||
一個變量對應一個保存了變量對應類型值的內存空間。普通變量在聲明語句創建時被綁定到一個變量名,比如叫x的變量,但是還有很多變量始終以表達式方式引入,例如x[i]或x.f變量。所有這些表達式一般都是讀取一個變量的值,除非它們是齣現在賦值語句的左邊,這種時候是給對應變量賦予一個新的值。
|
||||
|
||||
一個指針的值是一個變量的地址. 一個指針對應變量在內存中的存儲位置. 併不是每一個值都會有一個地址, 但是對於每一個變量必然有對應的地址. 通過指針, 我們可以直接讀或更新變量的值, 而不需要知道變量的名字(卽使變量有名字的話).
|
||||
一個指針的值是另一個變量的地址。一個指針對應變量在內存中的存儲位置。併不是每一個值都會有一個內存地址,但是對於每一個變量必然有對應的內存地址。通過指針,我們可以直接讀或更新對應變量的值,而不需要知道該變量的名字(如果變量有名字的話)。
|
||||
|
||||
如果這樣聲明一個變量 `var x int`, 那麽 `&x` 表達式(x的地址)將産生一個指向整數變量的指針, 對應的數據類型是 `*int`, 稱之爲 "指向 int 的指針". 如果指針名字爲 p, 那麽可以説 "p 指針指向 x", 或者説 "p 指針保存了 x 變量的地址". `*p` 對應 p 指針指向的變量的值. `*p` 表達式讀取變量的值, 爲 int 類型, 同時因爲 `*p` 對應一個變量, 所以可以齣現在賦值語句的左邊, 用於更新所指向的變量的值.
|
||||
如果用“var x int”聲明語句聲明一個x變量,那麽&x表達式(取x變量的內存地址)將産生一個指向該整數變量的指針,指針對應的數據類型是`*int`,指針被稱之爲“指向int類型的指針”。如果指針名字爲p,那麽可以説“p指針指向變量x”,或者説“p指針保存了x變量的內存地址”。同時`*p`表達式對應p指針指向的變量的值。一般`*p`表達式讀取指針指向的變量的值,這里爲int類型的值,同時因爲`*p`對應一個變量,所以該表達式也可以齣現在賦值語句的左邊,表示更新指針所指向的變量的值。
|
||||
|
||||
```Go
|
||||
x := 1
|
||||
@@ -14,18 +14,18 @@ fmt.Println(*p) // "1"
|
||||
fmt.Println(x) // "2"
|
||||
```
|
||||
|
||||
對於聚合類型, 比如結構體的每個字段, 或者是數組的每個元素, 也都是對應一個變量, 併且可以被穫取地址.
|
||||
對於聚合類型每個成員——比如結構體的每個字段、或者是數組的每個元素——也都是對應一個變量,因此可以被取地址。
|
||||
|
||||
變量有時候被稱爲可尋址的值. 如果變量由表達式臨時生成, 那麽表達式必鬚能接受 `&` 取地址操作.
|
||||
變量有時候被稱爲可尋址的值。卽使變量由表達式臨時生成,那麽表達式也必鬚能接受`&`取地址操作。
|
||||
|
||||
任何類型的指針的零值都是 nil. 如果 `p != nil` 測試爲眞, 那麽 p 是指向變量. 指針直接也是可以進行相等測試的, 隻有當它們指向同一個變量或全部是 nil 時纔相等.
|
||||
任何類型的指針的零值都是nil。如果`p != nil`測試爲眞,那麽p是指向某個有效變量。指針之間也是可以進行相等測試的,隻有當它們指向同一個變量或全部是nil時纔相等。
|
||||
|
||||
```Go
|
||||
var x, y int
|
||||
fmt.Println(&x == &x, &x == &y, &x == nil) // "true false false"
|
||||
```
|
||||
|
||||
在Go語言中, 返迴函數中局部變量的地址是安全的. 例如下面的代碼, 調用 f 函數時創建 v 局部變量, 在地址被返迴之後依然有效, 因爲指針 p 依然引用這個變量.
|
||||
在Go語言中,返迴函數中局部變量的地址也是安全的。例如下面的代碼,調用f函數時創建局部變量v,在局部變量地址被返迴之後依然有效,因爲指針p依然引用這個變量。
|
||||
|
||||
```Go
|
||||
var p = f()
|
||||
@@ -36,17 +36,17 @@ func f() *int {
|
||||
}
|
||||
```
|
||||
|
||||
每次調用 f 函數都將返迴不同的結果:
|
||||
每次調用f函數都將返迴不同的結果:
|
||||
|
||||
```Go
|
||||
fmt.Println(f() == f()) // "false"
|
||||
```
|
||||
|
||||
因爲指針包含了一個變量的地址, 因此將指針作爲參數調用函數, 將可以在函數中通過指針更新變量的值. 例如這個通過指針來更新變量的值, 然後返迴更新後的值, 可用在一個表達式中:
|
||||
因爲指針包含了一個變量的地址,因此如果將指針作爲參數調用函數,那將可以在函數中通過該指針來更新變量的值。例如下面這個例子就是通過指針來更新變量的值,然後返迴更新後的值,可用在一個表達式中(譯註:這是對C語言中`++v`操作的模擬,這里隻是爲了説明指針的用法,incr函數模擬的做法併不推薦):
|
||||
|
||||
```Go
|
||||
func incr(p *int) int {
|
||||
*p++ // increments what p points to; does not change p
|
||||
*p++ // 非常重要:隻是增加p指向的變量的值,併不改變p指針!!!
|
||||
return *p
|
||||
}
|
||||
|
||||
@@ -55,9 +55,9 @@ incr(&v) // side effect: v is now 2
|
||||
fmt.Println(incr(&v)) // "3" (and v is 3)
|
||||
```
|
||||
|
||||
每次我們對變量取地址, 或者複製指針, 我們都創建了變量的新的别名. 例如, *p 是 變量 v 的别名. 指針特别有加載的地方在於我們可以不用名字而訪問一個變量, 但是這是一把雙刃劍: 要找到一個變量的所有訪問者, 我們必鬚知道變量全部的别名. 不僅僅是指針創建别名, 很多其他引用類型也會創建别名, 例如 切片, 字典和管道, 甚至結構體, 數組和接口都會創建所引用變量的别名.
|
||||
每次我們對一個變量取地址,或者複製指針,我們都是爲原變量創建了新的别名。例如,`*p`就是是 變量v的别名。指針特别有價值的地方在於我們可以不用名字而訪問一個變量,但是這是一把雙刃劍:要找到一個變量的所有訪問者併不容易,我們必鬚知道變量全部的别名(譯註:這是Go語言的垃圾迴收器所做的工作)。不僅僅是指針會創建别名,很多其他引用類型也會創建别名,例如slice、map和chan,甚至結構體、數組和接口都會創建所引用變量的别名。
|
||||
|
||||
指針是 flag 包的關鍵, 它使用命令行參數來設置對應的變量, 而這些分布在整個程序中. 爲了説明這一點, 在早些的echo版本中, 包含了兩個可選的命令行參數: `-n` 用於忽略行尾的換行符, `-s sep` 用於指定分隔字符(默認是空格). 這是第四個版本, 對應包 gopl.io/ch2/echo4.
|
||||
指針是實現標準庫中flag包的關鍵技術,它使用命令行參數來設置對應變量的值,而這些對應命令行標誌參數的變量可能會零散分布在整個程序中。爲了説明這一點,在早些的echo版本中,就包含了兩個可選的命令行參數:`-n`用於忽略行尾的換行符,`-s sep`用於指定分隔字符(默認是空格)。下面這是第四個版本,對應包路徑爲gopl.io/ch2/echo4。
|
||||
|
||||
```Go
|
||||
gopl.io/ch2/echo4
|
||||
@@ -82,12 +82,11 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
`flag.Bool` 函數調用創建了一個新的布爾型標誌參數變量. 它有三個屬性: 第一個是的名字"n", 然後是標誌的默認值(這里是false), 最後是對應的描述信息. 如果用戶輸入了無效的標誌參數, 或者輸入 `-h` 或 `-help` 標誌參數, 將打印標誌參數的名字, 默認值和描述信息. 類似的, flag.String 用於創建一個字符串類型的標誌參數變量, 同樣包含參數名, 默認值, 和描述信息. 變量 `sep` 和 `n` 是一個指向標誌參數變量的指針, 因此必鬚用 *sep 和 *n 的方式間接引用.
|
||||
調用flag.Bool函數會創建一個新的對應布爾型標誌參數的變量。它有三個屬性:第一個是的命令行標誌參數的名字“n”,然後是該標誌參數的默認值(這里是false),最後是該標誌參數對應的描述信息。如果用戶在命令行輸入了一個無效的標誌參數,或者輸入`-h`或`-help`參數,那麽將打印所有標誌參數的名字、默認值和描述信息。類似的,調用flag.String函數將於創建一個對應字符串類型的標誌參數變量,同樣包含命令行標誌參數對應的參數名、默認值、和描述信息。程序中的`sep`和`n`變量分别是指向對應命令行標誌參數變量的指針,因此必鬚用`*sep`和`*n`形式的指針語法間接引用它們。
|
||||
|
||||
當程序運行時,必鬚在使用標誌參數對應的變量之前調用先flag.Parse函數,用於更新每個標誌參數對應變量的值(之前是默認值)。對於非標誌參數的普通命令行參數可以通過調用flag.Args()函數來訪問,返迴值對應對應一個字符串類型的slice。如果在flag.Parse函數解析命令行參數時遇到錯誤,默認將打印相關的提示信息,然後調用os.Exit(2)終止程序。
|
||||
|
||||
當程序運行時, 必鬚在標誌參數變量使用之前調用 flag.Parse 函數更新標誌參數變量的值(之前是默認值). 非標誌參數的普通類型參數可以用 flag.Args() 訪問, 對應一個 字符串切片. 如果 flag.Parse 解析遇到錯誤, 將打印提示信息, 然後調用 os.Exit(2) 終止程序.
|
||||
|
||||
讓我們運行一些 echo 測試用例:
|
||||
讓我們運行一些echo測試用例:
|
||||
|
||||
```
|
||||
$ go build gopl.io/ch2/echo4
|
||||
|
Reference in New Issue
Block a user