mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-08-08 00:03:26 +00:00
@@ -1,6 +1,6 @@
|
||||
## 2.1. 命名
|
||||
|
||||
Go語言中的函數名、變量名、常量名、類型名、語句標號和包名等所有的命名,都遵循一個簡單的命名規則:一個名字必鬚以一個字母(Unicode字母)或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線。大寫字母和小寫字母是不同的:heapSort和Heapsort是兩個不同的名字。
|
||||
Go語言中的函數名、變量名、常量名、類型名、語句標號和包名等所有的命名,都遵循一個簡單的命名規則:一個名字必須以一個字母(Unicode字母)或下劃線開頭,後面可以跟任意數量的字母、數字或下劃線。大寫字母和小寫字母是不同的:heapSort和Heapsort是兩個不同的名字。
|
||||
|
||||
Go語言中類似if和switch的關鍵字有25個;關鍵字不能用於自定義名字,隻能在特定語法結構中使用。
|
||||
|
||||
@@ -29,7 +29,7 @@ continue for import return var
|
||||
|
||||
這些內部預先定義的名字併不是關鍵字,你可以再定義中重新使用它們。在一些特殊的場景中重新定義它們也是有意義的,但是也要註意避免過度而引起語義混亂。
|
||||
|
||||
如果一個名字是在函數內部定義,那麽它的就隻在函數內部有效。如果是在函數外部定義,那麽將在當前包的所有文件中都可以訪問。名字的開頭字母的大小寫決定了名字在包外的可見性。如果一個名字是大寫字母開頭的(譯註:必鬚是在函數外部定義的包級名字;包級函數名本身也是包級名字),那麽它將是導出的,也就是説可以被外部的包訪問,例如fmt包的Printf函數就是導出的,可以在fmt包外部訪問。包本身的名字一般總是用小寫字母。
|
||||
如果一個名字是在函數內部定義,那麽它的就隻在函數內部有效。如果是在函數外部定義,那麽將在當前包的所有文件中都可以訪問。名字的開頭字母的大小寫決定了名字在包外的可見性。如果一個名字是大寫字母開頭的(譯註:必須是在函數外部定義的包級名字;包級函數名本身也是包級名字),那麽它將是導出的,也就是説可以被外部的包訪問,例如fmt包的Printf函數就是導出的,可以在fmt包外部訪問。包本身的名字一般總是用小寫字母。
|
||||
|
||||
名字的長度沒有邏輯限製,但是Go語言的風格是盡量使用短小的名字,對於局部變量尤其是這樣;你會經常看到i之類的短名字,而不是冗長的theLoopIndex命名。通常來説,如果一個名字的作用域比較大,生命週期也比較長,那麽用長的名字將會更有意義。
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
聲明語句定義了程序的各種實體對象以及部分或全部的屬性。Go語言主要有四種類型的聲明語句:var、const、type和func,分别對應變量、常量、類型和函數實體對象的聲明。這一章我們重點討論變量和類型的聲明,第三章將討論常量的聲明,第五章將討論函數的聲明。
|
||||
|
||||
一個Go語言編寫的程序對應一個或多個以.go爲文件後綴名的源文件中。每個源文件以包的聲明語句開始,説明該源文件是屬於哪個包。包聲明語句之後是import語句導入依賴的其它包,然後是包一級的類型、變量、常量、函數的聲明語句,包一級的各種類型的聲明語句的順序無關緊要(譯註:函數內部的名字則必鬚先聲明之後才能使用)。例如,下面的例子中聲明了一個常量、一個函數和兩個變量:
|
||||
一個Go語言編寫的程序對應一個或多個以.go爲文件後綴名的源文件中。每個源文件以包的聲明語句開始,説明該源文件是屬於哪個包。包聲明語句之後是import語句導入依賴的其它包,然後是包一級的類型、變量、常量、函數的聲明語句,包一級的各種類型的聲明語句的順序無關緊要(譯註:函數內部的名字則必須先聲明之後才能使用)。例如,下面的例子中聲明了一個常量、一個函數和兩個變量:
|
||||
|
||||
```Go
|
||||
gopl.io/ch2/boiling
|
||||
|
@@ -53,7 +53,7 @@ in, err := os.Open(infile)
|
||||
out, err := os.Create(outfile)
|
||||
```
|
||||
|
||||
簡短變量聲明語句中必鬚至少要聲明一個新的變量,下面的代碼將不能編譯通過:
|
||||
簡短變量聲明語句中必須至少要聲明一個新的變量,下面的代碼將不能編譯通過:
|
||||
|
||||
```Go
|
||||
f, err := os.Open(infile)
|
||||
|
@@ -16,7 +16,7 @@ fmt.Println(x) // "2"
|
||||
|
||||
對於聚合類型每個成員——比如結構體的每個字段、或者是數組的每個元素——也都是對應一個變量,因此可以被取地址。
|
||||
|
||||
變量有時候被稱爲可尋址的值。卽使變量由表達式臨時生成,那麽表達式也必鬚能接受`&`取地址操作。
|
||||
變量有時候被稱爲可尋址的值。卽使變量由表達式臨時生成,那麽表達式也必須能接受`&`取地址操作。
|
||||
|
||||
任何類型的指針的零值都是nil。如果`p != nil`測試爲眞,那麽p是指向某個有效變量。指針之間也是可以進行相等測試的,隻有當它們指向同一個變量或全部是nil時才相等。
|
||||
|
||||
@@ -55,7 +55,7 @@ incr(&v) // side effect: v is now 2
|
||||
fmt.Println(incr(&v)) // "3" (and v is 3)
|
||||
```
|
||||
|
||||
每次我們對一個變量取地址,或者複製指針,我們都是爲原變量創建了新的别名。例如,`*p`就是是 變量v的别名。指針特别有價值的地方在於我們可以不用名字而訪問一個變量,但是這是一把雙刃劍:要找到一個變量的所有訪問者併不容易,我們必鬚知道變量全部的别名(譯註:這是Go語言的垃圾迴收器所做的工作)。不僅僅是指針會創建别名,很多其他引用類型也會創建别名,例如slice、map和chan,甚至結構體、數組和接口都會創建所引用變量的别名。
|
||||
每次我們對一個變量取地址,或者複製指針,我們都是爲原變量創建了新的别名。例如,`*p`就是是 變量v的别名。指針特别有價值的地方在於我們可以不用名字而訪問一個變量,但是這是一把雙刃劍:要找到一個變量的所有訪問者併不容易,我們必須知道變量全部的别名(譯註:這是Go語言的垃圾迴收器所做的工作)。不僅僅是指針會創建别名,很多其他引用類型也會創建别名,例如slice、map和chan,甚至結構體、數組和接口都會創建所引用變量的别名。
|
||||
|
||||
指針是實現標準庫中flag包的關鍵技術,它使用命令行參數來設置對應變量的值,而這些對應命令行標誌參數的變量可能會零散分布在整個程序中。爲了説明這一點,在早些的echo版本中,就包含了兩個可選的命令行參數:`-n`用於忽略行尾的換行符,`-s sep`用於指定分隔字符(默認是空格)。下面這是第四個版本,對應包路徑爲gopl.io/ch2/echo4。
|
||||
|
||||
@@ -82,9 +82,9 @@ 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()函數來訪問,返迴值對應對應一個字符串類型的slice。如果在flag.Parse函數解析命令行參數時遇到錯誤,默認將打印相關的提示信息,然後調用os.Exit(2)終止程序。
|
||||
|
||||
讓我們運行一些echo測試用例:
|
||||
|
||||
|
@@ -9,7 +9,7 @@ fmt.Println(*p) // "0"
|
||||
fmt.Println(*p) // "2"
|
||||
```
|
||||
|
||||
用new創建變量和普通變量聲明語句方式創建變量沒有什麽區别,除了不需要聲明一個臨時變量的名字外,我們還可以在表達式中使用new(T)。換言之,new函數類似是一種語法醣,而不是一個新的基礎概念。
|
||||
用new創建變量和普通變量聲明語句方式創建變量沒有什麽區别,除了不需要聲明一個臨時變量的名字外,我們還可以在表達式中使用new(T)。換言之,new函數類似是一種語法糖,而不是一個新的基礎概念。
|
||||
|
||||
下面的兩個newInt函數有着相同的行爲:
|
||||
|
||||
|
@@ -44,7 +44,7 @@ func f() { func g() {
|
||||
}
|
||||
```
|
||||
|
||||
這里的x變量必鬚在堆上分配,因爲它在函數退出後依然可以通過包一級的global變量找到,雖然它是在函數內部定義的;用Go語言的術語説,這個x局部變量從函數f中逃逸了。相反,當g函數返迴時,變量`*y`將是不可達的,也就是説可以馬上被迴收的。因此,`*y`併沒有從函數g中逃逸,編譯器可以選擇在棧上分配`*y`的存儲空間(譯註:也可以選擇在堆上分配,然後由Go語言的GC迴收這個變量的內存空間),雖然這里用的是new方式。其實在任何時候,你併不需爲了編寫正確的代碼而要考慮變量的逃逸行爲,要記住的是,逃逸的變量需要額外分配內存,同時對性能的優化可能會産生細微的影響。
|
||||
這里的x變量必須在堆上分配,因爲它在函數退出後依然可以通過包一級的global變量找到,雖然它是在函數內部定義的;用Go語言的術語説,這個x局部變量從函數f中逃逸了。相反,當g函數返迴時,變量`*y`將是不可達的,也就是説可以馬上被迴收的。因此,`*y`併沒有從函數g中逃逸,編譯器可以選擇在棧上分配`*y`的存儲空間(譯註:也可以選擇在堆上分配,然後由Go語言的GC迴收這個變量的內存空間),雖然這里用的是new方式。其實在任何時候,你併不需爲了編寫正確的代碼而要考慮變量的逃逸行爲,要記住的是,逃逸的變量需要額外分配內存,同時對性能的優化可能會産生細微的影響。
|
||||
|
||||
Go語言的自動垃圾收集器對編寫正確的代碼是一個鉅大的幫助,但也併不是説你完全不用考慮內存了。你雖然不需要顯式地分配和釋放內存,但是要編寫高效的程序你依然需要了解變量的生命週期。例如,如果將指向短生命週期對象的指針保存到具有長生命週期的對象中,特别是保存到全局變量時,會阻止對短生命週期對象的垃圾迴收(從而可能影響程序的性能)。
|
||||
|
||||
|
@@ -39,7 +39,7 @@ i, j, k = 2, 3, 5
|
||||
|
||||
但如果表達式太複雜的話,應該盡量避免過度使用元組賦值;因爲每個變量單獨賦值語句的寫法可讀性會更好。
|
||||
|
||||
有些表達式會産生多個值,比如調用一個有多個返迴值的函數。當這樣一個函數調用出現在元組賦值右邊的表達式中時(譯註:右邊不能再有其它表達式),左邊變量的數目必鬚和右邊一致。
|
||||
有些表達式會産生多個值,比如調用一個有多個返迴值的函數。當這樣一個函數調用出現在元組賦值右邊的表達式中時(譯註:右邊不能再有其它表達式),左邊變量的數目必須和右邊一致。
|
||||
|
||||
```Go
|
||||
f, err = os.Open("foo.txt") // function call returns two values
|
||||
|
@@ -16,10 +16,10 @@ medals[2] = "bronze"
|
||||
|
||||
map和chan的元素,雖然不是普通的變量,但是也有類似的隱式賦值行爲。
|
||||
|
||||
不管是隱式還是顯式地賦值,在賦值語句左邊的變量和右邊最終的求到的值必鬚有相同的數據類型。更直白地説,隻有右邊的值對於左邊的變量是可賦值的,賦值語句才是允許的。
|
||||
不管是隱式還是顯式地賦值,在賦值語句左邊的變量和右邊最終的求到的值必須有相同的數據類型。更直白地説,隻有右邊的值對於左邊的變量是可賦值的,賦值語句才是允許的。
|
||||
|
||||
可賦值性的規則對於不同類型有着不同要求,對每個新類型特殊的地方我們會專門解釋。對於目前我們已經討論過的類型,它的規則是簡單的:類型必鬚完全匹配,nil可以賦值給任何指針或引用類型的變量。常量(§3.6)則有更靈活的賦值規則,因爲這樣可以避免不必要的顯式的類型轉換。
|
||||
可賦值性的規則對於不同類型有着不同要求,對每個新類型特殊的地方我們會專門解釋。對於目前我們已經討論過的類型,它的規則是簡單的:類型必須完全匹配,nil可以賦值給任何指針或引用類型的變量。常量(§3.6)則有更靈活的賦值規則,因爲這樣可以避免不必要的顯式的類型轉換。
|
||||
|
||||
對於兩個值是否可以用`==`或`!=`進行相等比較的能力也和可賦值能力有關繫:對於任何類型的值的相等比較,第二個值必鬚是對第一個值類型對應的變量是可賦值的,反之依然。和前面一樣,我們會對每個新類型比較特殊的地方做專門的解釋。
|
||||
對於兩個值是否可以用`==`或`!=`進行相等比較的能力也和可賦值能力有關繫:對於任何類型的值的相等比較,第二個值必須是對第一個值類型對應的變量是可賦值的,反之依然。和前面一樣,我們會對每個新類型比較特殊的地方做專門的解釋。
|
||||
|
||||
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
Go語言中的包和其他語言的庫或模塊的概念類似,目的都是爲了支持模塊化、封裝、單獨編譯和代碼重用。一個包的源代碼保存在一個或多個以.go爲文件後綴名的源文件中,通常一個包所在目録路徑的後綴是包的導入路徑;例如包gopl.io/ch1/helloworld對應的目録路徑是$GOPATH/src/gopl.io/ch1/helloworld。
|
||||
|
||||
每個包都對應一個獨立的名字空間。例如,在image包中的Decode函數和在unicode/utf16包中的 Decode函數是不同的。要在外部引用該函數,必鬚顯式使用image.Decode或utf16.Decode形式訪問。
|
||||
每個包都對應一個獨立的名字空間。例如,在image包中的Decode函數和在unicode/utf16包中的 Decode函數是不同的。要在外部引用該函數,必須顯式使用image.Decode或utf16.Decode形式訪問。
|
||||
|
||||
包還可以讓我們通過控製哪些名字是外部可見的來隱藏內部實現信息。在Go語言中,一個簡單的規則是:如果一個名字是大寫字母開頭的,那麽該名字是導出的(譯註:因爲漢字不區分大小寫,因此漢字開頭的名字是沒有導出的)。
|
||||
|
||||
|
Reference in New Issue
Block a user