您好,登錄后才能下訂單哦!
本內容為自己學習go知識記錄的筆記,方便復習查看
筆記內容參考無聞老師的github:https://github.com/Unknwon/go-fundamental-programming
課程視頻內容:百度網盤(提取碼:mgom)
筆記內容參考:Go編程基礎-課堂講義
Go是一門 并發支持 、垃圾回收 的 編譯型 系統編程語言,旨在創造一門具有在靜態編譯語言的 高性能 和動態語言的 高效開發 之間擁有良好平衡點的一門編程語言。
類型安全 和 內存安全
以非常直觀和極低代價的方案實現 高并發
高效的垃圾回收機制
快速編譯(同時解決C語言中頭文件太多的問題)
為多核計算機×××能提升的方案
UTF-8編碼支持
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(存放項目源碼)
在命令行或終端輸入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個元素,初始值1、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"http://賦值給第二級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) //第二、三次調用閉包函數打印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
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。