這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
1、變數
Go語言的變數聲明方式和c/c++語言不同。Go語言引入了var關鍵字。變數聲明如下:
var v1 int//聲明一個整型變數v1var v2 stringvar v3 [10]int//數組var v4 []int//數組切片var v5 struct { //結構體 f int}var v6 *int//指標var v7 map[string] int//map,ley為string,value為intvar v8 func(a int) int可以看到,變數的聲明沒有以分號 ; 作為結束符。var關鍵字也可一次定義多個變數:
var ( v1 int v2 string)
變數的初始化
var v1 int = 10 // 正確的使用方式1var v2 = 10 // 正確的使用方式2,編譯器可以自動推匯出 v2的類型v3 := 10 // 正確的使用方式3,編譯器可以自動推匯出 v3的類型
使用:= 可以減少代碼的書寫量,這對程式員來說是大大的好處啊但在我們使用的過程中,不應該出現如下:
var i inti := 2<span style="font-family: 'Microsoft YaHei';">//錯誤的做法</span>
變數賦值
var v10 int//先聲明,再賦值v10 = 123i, j = 2, 3//Go支援多重賦值
由多重賦值我們可以很方便的交換兩個數的值
i, j = j, i//編譯器會先計算等號右邊的值,再為左邊的賦值
匿名變數
Go語言支援多傳回值和匿名變數
func GetName() (firstName, lastName, nickName string) { return "May", "Chan", "Chibi Maruko"}返回了firstName, lastName, nickName,這三個變數均為string類型我們在接收函數返回的參數時,也時可以選著性的接收,使用 _ 來忽略參數
_, _, nickName := GetName()
常量在Go語言中,常量是指編譯期間就已知且不可改變的值。常量可以是數實值型別(包括整型、浮點型和複數類型)、布爾類型、字串類型等。
//以下為字面常量-123.14159265358979323846 // 浮點類型的常量3.2+12i // 複數類型的常量true // 布爾類型的常量"foo" // 字串常量
常量的定義
const Pi float64 = 3.14159265358979323846const zero = 0.0 // 無類型浮點常量const ( size int64 = 1024 eof = -1 // 無類型整型常量)const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重賦值const a, b, c = 3, 4, "foo"// a = 3, b = 4, c = "foo", 無類型整型和字串常量
如果常量在定義時沒有限定類型,那麼該常量 是一個 字面常量。常量定義的右值也可以是一個在編譯期運算的常量運算式,如:
const mask = 1 << 3
由於常量的賦值是一個編譯期行為,所以右值不能出現任何需要運行期才能得出結果的運算式,比如試圖以如下方式定義常量就會導致編譯錯誤:
const Home = os.GetEnv("HOME")
預定義常量Go語言預定義了這些常量: true、 false和iota。
iota比較特殊,可以被認為是一個可被編譯器修改的常量,在每一個const關鍵字出現時被重設為0,然後在下一個const出現之前,每出現一次iota,其所代表的數字會自動增1。
從以下的例子可以基本理解iota的用法:
const ( // iota被重設為 0c0 = iota // c0 == 0c1 = iota // c1 == 1c2 = iota // c2 == 2)const (a = 1 << iota // a == 1 (iota在每個const開頭被重設為 0)b = 1 << iota // b == 2c = 1 << iota // c == 4)const (u = iota * 42 // u == 0v float64 = iota * 42 // v == 42.0w = iota * 42 // w == 84)const x = iota // x == 0 ( 因為 iota又被重設為 0了 )const y = iota // y == 0 ( 同上)
如果兩個const的指派陳述式的運算式是一樣的,那麼可以省略後一個賦值運算式。因此,上面的前兩個const語句可簡寫為:
const ( // iota被重設為 0c0 = iota // c0 == 0c1 // c1 == 1c2 // c2 == 2)const (a = 1 <<iota // a == 1 (iota在每個const開頭被重設為 0)b // b == 2c // c == 4)
枚舉
枚舉指一系列相關的常量,比如下面關於一個星期中每天的定義。通過上一節的例子,我們看到可以用在const後跟一對圓括弧的方式定義一組常量,這種定義法在Go語言中通常用於定義枚舉值。 Go語言並不支援眾多其他語言明確支援的enum關鍵字。
const (Sunday = iotaMondayTuesdayWednesdayThursdayFridaySaturdaynumberOfDays // 這個常量沒有匯出)
同Go語言的其他符號( symbol)一樣,以大寫字母開頭的常量在包外可見。以上例子中numberOfDays為包內私人,其他符號則可被其他包訪問。
類型
Go語言內建以下這些基礎類型:
布爾類型: bool。
整型: int8、 byte、 int16、 int、 uint、 uintptr等。
浮點類型: float32 、 float64。
複數類型: complex64、 complex128。
字串: string。
字元類型: rune。
錯誤類型: error。
此外, Go語言也支援以下這些複合類型:
指標( pointer)
數組( array)
切片( slice)
字典( map)
通道( chan)
結構體( struct)
介面( interface)
在這些基礎類型之上Go還封裝了下面這幾種類型: int、 uint和uintptr等。這些類型的特點在於使用方便,但使用者不能對這些類型的長度做任何假設。對於常規的開發來說,用int和uint就可以了,沒必要用int8之類明確指定長度的類型,以免導致移植困難。
bool類型Go語言中的布爾類型與其他語言基本一致,關鍵字也為bool,可賦值為預定義的true和false範例程式碼如下:
var v1 boolv1 = truev2 := (1 == 2) // v2也會被推導為 bool類型
布爾類型不能接受其他類型的賦值,不支援自動或強制的類型轉換。以下的樣本是一些錯誤的用法,會導致編譯錯誤:
var b boolb = 1 // 編譯錯誤b = bool(1) // 編譯錯誤以下的用法才是正確的:var b boolb = (1!=0) // 編譯正確fmt.Println("Result:", b) // 列印結果為 Result: true
整型
1. 類型表示
需要注意的是, int和int32 在Go語言裡被認為是兩種不同的類型,編譯器也不會幫你自動做類型轉換,比如以下的例子會有編譯錯誤:
var value2 int32value1 := 64 // value1將會被自動推導為 int類型value2 = value1 // 編譯錯誤
使用強制類型轉換可以解決這個編譯錯誤:
value2 = int32(value1) // 編譯通過
當然,開發人員在做強制類型轉換時,需要注意資料長度被截短而發生的資料精度損失(比如將浮點數強制轉為整數)和值溢出(值超過轉換的目標類型的值範圍時)問題。
2. 數值運算
Go語言支援下面的常規整數運算: +、 -、 *、 /和%。加減乘除就不詳細解釋了,需要說下的是, % 和在C語言中一樣是求餘運算,比如:
5 % 3 // 結果為: 2
3. 比較運算Go語言支援以下的幾種比較子: >、 <、 ==、 >=、 <=和!=。這一點與大多數其他語言相同,與C語言完全一致。
下面為條件判斷語句的例子:
i, j := 1, 2if i == j { fmt.Println("i and j are equal.")}兩個不同類型的整型數不能直接比較,比如int8類型的數和int類型的數不能直接比較,但各種類型的整型變數都可以直接與字面常量( literal)進行比較,比如:
var i int32var j int64i, j = 1, 2if i == j { // 編譯錯誤 fmt.Println("i and j are equal.")}if i == 1 || j == 2 { // 編譯通過 fmt.Println("i and j are equal.")}
4. 位元運算
Go語言的大多數位元運算符與C語言都比較類似,除了取反在C語言中是~x,而在Go語言中是^x。
浮點型
浮點型用於表示包含小數點的資料,比如1.234就是一個浮點型資料。 Go語言中的浮點類型採用IEEE-754標準的表達方式。
1. 浮點數表示
Go語言定義了兩個類型float32 和float64 ,其中float32 等價於C語言的float 類型,
float64等價於C語言的double類型。
在Go語言裡,定義一個浮點數變數的代碼如下:
var fvalue1 float32fvalue1 = 12fvalue2 := 12.0 // 如果不加小數點, fvalue2會被推導為整型而不是浮點型
對於以上例子中類型被自動推導的fvalue2 ,需要注意的是其類型將被自動設為float64,而不管賦給它的數字是否是用32位長度表示的。因此,對於以上的例子,下面的賦值將導致編譯
錯誤:
fvalue1 = fvalue2
而必須使用這樣的強制類型轉換:
fvalue1 = float32(fvalue2)
2. 浮點數比較
因為浮點數不是一種精確的表達方式,所以像整型那樣直接用==來判斷兩個浮點數是否相等是不可行的,這可能會導致不穩定的結果。
下面是一種推薦的替代方案:
import "math"// p為使用者自訂的比較精度,比如0.00001func IsEqual(f1, f2, p float64) bool { return math.Fdim(f1, f2) < p}
複數類型
複數實際上由兩個實數(在電腦中用浮點數表示)構成,一個表示實部( real),一個表示虛部( imag)。如果瞭解了數學上的複數是怎麼回事,那麼Go語言的複數就非常容易理解了。
1. 複數表示
複數表示的樣本如下:
var value1 complex64 // 由2個float32構成的複數類型value1 = 3.2 + 12ivalue2 := 3.2 + 12i // value2 是complex128類型value3 := complex(3.2, 12) // value3 結果同 value2
2. 實部與虛部
對於一個複數z = complex(x, y) ,就可以通過Go語言內建函數real(z) 獲得該複數的實
部,也就是x,通過imag(z) 獲得該複數的虛部,也就是y。
更多關於複數的函數,請查閱math/cmplx標準庫的文檔。
字串
在Go語言中,字串也是一種基本類型。相比之下, C/C++語言中並不存在原生的字串類型,通常使用字元數組來表示,並以字元指標來傳遞。
Go語言中字串的聲明和初始化非常簡單,舉例如下:
var str string // 聲明一個字串變數str = "Hello world" //字串賦值ch := str[0] // 取字串的第一個字元fmt.Printf("The length of \"%s\" is %d \n", str, len(str))fmt.Printf("The first character of \"%s\" is %c.\n", str, ch)輸出結果為:
The length of "Hello world" is 11The first character of "Hello world" is H.
字串的內容可以用類似於數組下標的方式擷取,但與數組不同,字串的內容不能在初始化後被修改,比如以下的例子:
str := "Hello world" // 字串也支援聲明時進行初始化的做法str[0] = 'X' // 編譯錯誤
在這個例子中我們使用了一個Go語言內建的函數len() 來取字串的長度。這個函數非常有用,我們在實際開發過程中處理字串、數組和切片時將會經常用到。
本節中我們還順便示範了Printf() 函數的用法。有C語言基礎的讀者會發現, Printf() 函數的用法與C語言運行庫中的printf() 函數如出一轍。讀者在以後學習更多的Go語言特性時,可以配合使用Println() 和Printf() 來列印各種自己感興趣的資訊,從而讓學習過程更加直觀、有趣。
Go編譯器支援UTF-8的原始碼檔案格式。這意味著原始碼中的字串可以包含非ANSI的字元,比如“Hello world. 你好,世界!”可以出現在Go代碼中。但需要注意的是,如果你的Go代
碼需要包含非ANSI字元,儲存源檔案時請注意編碼格式必須選擇UTF-8。特別是在Windows下一般編輯器都預設存為本地編碼,比如中國地區可能是GBK編碼而不是UTF-8,如果沒注意這點在編譯和運行時就會出現一些意料之外的情況。
字串的編碼轉換是處理文字文件(比如TXT、 XML、 HTML等)非常常見的需求,不過可惜的是Go語言僅支援UTF-8和Unicode編碼。對於其他編碼, Go語言標準庫並沒有內建的編碼轉換支援。不過,所幸的是我們可以很容易基於iconv庫用Cgo封裝一個。這裡有一個開源項目:
https://github.com/xushiwei/go-iconv。
1. 字串操作
更多的字串操作,請參考標準庫strings包。
2. 字串遍曆
Go語言支援兩種方式遍曆字串。一種是以位元組數組的方式遍曆:
str := "Hello, 世界"n := len(str)for i := 0; i < n; i++ { ch := str[i] // 依據下標取字串中的字元,類型為 byte fmt.Println(i, ch)}這個例子的輸出結果為:
0 721 1012 1083 1084 1115 446 327 2288 1849 15010 23111 14912 140
可以看出,這個字串長度為13。儘管從直觀上來說,這個字串應該只有9個字元。這是因為每個中文字元在UTF-8中佔3個位元組,而不是1個位元組。
另一種是以Unicode字元遍曆:
str := "Hello, 世界"for i, ch := range str { fmt.Println(i, ch)//ch的類型為 rune}輸出結果為:
0 721 1012 1083 1084 1115 446 327 1999010 30028
以Unicode字元方式遍曆時,每個字元的類型是rune(早期的Go語言用int類型表示Unicode
字元),而不是byte。
字元類型
在Go語言中支援兩個字元類型,一個是byte(實際上是uint8的別名),代表UTF-8字串的單個位元組的值;另一個是rune,代單個Unicode字元。
關於rune相關的操作,可查閱 Go標準庫的unicode包。另外unicode/utf8 包也提供了UTF8和Unicode之間的轉換。出於簡化語言的考慮, Go語言的多數API都假設字串為UTF-8編碼。儘管Unicode字元在標準庫中有支援,但實際上較少使用。