? ? ? ? ? ? ?大家好,這是我給大家準備的新的一期專欄,專門講golang,從入門到精通各種框架和中間件,工具類庫,希望對go有興趣的同學可以訂閱此專欄。
---------------------------------------------------------------------------------------------------------------------------------
? ? ? ? ? ? ?上一篇文章中?【go從入門到精通】go包,內置類型和初始化順序? 偏重概念和知識,想必大家都對如何開始寫代碼已經開始蠢蠢欲動了,但是不要著急,基礎知識扎實,能讓你少走很多彎道。
基本數據類型
? ? ? ? 正如前一篇的內容,go的基本數據類型和其他語言都差不多,我們可以先看看go是如何聲明變量的。
標準聲明
Go語言的變量聲明格式為:
var 變量名 變量類型
變量聲明以關鍵字var
開頭,變量類型放在變量的后面,行尾無需分號。 舉個例子:
var str string var i int32var b bool
批量聲明
每聲明一個變量就需要寫var
關鍵字會比較繁瑣,go語言中還支持批量變量聲明:
var (a stringb intc boold float32)
變量的初始化
Go語言在聲明變量的時候,會自動對變量對應的內存區域進行初始化操作。每個變量會被初始化成其類型的默認值,例如: 整型和浮點型變量的默認值為0。 字符串變量的默認值為空字符串。 布爾型變量默認為false
。 切片、函數、指針變量的默認為nil
。
當然我們也可在聲明變量的時候為其指定初始值。變量初始化的標準格式如下:
var 變量名 類型 = 表達式
舉個例子:
? ? var a int32 = 1
?類型推導
有時候我們會將變量的類型省略,這個時候編譯器會根據等號右邊的值來推導變量的類型完成初始化。
? ? var str = "pprof.cn"
? ? var i = 1
短變量聲明
在函數內部,可以使用更簡略的 := 方式聲明并初始化變量。
package mainimport ("fmt"
)func main() {var i = 10j := 100fmt.Println(i,j)
}
匿名變量
????????在使用多重賦值時,如果想要忽略某個值,可以使用匿名變量。 匿名變量用一個下劃線_表示,例如我希望通過strconv。Atoi來將數字字符串轉整型數字,由于這個函數有2個返回值,我只想用i接收最終的整型變量,用_來作為匿名變量,可以用下面的代碼來實現:
package mainimport ("fmt""strconv"
)func main() { i,_:=strconv.Atoi("100")fmt.Println(i)
}
? ? ? ? ? ? 匿名變量不占用命名空間,不會分配內存,所以匿名變量之間不存在重復聲明。 (在Lua等編程語言里,匿名變量也被叫做啞元變量。)
注意事項:
? ? 函數外的每個語句都必須以關鍵字開始(var、const、func等)
? ? :=不能使用在函數外。
? ? _多用于占位,表示忽略值。
const常量的初始化
????????相對于變量,常量是恒定不變的值,多用于定義程序運行期間不會改變的那些值。 常量的聲明和變量聲明非常類似,只是把var換成了const,常量在定義的時候必須賦值。
? ? const pi = 3.1415??
const同時聲明多個常量時,如果省略了值則表示和上面一行的值相同。 例如:
? ? const (
? ? ? ? n1 = 100
? ? ? ? n2
? ? ? ? n3
? ? )
上面示例中,常量n1、n2、n3的值都是100。
其他注意的
但是這里要注意的一些區別是:
? ? ? (1) 空指針值 nil :
package mainimport ("fmt"
)
func main(){i := 0p := &i if p == nil {fmt.Println("p is nil")}
}
? ? ? ?(2)多行字符串的輸出方式:
????????Go語言中要定義一個多行字符串時,就必須使用反引號
字符:
package mainimport ("fmt"
)
func main(){s := `thisisgolangprogram`fmt.Println(s)
}
反引號間換行將被作為字符串中的換行,但是所有的轉義字符均無效,文本將會原樣輸出。
? ??
? ? ? (3)byte和rune類型
????????組成每個字符串的元素叫做“字符”,可以通過遍歷或者單個獲取字符串元素獲得字符。 字符用單引號(’)包裹起來,如:
Go 語言的字符有以下兩種:
? ? uint8類型,或者叫 byte 型,代表了ASCII碼的一個字符。
? ? rune類型,代表一個 UTF-8字符。
var a := '啊'var b := 'b'
當需要處理中文、日文或者其他復合字符時,則需要用到rune
類型。rune
類型實際是一個int32
。
Go 使用了特殊的?rune
?類型來處理?Unicode
,讓基于?Unicode
的文本處理更為方便,也可以使用?byte
?型進行默認字符串處理,性能和擴展性都有照顧
package mainimport ("fmt"
)func disp() {s := "hello 你好"for i := 0; i < len(s); i++ { //bytefmt.Printf("%v(%c) ", s[i], s[i])}fmt.Println("")for _, r := range s { //runefmt.Printf("%v(%c) ", r, r)}
}
func main() {disp()
}
輸出:
104(h) 101(e) 108(l) 108(l) 111(o) 32( ) 228(?) 189(?) 160( ) 229(?) 165(¥) 189(?)?
104(h) 101(e) 108(l) 108(l) 111(o) 32( ) 20320(你) 22909(好)
因為UTF8編碼下一個中文漢字由3-4
個字節組成,所以我們不能簡單的按照字節去遍歷一個包含中文的字符串,否則就會出現上面輸出中第一行的結果。
字符串底層是一個byte數組,所以可以和[]byte類型相互轉換。字符串是不能修改的 字符串是由byte字節組成,所以字符串的長度是byte字節的長度。 rune類型用來表示utf8字符,一個rune字符由一個或多個byte組成。
我們有時候會有這樣的需求,判斷某個游戲玩家起的昵稱不能超過10個漢字或者字符,那么我想你這個時候就知道用哪種方式了。
?(4)修改字符串
????????要修改字符串,需要先將其轉換成[]rune或[]byte
,完成后再轉換為string
。無論哪種轉換,都會重新分配內存,并復制字節數組。
package main
import ("fmt"
)func modstring1(s string) {// 強制類型轉換bytes := []byte(s)bytes[0] = 'H'fmt.Println(string(bytes))
}
func modstring2(s string) {// 強制類型轉換bytes := []rune(s)bytes[0] = '你'fmt.Println(string(bytes))
}
func main() {s := "hello"modstring1(s)modstring2(s)
}
? ? ? (5)array數組? ??
? ? ? ? ? ? go的數組array是同一種數據類型的固定長度的序列。
? ? ? ? ?? (1)如何定義一個數組呢?我們可以用: var 數組名? ?[長度],不過你要注意的是長度也是數組類型的一部分。
? ? ? ? ? ? ?比如:
????????????????var a [10]int 和var a [100]int是不同的類型? ??
? ? ? ? ? ? ? ?var arr0 [5]int = [5]int{1, 2, 3}? // 未初始化元素值為 0。?
? ? ? ? ? ? ? ?c := [5]int{2: 100, 4: 200} // 使用索引號初始化元素。
? ? ? ? ? ? ? ?var arr2 = [...]int{1, 2, 3, 4, 5, 6}??// 通過初始化值確定數組長度。
? ? ? ? ??
? ? ? ? ? ? (2)數組可以通過下標進行訪問,下標是從0開始,最后一個元素下標是:len-1
? ? ? ? ? 遍歷數組有兩種方式:
? ? for i := 0; i < len(a); i++ {
? ? }
? ? for index, v := range a {
? ? }
? ? ? ? ? ? ?(3)訪問越界,如果下標在數組合法范圍之外,則觸發訪問越界,會panic
? ? ? ? ?
package mainimport "fmt"func main() {a := [3]int{1, 2, 3}b := a[0]c := a[6]fmt.Println(b, c)
}
?運行時報錯信息如下:
PS E:\project\go\hello> ./main.exe
panic: runtime error: index out of range [6] with length 3goroutine 1 [running]:
main.main()E:/project/go/hello/main.go:8 +0x1d
? ? ? ? ?
? ? ? ?????????(4)?數組是值類型,賦值和傳參會復制整個數組,而不是指針。因此改變副本的值,不會改變本身的值。
? ? ? ?此時此刻,? ? 一定有人想做下驗證,于是寫了如下代碼:
package mainimport "fmt"func fun1(a []int) {a[0] = 100
}
func main() {a := []int{1, 2, 3}fun1(a)fmt.Println(a[0])
}
? ? ? ?這段代碼似乎是想修改數組a的第一個元素的值,但是很不幸,你可能認為調用fun1之后,a的第一個元素的值仍然是1。
? ? ? 為什么呢?? 因為你傳的不是數組,而且切片。 數組是指定了長度,且是值傳遞 而切片是沒有指定長度,且是引用傳遞。所以你的代碼改成這樣就沒有問題了:
package mainimport "fmt"func fun1(a [3]int) {a[0] = 100
}
func main() {a := [3]int{1, 2, 3}fun1(a)fmt.Println(a[0])
}
? ?(6)slice和map
? ? ? ?后邊逐步針對這slIce,map,以及和數組的三者之間的比較差異來細細道來。?
類型轉換
????????Go語言中只有強制類型轉換,沒有隱式類型轉換。該語法只能在兩個類型之間支持相互轉換的時候使用。
強制類型轉換的基本語法如下:
? ? ? ? 目標類型(表達式)
其中,目標類型表示要轉換的類型。表達式包括變量、復雜算子和函數返回值等.
package mainimport ("fmt"
)func main() {i := int32(0)var j intj = i fmt.Println(j)
}
?比如這段代碼,簡簡單單,但是編譯的時候報錯:
cannot use i (variable of type int32) as int value in assignment
所以我們在把i賦值給j的時候,需要做個類型強制轉換,j = int(i)
運算符
? ? ? ? ?go的常用的運算符用法和其他語言沒有區別
? ? ??