本內容爲本身學習go知識記錄的筆記,方便複習查看
筆記內容參考無聞老師的github:https://github.com/Unknwon/go-fundamental-programming
課程視頻內容:百度網盤(提取碼:mgom)
筆記內容參考:Go編程基礎-課堂講義python
Go是一門 併發支持 、垃圾回收 的 編譯型 系統編程語言,旨在創造一門具備在靜態編譯語言的 高性能 和動態語言的 高效開發 之間擁有良好平衡點的一門編程語言。git
類型安全 和 內存安全
以很是直觀和極低代價的方案實現 高併發
高效的垃圾回收機制
快速編譯(同時解決C語言中頭文件太多的問題)
爲多核計算機×××能提高的方案
UTF-8編碼支持github
Go在谷歌:以軟件工程爲目的的語言設計編程
包括VIM,IDEA,Sublime Text,Eclipse等衆多知名IDE均已支持數組
全球最大視頻網站 Youtube(谷歌)
七牛雲存儲以及旗下網盤服務(Q盤)
愛好者開發的Go論壇及博客
已用Go開發服務端的著名企業:谷歌、盛大、七牛、360
其它海量開源項目:go-wiki、Go Walker、Go Language Resources七牛雲存儲
做爲一門2009年才正式發佈的編程語言,Go是很是年輕的,所以不能稱爲一門成熟的編程語言,但開發社區天天都在不斷更新其核心代碼,給咱們這些愛好者給予了很大的學習和開發動力。安全
以Google Group爲主的郵件列表天天都會更新10至20帖,國內的Go愛好者QQ羣和論壇天天也在進行大量的討論,所以能夠說目前Go愛好者羣體是足夠壯大。閉包
Go源碼安裝:參考連接
Go標準包安裝:下載地址
第三方工具安裝併發
根據約定,GOPATH下須要創建3個目錄:
bin(存放編譯後生成的可執行文件)
pkg(存放編譯後生成的包文件)
src(存放項目源碼)app
在命令行或終端輸入go便可查看全部支持的命令
go get:獲取遠程包(需 提早安裝 git或hg) go run:直接運行程序 go build:測試編譯,檢查是否有編譯錯誤 go fmt:格式化源碼(部分IDE在保存時自動調用) go install:編譯包文件並編譯整個程序 go test:運行測試文件 go doc:查看文檔(CHM手冊)
本套教程主要使用 Sublime Text
其它IDE安裝方案:參考連接
Sublime Text
下載Sublime Text:官方網站
安裝gosublime(破解版可能沒法安裝):安裝指令
Sublime Text 2 入門及技巧
輸出:hello.go
- break default func interface select - case defer go map struct - chan else goto package switch - const fallthrough if range type - continue for import return var
// 單行註釋 /* */ 多行註釋
Go程序是經過 package 來組織的(與python相似)
只有 package 名稱爲 main 的包能夠包含 main 函數
一個可執行程序 有且僅有 一個 main 包
經過 import 關鍵字來導入其它非 main 包
經過 const 關鍵字來進行常量的定義
經過在函數體外部使用 var 關鍵字來進行全局變量的聲明與賦值
經過 type 關鍵字來進行結構(struct)或接口(interface)的聲明
經過 func 關鍵字來進行函數的聲明
導入包以後,就可使用格式<PackageName>.<FuncName>
來對包中的函數進行調用
若是導入包以後 未調用 其中的函數或者類型將會報出編譯錯誤:
當使用第三方包時,包名可能會很是接近或者相同,此時就可使用
別名來進行區別和調用
不建議使用,易混淆
不能夠和別名同時使用
Go語言中,使用 大小寫 來決定該 常量、變量、類型、接口、結構
或函數 是否能夠被外部包所調用:
根據約定,函數名首字母 小寫 即爲private
函數名首字母 大寫 即爲public
或通常類型(非接口、非結構)是否也能夠用一樣的方法呢?
布爾型:bool
type (
bype int8
rune int32
文本 string
)
func main() {
var b 文本
b = "中文類型名字"
fmt.Println(b)
}
$ go run basic_struct.go
中文類型名字
變量的聲明格式:var <變量名稱> <變量類型>
變量的賦值格式:<變量名稱> = <表達式>
聲明的同時賦值:var <變量名稱> [變量類型] = <表達式>
Go中不存在隱式轉換,全部類型轉換必須顯式聲明
轉換隻能發生在兩種相互兼容的類型之間
類型轉換的格式:
package main import ( "fmt" ) func main() { //fmt.Println("hello world") var a int = 65 fmt.Println(a) b := string(a) fmt.Println(b) } daixuandeMacBook-Pro:學習go daixuan$ go run hello.go 65 A 若是我就是想輸出字符串65,怎麼辦? package main import ( "fmt" "strconv" ) func main() { var a int = 65 b := strconv.Itoa(a) fmt.Println(b) } $ go run basic_struct.go 65
string() 表示將數據轉換成文本格式,由於計算機中存儲的任何東西
本質上都是數字,所以此函數天然地認爲咱們須要的是用數字65表示
的文本 A。
常量的值在編譯時就已經肯定
常量的定義格式與變量基本相同
等號右側必須是常量或者常量表達式
常量表達式中的函數必須是內置函數
在定義常量組時,若是不提供初始值,則表示將使用上行的表達式
使用相同的表達式不表明具備相同的值
package main import ( "fmt" ) const ( a, b = 1, "2" c, d //這裏兩個變量c,d使用初始化規則,值等於上一行表達式,注意每一行的常量個數相同 ) func main() { fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) } $ go run basic_struct.go 1 2 1 2
iota是常量的計數器,從0開始,組中每定義1個常量自動遞增1
經過初始化規則與iota能夠達到枚舉的效果
每遇到一個const關鍵字,iota就會重置爲0
package main import ( "fmt" ) const ( a = "A" b //b初始化規則b=a="A" c = iota //已經有2個常量a,b,因此c=2 d //注意:這裏d套用了c的常量表達式d=iota,已經有3個常量a,b,c,因此d=3 ) const ( e = iota //每遇到一個const關鍵字,iota就會重置爲0,因此e=iota=0 f //f套用了e的常量表達式f=iota,e=0,f=e+1=1 ) func main() { fmt.Println(a) fmt.Println(b) fmt.Println(c) fmt.Println(d) fmt.Println(e) fmt.Println(f) } go run basic_struct.go A A 2 3 0 1
Go中的運算符均是從左至右結合
優先級(從高到低)
^ ! (一元運算符)
^(異或,相同爲0,不一樣爲1) (二元運算符) == != < <= >= > <- (專門用於channel) && |
---|
package main import ( "fmt" ) /* 6 :0110 11:1011 & 0010 = 2 | 1111 =15 ^ 1101 =13 //異或,相同爲0,不一樣爲1,1^0=1,0^1=1,0^0=0,1^1=0 &^ 0100 = 4 //表示若是B後邊的爲1,須要強制將A的改成0 */ func main() { fmt.Println(6 & 11) fmt.Println(6 | 11) fmt.Println(6 ^ 11) fmt.Println(6 &^ 11) } go run basic_struct.go 2 15 13 4
Go雖然保留了指針,但與其它編程語言不一樣的是,在Go當中不
支持指針運算以及」->」運算符,而直接採用」.」選擇符來操做指針
目標對象的成員
操做符」&」取變量地址,使用」*」經過指針間接訪問目標對象
默認值爲 nil 而非 NULL
遞增遞減語句
func main() { a := 1 var p *int = &a //p是指向a地址(0xc420016088)的int指針 fmt.Println(p) fmt.Println(*p) } $go run basic_struct.go 0xc420016088 1 *p取地址內的值1 func main() { a := 1 var p = &a fmt.Println(p) fmt.Println(*p) } $ go run basic_struct.go 0xc420016088 1
在Go當中,++ 與 -- 是做爲語句而並非做爲表達式
簡單理解爲:
/容許: a :=1 a++ //不容許,報錯 a :=1 a := a++
條件表達式沒有括號
支持一個初始化表達式(能夠是並行方式)
左大括號必須和條件語句或else在同一行
支持單行模式
初始化語句中的變量爲block級別,同時隱藏外部同名變量1.0.3版本中的編譯器BUG
func main() { a := 10 if a := 3; a > 1 { //能夠在if中初始化a:=3,可是a=3僅僅在if中有效 fmt.Println(a) } fmt.Println(a) //若是沒有提早定義a :=10 ,這裏會報錯 } go run basic_struct.go 3 10
func main() { a := true if a, b, c := 1, 2, 3; a+b+c > 6 { //能夠在if中初始化a:=1,可是a=1僅僅在if中有效,if外a=true fmt.Println("大於6") } else { fmt.Println("小於等於6") fmt.Println(a) } fmt.Println(a) } $ go run basic_struct.go 小於等於6 1 true
Go只有for一個循環語句關鍵字,但支持3種形式
初始化和步進表達式能夠是多個值
條件語句每次循環都會被從新檢查,所以不建議在條件語句中
使用函數,儘可能提早計算好條件並以變量或常量代替
左大括號必須和條件語句在同一行
(1)for+if
func main() { a := 1 for { a++ if a > 3 { break } fmt.Println(a) } fmt.Println("Over") } $ go run basic_struct.go 2 3 Over
(2)for 自帶判斷條件
func main() { a := 1 for a <= 3 { a++ fmt.Println(a) } fmt.Println("Over") } $ go run basic_struct.go 2 3 4 Over
(3經典方式
func main() { a := 1 for i := 0; i < 3; i++ { a++ fmt.Println(a) } fmt.Println("Over") } $ go run basic_struct.go 2 3 4 Over
可使用任何類型或表達式做爲條件語句
不須要寫break,一旦條件符合自動終止
如但願繼續執行下一個case,需使用fallthrough語句
支持一個初始化表達式(能夠是並行方式),右側需跟分號
左大括號必須和條件語句在同一行
func main() { a := 1 switch a { case 0: fmt.Println("a=0") case 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=1 func main() { a := 1 switch { case a >= 0: fmt.Println("a=0") //a=0知足條件,打印a=0跳出 case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=0 func main() { a := 1 switch { case a >= 0: fmt.Println("a=0") //a=0知足條件,打印a=0,有fallthrough不跳出,檢查下一個 fallthrough case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=0 a=1 func main() { switch a := 1; { //在switch中定義a,做用於switch內部,外部調用失敗 case a >= 0: fmt.Println("a=0") fallthrough case a >= 1: fmt.Println("a=1") default: fmt.Println("None") } } $ go run basic_struct.go a=0 a=1
三個語法均可以配合標籤使用,實現跳出多層循環
標籤名區分大小寫,若不使用會形成編譯錯誤
Break與continue配合標籤可用於多層循環的跳出
Goto是調整執行位置,與其它2個語句配合標籤的結果並不相同
func main() { LABEL1: for { for i := 0; i < 10; i++ { if i > 3 { break LABEL1 } } } fmt.Println("ok") } $ go run basic_struct.go ok func main() { for { for i := 0; i < 10; i++ { if i > 3 { goto LABEL1 } } } LABEL1: //標籤放在最外層循環的外側,確保不死循環,能夠跳出 fmt.Println("ok") } $ go run basic_struct.go ok func main() { LABEL1: for i := 0; i < 10; i++ { //把有限循環放在最外面,那麼continue最終會結束,不會死循環 for { continue LABEL1 fmt.Println(i) } } fmt.Println("ok") } $ go run basic_struct.go ok
請嘗試並思考爲何。
func main() { LABEL1: for i := 0; i < 10; i++ { for { fmt.Println(i) continue LABEL1 } } fmt.Println("ok") } $ go run basic_struct.go 0 1 2 3 4 5 6 7 8 9 ok func main() { LABEL1: for i := 0; i < 10; i++ { for { fmt.Println(i) goto LABEL1 } } fmt.Println("ok") } $ go run basic_struct.go 0 0 0 ...... 由於一執行循環,輸出0後,goto到label1,用從新開始循環,從新輸出0以後,又調到label1,又進入循環,無線下去輸出0,死循環
定義數組的格式:var <varName> [n]<type>,n>=0
數組長度也是類型的一部分,所以具備不一樣長度的數組爲不一樣類型
func main() { var a [2]int //初始化int類型數組a,定義爲2個元素,默認值爲0 var b [2]int b = a fmt.Println(b) } $ go run basic_struct.go [0 0] func main() { a := [2]int{1, 1} //初始化int類型數組a,定義爲2個元素,初始值爲1 var b [2]int b = a fmt.Println(b) } $ go run basic_struct.go [1 1] func main() { a := [2]int{1} //初始化int類型數組a,定義爲2個元素,初始值一、0 var b [2]int b = a fmt.Println(b) } $ go run basic_struct.go [1 0] func main() { a := [20]int{18: 2, 19: 1} //定義數組第19個元素值爲2,第20個元素爲1 fmt.Println(a) } $ go run basic_struct.go [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 1] func main() { a := [...]int{3, 2, 1, 19: 1} //使用...定義數組,第20個元素是1,則數組長度爲20 fmt.Println(a) } $ go run basic_struct.go [3 2 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]
注意區分:
指向數組的指針[100]int -->&[0 0 0 0 0 0 0 0 0 1]
指針數組[100]int -->[0xc420016088 0xc420016090]
func main() { a := [...]int{9: 1} var p *[10]int = &a //p是指向數組的指針 fmt.Println(p) } $ go run basic_struct.go &[0 0 0 0 0 0 0 0 0 1] //數組前面有一個&,p是指向數組的指針 func main() { a := [...]int{9: 1} var p = &a //這樣更簡單,p是指向數組的指針 fmt.Println(p) } $ go run basic_struct.go &[0 0 0 0 0 0 0 0 0 1] func main() { x, y := 1, 2 a := [...]*int{&x, &y} //定義a是指針類型的數組,保存的數組元素是指向int型的指針:變量x和y的地址 fmt.Println(a) } $ go run basic_struct.go [0xc420016088 0xc420016090]
數組在Go中爲值類型,傳遞數組是整個拷貝的,其餘語言爲了節省內存,數組是引用類型。
數組之間可使用==或!=進行比較,但不可使用<或>
func main() { a := [2]int{1, 2} b := [2]int{1, 2} fmt.Println(a == b) } $ go run basic_struct.go true func main() { a := [2]int{1, 2} b := [2]int{1, 200} fmt.Println(a == b) } $ go run basic_struct.go false //注意:數組長度不一樣,沒法直接比較,不然報錯 func main() { a := [2]int{1, 2} b := [1]int{10} fmt.Println(a == b) } $ go run basic_struct.go # command-line-arguments ./basic_struct.go:17:16: invalid operation: a == b (mismatched types [2]int and [1]int)
可使用new來建立數組,此方法返回一個指向數組的指針
func main() { p := new([10]int) fmt.Println(p) } $ go run basic_struct.go &[0 0 0 0 0 0 0 0 0 0] //可使用索引直接對數組元素操做 func main() { a := [10]int{} a[1] = 2 //使用索引直接對數組元素賦值 fmt.Println(a) p := new([10]int) p[1] = 2 //使用索引直接對數組元素賦值 fmt.Println(p) fmt.Println(*p) } $ go run basic_struct.go [0 2 0 0 0 0 0 0 0 0] &[0 2 0 0 0 0 0 0 0 0] [0 2 0 0 0 0 0 0 0 0]
Go支持多維數組
func main() { a := [2][3]int{ {1, 1, 1}, {2, 2, 2}} fmt.Println(a) } $ go run basic_struct.go [[1 1 1] [2 2 2]] func main() { a := [2][3]int{ {1: 1}, //給數組元素賦值 {2: 2}} //給數組元素賦值 fmt.Println(a) } $ go run basic_struct.go [[0 1 0] [0 0 2]]
Go語言版冒泡排序
func main() { a := []int{5, 2, 6, 3, 9} fmt.Println(a) num := len(a) for i := 0; i < num; i++ { for j := i + 1; j < num; j++ { if a[i] < a[j] { temp := a[i] a[i] = a[j] a[j] = temp } } } fmt.Println(a) } $ go run basic_struct.go [5 2 6 3 9] [9 6 5 3 2]
其自己並非數組,它指向底層的數組
做爲變長數組的替代方案,能夠關聯底層數組的局部或所有
爲引用類型
能夠直接建立或從底層數組獲取生成
使用len()獲取元素個數,cap()獲取容量
通常使用make()建立
若是多個slice指向相同底層數組,其中一個的值改變會影響所有
func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[5:10]//a[5:10]指的是:a[5,6,7,8,9],不包括a[10] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [6 7 8 9 0] func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[5:len(a)]//a[5,6,7,8,9] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [6 7 8 9 0] func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[5:]//a[5,6,7,8,9] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [6 7 8 9 0] func main() { a := [10]int{1,2,3,4,5,6,7,8,9,0} fmt.Println(a) s1:=a[:5]//a[5,6,7,8,9] fmt.Println(s1) } $ go run basic_struct.go [1 2 3 4 5 6 7 8 9 0] [1 2 3 4 5]
make([]T, len, cap)
其中cap能夠省略,則和len的值相同
len表示存數的元素個數,cap表示容量
func main() { s1:=make([]int,3,10) fmt.Println(s1) } $ go run basic_struct.go [0 0 0] func main() { s1:=make([]int,3,10) fmt.Println(s1) fmt.Println(len(s1),cap(s1))//打印元素個數3和容量10 } $ go run basic_struct.go [0 0 0] 3 10
func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sa := a[2:5] fmt.Println(string(sa)) } $ go run basic_struct.go cde func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sb := a[3:5] fmt.Println(string(sb)) } $ go run basic_struct.go de
Reslice時索引以被slice的切片爲準
索引不能夠超過被slice的切片的容量cap()值
索引越界不會致使底層數組的從新分配而是引起錯誤
func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sa := a[2:5] fmt.Println(len(sa),cap(sa))//sa元素3,容量是9 sb := a[3:5] fmt.Println(len(sb),cap(sb))//sb元素2,容量是8,比sa少1 fmt.Println(string(sa)) fmt.Println(string(sb)) } $ go run basic_struct.go 3 9 2 8 cde de func main() { a:=[]byte{'a','b','c','d','e','f','g','h','i','j','k'} sa := a[2:5] fmt.Println(len(sa),cap(sa))//sa元素3,容量是9(cdefghijk) sb := sa[2:5] fmt.Println(len(sb),cap(sb))//sb元素3,容量是7(efghijk) fmt.Println(string(sa)) fmt.Println(string(sb)) } $ go run basic_struct.go 3 9 3 7 cde efg
能夠在slice尾部追加元素
能夠將一個slice追加在另外一個slice尾部
若是最終長度未超過追加到slice的容量則返回原始slice
若是超過追加到的slice的容量則將從新分配數組並拷貝原始數據(容量翻倍)
func main() { s1 := make([]int,3,6) fmt.Printf("%p\n",s1) s1 = append(s1,1,2,3) fmt.Printf("%v,%p\n",s1,s1) } $ go run basic_struct.go 0xc4200180c0 //容量沒有變化,內存地址沒有變化 [0 0 0 1 2 3] 0xc4200180c0 func main() { s1 := make([]int,3,6) fmt.Printf("%p\n",s1) s1 = append(s1,1,2,3) fmt.Printf("%v,%p\n",s1,s1) s1 = append(s1,1,2,3) //這裏容量不夠,從新分配容量(翻倍),拷貝原來元素再追加 fmt.Printf("%v,%p\n",s1,s1) } $ go run basic_struct.go 0xc420092000 [0 0 0 1 2 3],0xc420092000 [0 0 0 1 2 3 1 2 3],0xc420072060 func main() { a := []int{1,2,3,4,5} s1 := a[2:5] s2 := a[1:3] fmt.Println(s1,s2) s1[0] = 9 //slice指向底層數組,多個slice指向同一個數組時候,其中一個改變,會影響數組,同時影響其餘slice值 fmt.Println(a) fmt.Println(s1,s2) } $ go run basic_struct.go [3 4 5] [2 3] [1 2 9 4 5] [9 4 5] [2 9] func main() { a := []int{1,2,3,4,5} s1 := a[2:5] s2 := a[1:3] fmt.Println(s1,s2) s2=append(s2,1,2,1,1,1,1,1,1,1)//apend元素個數超過slice容量,會指向新的內存地址的底層數組(從a拷貝過來的),此時改變a,不會影響s2的值 s1[0] = 9 fmt.Println(a) fmt.Println(s1) fmt.Println(s2) } $ go run basic_struct.go [3 4 5] [2 3] [1 2 9 4 5] [9 4 5] [2 3 1 2 1 1 1 1 1 1 1]
slice拷貝,以個數少的爲準
//把s2拷貝到s1 func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s1,s2) //s2的元素7,8,9會覆蓋s1的前三個元素1,2,3 fmt.Println(s1) } $ go run basic_struct.go [7 8 9 4 5 6] //把s1拷貝到s2 func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s2,s1)//把s1的前三個元素1,2,3拷貝覆蓋s2 fmt.Println(s2,s1) } $ go run basic_struct.go [1 2 3] [1 2 3 4 5 6] func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s2,s1[3:6]) //把s1的後三個元素拷貝到s2,覆蓋s2元素 fmt.Println(s2,s1) } $ go run basic_struct.go [4 5 6] [1 2 3 4 5 6] func main() { s1:=[]int{1,2,3,4,5,6} s2:=[]int{7,8,9} copy(s2[1:3],s1[4:6]) //把s1的後2個元素拷貝到s2的後兩位,覆蓋s2後2個元素,保留第一個元素7 fmt.Println(s2,s1) } $ go run basic_struct.go [7 5 6] [1 2 3 4 5 6]
相似其它語言中的哈希表或者字典,以key-value形式存儲數據
Key必須是支持==或!=比較運算的類型,不能夠是函數、map或slice
Map查找比線性搜索快不少,但比使用索引訪問數據的類型慢100倍
Map使用make()建立,支持 := 這種簡寫方式
func main() { var m map[int]string //定義map,int是key類型,string是value類型 m=map[int]string{} fmt.Println(m) } $ go run basic_struct.go map[]
make([keyType]valueType, cap),cap表示容量,可省略
超出容量時會自動擴容,但儘可能提供一個合理的初始值
使用len()獲取元素個數
func main() { var m map[int]string=make(map[int]string) fmt.Println(m) } $ go run basic_struct.go map[] func main() { var m =make(map[int]string) fmt.Println(m) } $ go run basic_struct.go map[] func main() { m :=make(map[int]string) fmt.Println(m) } $ go run basic_struct.go map[] func main() { m :=make(map[int]string) m[1] = "ok" fmt.Println(m) } $ go run basic_struct.go map[1:ok] func main() { m :=make(map[int]string) m[1] = "ok" a:=m[1] fmt.Println(a) } ok func main() { m :=make(map[int]string) //m[1] = "ok" a:=m[1] fmt.Println(a) } 輸出爲空
鍵值對不存在時自動添加,使用delete()刪除某鍵值對
使用 for range 對map和slice進行迭代操做
func main() { m :=make(map[int]string) m[1] = "ok" delete(m,1) a:=m[1] fmt.Println(a) } 輸出爲空 func main() { m := make(map[int]map[int]string) //使用make建立初始化m,定義m的value是另外一個map[int]string m[1]=make(map[int]string)//這裏是用make初始化另外一個map,做爲value賦值給m[1] m[1][1]= "ok" a:=m[1][1] fmt.Println(a) } ok func main() { m := make(map[int]map[int]string) m[1]=make(map[int]string) //這裏只對m[1]初始化了,沒有對m[2]初始化,因此把ok賦值給nil報錯 m[2][1]= "ok" b:=m[2][1] fmt.Println(b) } panic: assignment to entry in nil map goroutine 1 [running]: main.main() /Users/daixuan/go/hello.go:8 +0x168
map嵌套map需注意,每一級的map都得初始化,怎麼知道map是否被初始化呢?
採用多返回值的方式,當有一個返回值的時候,返回value,當有多個返回值的時候,第二個返回bool類型的值,true或者false
package main import "fmt" func main() { m := make(map[int]map[int]string) m[1]=make(map[int]string) m[1][1]= "123" a, ok :=m[1][1] //m[1]被初始化,a=123,不爲空,因此ok=true b, nok :=m[2][1] //m[2]未被初始化,b=nil,爲空,因此nok=false fmt.Println(a,ok) fmt.Println(b,nok) } 123 true false package main import "fmt" func main() { m := make(map[int]map[int]string) a, ok :=m[2][1] fmt.Println(a,ok) //第一次沒有初始化map,因此ok=false if !ok { //這裏判斷是否ok==false,那麼去初始化map[2](第二級的map) m[2]=make(map[int]string)//初始化第二級別map } m[2][1]="good"//賦值給第二級map的key=1的value是'good' a, ok =m[2][1] fmt.Println(a,ok) //a=good,因此ok=true } false good true
迭代操做(slice和map均可以迭代操做):
package main import ( "fmt" ) func main() { slice := []int{10,20,30,40} for index,value :=range slice { //i是slice的索引,至關於計數器,int型,0,1,2,3,4....,v是slice存儲的值取出賦值給v,修改v的值不會影響slice自己 if slice[index]==30{ slice[index]=300 //使用slice[index]=300 能夠直接修改slice原始的值爲300 value=400 fmt.Println(index,value) fmt.Println(slice[index]) //使用slice[index]=300 能夠直接修改slice原始的值爲300 } } } 2 400 300 package main import ( "fmt" ) func main() { slice := []int{10,20,30,40} for _,value :=range slice { //用空白標識符下劃線 _ 來忽略索引值 fmt.Println(value) } } 10 20 30 40 package main import ( "fmt" ) func main() { m := make(map[int]string) m[1]="ok" for key, value := range m { fmt.Println(key, value) } info := map[string]string{ "name": "david", "address": "shanghai", } for k,v := range info{ fmt.Printf("Key:%s,Value:%s\n",k,v) } } 1 ok Key:name,Value:david Key:address,Value:shanghai package main import ( "fmt" ) func main() { sm := make([]map[int]string,5)//定義一個以map爲原數類型的slice,定義map方法:m :=map[int]string,定義slice方法:slice := make([]string,5) for _, v := range sm { //對slice:sm進程迭代操做 v = make(map[int]string) //對slice中的map初始化 v[1] ="OK" //這裏對v是個拷貝賦值,不會影響slice自己的值,因此v=map[1:OK],而sm是:[map[] map[] map[] map[] map[]] fmt.Println(v) } fmt.Println(sm) } map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK] [map[] map[] map[] map[] map[]]
若是想把slice的值修改掉,怎麼辦呢?
package main import ( "fmt" ) func main() { sm := make([]map[int]string,5)//定義一個以map爲原數類型的slice,定義map方法:m :=map[int]string,定義slice方法:slice := make([]string,5) for i := range sm { //對slice:sm進程迭代操做,i=0,1,2,3,4 sm[i] = make(map[int]string) //對slice中的map初始化 sm[i][1] ="OK" //這裏對第i個切片sm[i]賦值key=1,value="OK",會影響slice自己的值,因此v=map[1:OK],而sm是:[map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK]] fmt.Println(sm[i]) } fmt.Println(sm) } map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK] [map[1:OK] map[1:OK] map[1:OK] map[1:OK] map[1:OK]]
map是無序的,不能直接排序,可是咱們能夠對其key進程間接排序,須要藉助slice,實現根據key有序的取出map中的值,實現map的簡介排序
package main import ( "fmt" ) func main() { m := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} //定義map,map沒有索引 s := make([]int,len(m))//定義slice,slice以索引爲固定的key i :=0 for k,_ := range m{ s[i] = k //把map中全部的key存在slice中,可是無序的 i++ } fmt.Println(s) } [5 1 2 3 4] package main import ( "fmt" "sort" ) func main() { m := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} //定義map,map沒有索引 s := make([]int,len(m))//定義slice,slice以索引爲固定的key i :=0 for k,_ := range m{ s[i] = k //把map中全部的key存在slice中 i++ } sort.Ints(s)//使用sort進程排序,把map中的key排序 fmt.Println(s) } [1 2 3 4 5] package main import ( "fmt" "sort" ) func main() { m := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} //定義map,map沒有索引 fmt.Println(len(m)) s := make([]int,len(m))//定義slice,slice以索引爲固定的key i :=0 for k,_ := range m{ s[i] = k //把map中全部的key存在slice中 i++ } sort.Ints(s) fmt.Println(s) for j := range s{ fmt.Println(m[j+1]) } } 5 [1 2 3 4 5] a b c d e
package main import ( "fmt" ) func main() { m1 := map[int]string{1:"a",2:"b",3:"c",4:"d",5:"e"} fmt.Println(m1) m2 := make(map[string]int) for k,v := range m1{ m2[v] =k } fmt.Println(m2) } map[3:c 4:d 5:e 1:a 2:b] map[e:5 a:1 b:2 c:3 d:4]
程序正確運行後應輸出以下結果:
Go 函數 不支持 嵌套、重載和默認參數
但支持如下特性:
無需聲明原型、不定長度變參、多返回值、命名返回值參數
匿名函數、閉包
定義函數使用關鍵字 func,且左大括號不能另起一行
函數也能夠做爲一種類型使用
Go 語言函數定義格式以下:
func function_name( [parameter list] ) [return_types] { 函數體 }
函數定義解析:
func:函數由 func 開始聲明
function_name:函數名稱,函數名和參數列表一塊兒構成了函數簽名。
parameter list]:參數列表,參數就像一個佔位符,當函數被調用時,你能夠將值傳遞給參數,這個值被稱爲實際參數。參數列表指定的是參數類型、順序、及參數個數。參數是可選的,也就是說函數也能夠不包含參數。
return_types:返回類型,函數返回一列值。return_types 是該列值的數據類型。有些功能不須要返回值,這種狀況下 return_types 不是必須的。
函數體:函數定義的代碼集合。
定義參數列表是一個int輸入,另外一個string輸入,無返回值 fun A(a int,b string){ } 定義參數列表是一個int輸入,另外一個string輸入,只有一個int類型返回值 fun A(a int,b string)int{ } 定義參數列表是一個int輸入,另外一個string輸入,返回類型是一個int+一個string+一個int fun A(a int,b string)(int,string,int){ } 定義參數列表是3個int輸入,3個int型輸出 fun A(a int,b int, c int)(a int,b int,c int){ } 能夠簡寫爲: fun A(a,b, c int)(a,b,c int){ }
命令返回值參數和不命名返回值參數有什麼區別呢?
若是你要返回多個返回值的話,並且使用(a,b,c int)簡寫形式的話,你就必須命名返回值,否則它就不知道了
func A() (a,b,c int){ a,b,c = 1,2,3 //這裏不是:=,由於已經在內存中給a,b,c分配過內存地址了 return a,b,c //這裏也能夠直接寫return,由於已經定義了返回值變量和類型(a,b,c int),代碼可讀性要求返回值return後加上變量a,b,c }
若是這樣定義返回值的話 (int,int,int),就能夠不用命名返回值(不定義返回值是a,b,c)
func A() (int,int,int){ a,b,c := 1,2,3 //這裏必須是:=,由於是首次初始化變量 return a,b,c //這裏不能夠只寫return }
若是A是一串int型的數字,n個,我要計算A長得最大值,怎麼寫呢?
使用go中的不定長變參
package main import "fmt" func main() { A(1,2,3,4,5,6,7) } func A(a ...int){ //...就是不定長變參,A就是一個slice,能夠打印出來 fmt.Println(a) } 輸出:[1 2 3 4 5 6 7] package main import "fmt" func main() { A("a",1,2,3,4,5,6,7) } func A(b string,a ...int){ //若是A使用了不定長變參"...",不能夠在...後邊定義變量b,能夠在a以前定義變量b fmt.Println(b,a) } 輸出:a [1 2 3 4 5 6 7]
slice的值拷貝和直接slice地址拷貝有什麼區別呢?
package main import "fmt" func main() { a :=1 A(a) fmt.Println(a) } func A(a int){ //若是A使用了不定長變參"..."定義slice A a=2 //值拷貝不會修改面main函數中a的值,能夠理解爲:局部變量修改不會影響全局變量的值 fmt.Println(a) } 2 1 package main import "fmt" func main() { a,b :=1,2 A(a,b) fmt.Println(a,b) } func A(s ...int){ //若是A使用了不定長變參"..."定義slice s s[0]=3//a=3,值拷貝不會影響main函數的內部a的值 s[1]=4//b=4,值拷貝不會影響main函數的內部b的值 fmt.Println(s) } [3 4] 1 2 package main import "fmt" func main() { s1 := []int{1,2,3,4} A(s1) fmt.Println(s1) } func A(s []int){ //若是A使用了不定長變參"..."定義slice A s[0]=5//在A()函數的內部修改影響到了main函數中的s1的值,這裏拿到了slice的地址,拷貝修改了slice內存地址中的值,實際上就是對slice自己進行操做 s[1]=6 s[2]=7 s[3]=8 fmt.Println(s) } [5 6 7 8] [5 6 7 8]
若是我想對這種int,string,也進行內存地址值得拷貝覆蓋操做,怎麼作?
採用指針地址值傳遞,先取出地址,再賦值
package main import "fmt" func main() { a :=1 A(&a)//調用A()函數,因爲A()要求輸出是指針類型(一個地址值0xxxx),全部輸入&a符合輸入要求&a=0xxxx fmt.Println(a)//這裏也打印2,說明內存中的*a的值已經被修改 } func A(a *int){ //定義了指針類型的a,a可能的值是a=0xxxxx *a=2 //把內存地址爲0xxxx的變量*a從新賦值爲2 fmt.Println(*a)//打印*a的變量值, } 2 2
函數也是一種數據類型,給個例子
package main import "fmt" func main() { a :=A //這裏a就是A的複製品 a() } func A(){ fmt.Println("Func A") } Func A
什麼是匿名函數呢?
package main import "fmt" func main() { a := func() { //定義a是匿名函數,能夠調用而且打印Func A fmt.Println("Func A") } a() } Func A
那麼什麼是閉包?
package main import "fmt" func main() { f := closure(10) //調用閉包函數closure(),返回一個匿名函數給f,賦值x=10 /* 此時f就是匿名函數: func(y int)int { fmt.Printf("%p\n",&x) return x + y } */ fmt.Println(f(1)) //第一次調用func(),x=10,y=1,return 11 fmt.Println(f(2)) //第二次調用func(),x=10,y=2,return 12 } func closure(x int) func(int) int { //閉包函數的做用是返回一個匿名函數 fmt.Printf("%p\n",&x) //第一次調用閉包函數打印x變量地址0xc420012070 return func(y int)int { fmt.Printf("%p\n",&x) //第2、三次調用閉包函數打印x變量地址0xc420012070,三次相同, return x + y } } 0xc420012070 0xc420012070 11 0xc420012070 12
實例
如下實例爲 max() 函數的代碼,該函數傳入兩個整型參數 num1 和 num2,並返回這兩個參數的最大值:
//函數返回兩個數的最大值 func max(num1, num2 int) int { // 聲明局部變量 var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }
函數調用
當建立函數時,你定義了函數須要作什麼,經過調用改函數來執行指定任務。
調用函數,向函數傳遞參數,並返回值,例如:
package main import "fmt" func main() { /* 定義局部變量 */ var a int = 100 var b int = 200 var ret int /* 調用函數並返回最大值 */ ret = max(a, b) fmt.Printf( "最大值是 : %d\n", ret ) } /* 函數返回兩個數的最大值 */ func max(num1, num2 int) int { /* 定義局部變量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result } 以上實例在 main() 函數中調用 max()函數,執行結果爲: 最大值是 : 200
函數返回多個值
Go 函數能夠返回多個值,例如:
package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("Mahesh", "Kumar") fmt.Println(a, b) } 以上實例執行結果爲: Kumar Mahesh
傳遞類型 | 描述 |
---|---|
值傳遞 | 值傳遞是指在調用函數時將實際參數複製一份傳遞到函數中,這樣在函數中若是對參數進行修改,將不會影響到實際參數。 |
引用傳遞 | 引用傳遞是指在調用函數時將實際參數的地址傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。 |
默認狀況下,Go 語言使用的是值傳遞,即在調用過程當中不會影響到實際參數。
函數用法 函數用法 |
描述 |
---|---|
函數做爲值 | 函數定義後可做爲值來使用 |
閉包 | 閉包是匿名函數,可在動態編程中使用 |
方法 | 方法就是一個包含了接受者的函數 |
執行方式相似其它語言中的析構函數,在函數體執行結束後
按照調用順序的相反順序逐個執行(先進後出,後進先出)
即便函數發生嚴重錯誤也會執行
支持匿名函數的調用
經常使用於資源清理、文件關閉、解鎖以及記錄時間等操做
經過與匿名函數配合可在return以後修改函數計算結果
若是函數體內某個變量做爲defer時匿名函數的參數,則在定義defer時即已經得到了拷貝,不然則是引用某個變量的地址
Go 沒有異常機制,但有 panic/recover 模式來處理錯誤
Panic 能夠在任何地方引起,但recover只有在defer調用的函數中有效
package main import "fmt" func main() { fmt.Println("a") defer fmt.Println("b") defer fmt.Println("c")//先調用打印c,在調用打印b } a c b package main import "fmt" func main() { for i :=0;i < 3 ;i ++{ defer fmt.Println(i) } } 2 //打印結果是2,1,0,而不是0,1,2 1 0 package main import "fmt" func main() { for i :=0;i < 3 ;i ++{//這裏循環結束的時候i=3,閉包中的匿名函數調用的i=3,因此三次打印出來的值都是3 defer func(){ fmt.Println(i) //引用局部變量i,在退出for循環體的時候,i=3,在main函數return的時候,開始執行defer語句,i=3,因此所有打印3 }() //這個括號的意思是調用這個函數,能夠理解爲defer a() } } 3 3 3 package main import "fmt" func main() { A() B() C() } func A() { fmt.Println("Func A") } func B() { defer func(){//定義好defer遇到panic後Recover,必須在出現panic以前就定義好defer處理函數 if err := recover();err !=nil{ fmt.Println("Recover in B") } }() panic("Panic in B ")//定義panic } func C(){ fmt.Println("Func C") } Func A Recover in B Func C
package main import "fmt" func main() { var fs = [4]func(){}//定義fs是func類型的slice for i :=0;i<4;i++{ defer fmt.Println("defer i = ",i)//i做爲一個參數,值拷貝傳遞,正常輸出0,1,2,3,可是使用了defer定義,因此出書3,2,1,0 defer func(){ fmt.Println("defer_closure i =",i)//閉包匿名函數,外層循環結束,i=4,因此打印defer_closure i = 4 }() fs[i] = func() { fmt.Println("closure i =",i)//先將這些匿名函數存在func類型的slice中,這裏i來自於外層的for循環,外層for循環結束以後引用地址內容值i=4,因此輸出closure i = 4 } } for _,f := range fs{//調用fs,打印closure i = 4 f() } } closure i = 4 closure i = 4 closure i = 4 closure i = 4 defer_closure i = 4 defer i = 3 defer_closure i = 4 defer i = 2 defer_closure i = 4 defer i = 1 defer_closure i = 4 defer i = 0