W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
這小節(jié)我們將要介紹如何定義變量、常量、Go內(nèi)置類型以及Go程序設(shè)計中的一些技巧。
Go語言里面定義變量有多種方式。
使用var
關(guān)鍵字是Go最基本的定義變量方式,與C語言不同的是Go把變量類型放在變量名后面:
//定義一個名稱為“variableName”,類型為"type"的變量
var variableName type
定義多個變量
//定義三個類型都是“type”的變量
var vname1, vname2, vname3 type
定義變量并初始化值
//初始化“variableName”的變量為“value”值,類型是“type”
var variableName type = value
同時初始化多個變量
/*
定義三個類型都是"type"的變量,并且分別初始化為相應(yīng)的值
vname1為v1,vname2為v2,vname3為v3
*/
var vname1, vname2, vname3 type= v1, v2, v3
你是不是覺得上面這樣的定義有點繁瑣?沒關(guān)系,因為Go語言的設(shè)計者也發(fā)現(xiàn)了,有一種寫法可以讓它變得簡單一點。我們可以直接忽略類型聲明,那么上面的代碼變成這樣了:
/*
定義三個變量,它們分別初始化為相應(yīng)的值
vname1為v1,vname2為v2,vname3為v3
然后Go會根據(jù)其相應(yīng)值的類型來幫你初始化它們
*/
var vname1, vname2, vname3 = v1, v2, v3
你覺得上面的還是有些繁瑣?好吧,我也覺得。讓我們繼續(xù)簡化:
/*
定義三個變量,它們分別初始化為相應(yīng)的值
vname1為v1,vname2為v2,vname3為v3
編譯器會根據(jù)初始化的值自動推導(dǎo)出相應(yīng)的類型
*/
vname1, vname2, vname3 := v1, v2, v3
現(xiàn)在是不是看上去非常簡潔了?:=
這個符號直接取代了var
和type
,這種形式叫做簡短聲明。不過它有一個限制,那就是它只能用在函數(shù)內(nèi)部;在函數(shù)外部使用則會無法編譯通過,所以一般用var
方式來定義全局變量。
_
(下劃線)是個特殊的變量名,任何賦予它的值都會被丟棄。在這個例子中,我們將值35
賦予b
,并同時丟棄34
:
_, b := 34, 35
Go對于已聲明但未使用的變量會在編譯階段報錯,比如下面的代碼就會產(chǎn)生一個錯誤:聲明了i
但未使用。
package main
func main() {
var i int
}
所謂常量,也就是在程序編譯階段就確定下來的值,而程序在運行時無法改變該值。在Go程序中,常量可定義為數(shù)值、布爾值或字符串等類型。
它的語法如下:
const constantName = value
//如果需要,也可以明確指定常量的類型:
const Pi float32 = 3.1415926
下面是一些常量聲明的例子:
const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"
賦值后打印出這些常量后的結(jié)果為
Go 常量和一般程序語言不同的是,可以指定相當(dāng)多的小數(shù)位數(shù)(例如200位), 若指定給float32自動縮短為32bit,指定給float64自動縮短為64bit,詳情參考鏈接
在Go中,布爾值的類型為bool
,值是true
或false
,默認(rèn)為false
。
//示例代碼
var isActive bool // 全局變量聲明
var enabled, disabled = true, false // 忽略類型的聲明
func test() {
var available bool // 一般聲明
valid := false // 簡短聲明
available = true // 賦值操作
}
整數(shù)類型有無符號和帶符號兩種。Go同時支持int
和uint
,這兩種類型的長度相同,但具體長度取決于不同編譯器的實現(xiàn)。Go里面也有直接定義好位數(shù)的類型:rune
, int8
, int16
, int32
, int64
和byte
, uint8
, uint16
, uint32
,uint64
。其中rune
是int32
的別稱,byte
是uint8
的別稱。
需要注意的一點是,這些類型的變量之間不允許互相賦值或操作,不然會在編譯時引起編譯器報錯。
如下的代碼會產(chǎn)生錯誤:invalid operation: a + b (mismatched types int8 and int32)
var a int8
var b int32
c:=a + b
另外,盡管int的長度是32 bit, 但int 與 int32并不可以互用。
浮點數(shù)的類型有float32
和float64
兩種(沒有float
類型),默認(rèn)是float64
。
這就是全部嗎?No!Go還支持復(fù)數(shù)。它的默認(rèn)類型是complex128
(64位實數(shù)+64位虛數(shù))。如果需要小一些的,也有complex64
(32位實數(shù)+32位虛數(shù))。復(fù)數(shù)的形式為RE + IMi
,其中RE
是實數(shù)部分,IM
是虛數(shù)部分,而最后的i
是虛數(shù)單位。下面是一個使用復(fù)數(shù)的例子:
var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)
我們在上一節(jié)中講過,Go中的字符串都是采用UTF-8
字符集編碼。字符串是用一對雙引號(""
)或反引號(`)括起來定義,它的類型是string
。
//示例代碼
var frenchHello string // 聲明變量為字符串的一般方法
var emptyString string = "" // 聲明了一個字符串變量,初始化為空字符串
func test() {
no, yes, maybe := "no", "yes", "maybe" // 簡短聲明,同時聲明多個變量
japaneseHello := "Konichiwa" // 同上
frenchHello = "Bonjour" // 常規(guī)賦值
}
在Go中字符串是不可變的,例如下面的代碼編譯時會報錯:cannot assign to s[0]
var s string = "hello"
s[0] = 'c'
但如果真的想要修改怎么辦呢?下面的代碼可以實現(xiàn):
s := "hello"
c := []byte(s) // 將字符串 s 轉(zhuǎn)換為 []byte 類型
c[0] = 'c'
s2 := string(c) // 再轉(zhuǎn)換回 string 類型
fmt.Printf("%s\n", s2)
Go中可以使用+
操作符來連接兩個字符串:
s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)
修改字符串也可寫為:
s := "hello"
s = "c" + s[1:] // 字符串雖不能更改,但可進(jìn)行切片操作
fmt.Printf("%s\n", s)
如果要聲明一個多行的字符串怎么辦?可以通過' '來聲明:
m := `hello
world`
``` 括起的字符串為Raw字符串,即字符串在代碼中的形式就是打印時的形式,它沒有字符轉(zhuǎn)義,換行也將原樣輸出。例如本例中會輸出:
hello
world
Go內(nèi)置有一個error
類型,專門用來處理錯誤信息,Go的package
里面還專門有一個包errors
來處理錯誤:
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
下面這張圖來源于Russ Cox Blog中一篇介紹Go數(shù)據(jù)結(jié)構(gòu)的文章,大家可以看到這些基礎(chǔ)類型底層都是分配了一塊內(nèi)存,然后存儲了相應(yīng)的值。
map
也就是Python中字典的概念,它的格式為map[keyType]valueType
我們看下面的代碼,map
的讀取和設(shè)置也類似slice
一樣,通過key
來操作,只是slice
的index
只能是`int`類型,而map
多了很多類型,可以是int
,可以是string
及所有完全定義了==
與!=
操作的類型。
// 聲明一個key是字符串,值為int的字典,這種方式的聲明需要在使用之前使用make初始化
numbers := make(map[string]int)
// 另一種map的聲明方式
var numbers map[string]int
numbers["one"] = 1 //賦值 numbers["tow"] = 2 //賦值
numbers["three"] = 3 //賦值
fmt.Println("第一個數(shù)字是: ", numbers["one"]) // 讀取數(shù)據(jù)
fmt.Println("第二個數(shù)字是: ", numbers["tow"]) // 讀取數(shù)據(jù)
fmt.Println("第三個數(shù)字是: ", numbers["three"]) // 讀取數(shù)據(jù)
按照上面的輸入,輸出結(jié)果為
這個map
就像我們平??吹降谋砀褚粯?,左邊列是key
,右邊列是值
使用map過程中需要注意的幾點:
map
是無序的,每次打印出來的map
都會不一樣,它不能通過index
獲取,而必須通過key
獲取map
的長度是不固定的,也就是和slice
一樣,也是一種引用類型len
函數(shù)同樣適用于map
,返回map
擁有的key
的數(shù)量map
的值可以很方便的修改,通過numbers["one"]=11
可以很容易的把key為one
的字典值改為11
map
和其他基本型別不同,它不是thread-safe,在多個go-routine存取時,必須使用mutex lock機(jī)制map
的初始化可以通過key:val
的方式初始化值,同時map
內(nèi)置有判斷是否存在key
的方式
通過delete
刪除map
的元素:
// 初始化一個字典
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map有兩個返回值,第二個返回值,如果不存在key,那么ok為false,如果存在ok為true
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C") // 刪除key為C的元素
上面說過了,map
也是一種引用類型,如果兩個map
同時指向一個底層,那么一個改變,另一個也相應(yīng)的改變:
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // 現(xiàn)在m["hello"]的值已經(jīng)是Salut了
make
用于內(nèi)建類型(map
、slice
和channel
)的內(nèi)存分配。new
用于各種類型的內(nèi)存分配。
內(nèi)建函數(shù)new
本質(zhì)上說跟其它語言中的同名函數(shù)功能一樣:new(T)
分配了零值填充的T
類型的內(nèi)存空間,并且返回其地址,即一個*T
類型的值。用Go的術(shù)語說,它返回了一個指針,指向新分配的類型T
的零值。有一點非常重要:
new
返回指針。
內(nèi)建函數(shù)make(T, args)
與new(T)
有著不同的功能,make只能創(chuàng)建slice
、map
和channel
,并且返回一個有初始值(非零)的T
類型,而不是*T
。本質(zhì)來講,導(dǎo)致這三個類型有所不同的原因是指向數(shù)據(jù)結(jié)構(gòu)的引用在使用前必須被初始化。例如,一個slice
,是一個包含指向數(shù)據(jù)(內(nèi)部array
)的指針、長度和容量的三項描述符;在這些項目被初始化之前,slice
為nil
。對于slice
、map
和channel
來說,make
初始化了內(nèi)部的數(shù)據(jù)結(jié)構(gòu),填充適當(dāng)?shù)闹怠?/p>
make
返回初始化后的(非零)值。
下面這個圖詳細(xì)的解釋了new
和make
之間的區(qū)別。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: