Go基础
变量
基本结构:var 变量名 变量类型 = 值
注:_
(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package main var a int var b, c int var d int = 1 var e, f int = 1, 2 var g = 1 var h, i = 1, "string" func main() { j := 1; k, l := 1, 2 }
|
常量
常量可定义为数值、布尔值或字符串等类型。
1 2 3 4
| const a int = 1 const b = 1 const c, d = 1, 2 "string"
|
内置基本类型
Boolean
布尔值的类型为bool,值是true或false,默认为false。
注:不能用0和非0表示true或false
数值类型
1 2 3 4 5 6 7
| 1. 整型 * 分为无符号和带符号,例如:int和uint * 8,16,32,64位,例如:int32和uint32 * rune是int32的别称,byte是uint8的别称 2. 浮点型 float32和float64 3. 复数 complex64和complex128 注:不同类型之间不能进行运算
|
字符串
定义
1 2 3 4 5
| var a string var b string = "" func test() { no, yes, maybe := "no", "yes" }
|
修改
1 2 3 4 5
| s := "hello" c := []byte(s) c[0] = 'c' s2 := string(c) fmt.Printf("%s\n", s2)
|
连接
1 2 3 4
| s := "hello," m := " world" a := s + m fmt.Printf("%s\n", a)
|
原始格式输出
` 括起的字符串为Raw字符串,即字符串在代码中的形式就是打印时的形式,它没有字符转义,换行也将原样输出。
错误类型
Go内置有一个error类型,专门用来处理错误信息,Go的package里面还专门有一个包errors来处理错误:
1 2 3 4
| err := errors.New("emit macho dwarf: elf header corrupted") if err != nil { fmt.Print(err) }
|
Go数据底层的存储
下面这张图来源于Russ Cox Blog中一篇介绍Go数据结构的文章,大家可以看到这些基础类型底层都是分配了一块内存,然后存储了相应的值。
一些技巧
分组声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import( "fmt" "os" ) const( i = 100 pi = 3.1415 prefix = "Go_" ) var( i int pi float32 prefix string )
|
iota枚举
Go里面有一个关键字iota,这个关键字用来声明enum的时候采用,它默认开始值是0,每调用一次加1:
1 2 3 4 5 6 7 8 9 10 11 12
| const( x = iota y = iota z = iota w ) const v = iota const ( e, f, g = iota, iota, iota )
|
除非被显式设置为其它值或iota,每个const分组的第一个常量被默认设置为它的0值,第二及后续的常量被默认设置为它前面那个常量的值,如果前面那个常量的值是iota,则它也被设置为iota。
私有和公有
大写字母开头的变量或函数,为公有,相当于java中的public
小写字母开头的变量或函数为,私有,相当于java中的private。
array、slice、map
array
基本结构:var 变量名 [长度]类型
- 数组长度不能改变
- 长度也是数组类型的一部分,因此3int与[4]int是不同的类型
- 当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。
1 2 3 4
| var arr [10]int a := [3]int{1, 2, 3} b := [10]int{1, 2, 3} c := [...]int{4, 5, 6}
|
slice
声明
基本结构:var 变量名 []类型
slice是一个引用类型。slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度。
1 2 3 4 5 6 7
| var slice []int var slice = []int{1, 2, 3} slice := []byte {'a', 'b', 'c'} array := [3]byte {'a', 'b', 'c'} slice := array[1, 2] // slice通过array[i:j]来获取,其中i是数组的开始位置,j是结束位置,但不包含array[j],它的长度是j-i。
|
内置函数
- len 获取slice的长度
- cap 获取slice的最大容量
- append 向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice
- copy 函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数
长度与容量
1 2 3 4 5 6 7 8 9
| a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0} s := a[0:] s = append(s, 11, 22, 33) sa := a[2:7] sb := sa[3:5] fmt.Println(a, len(a), cap(a)) fmt.Println(s, len(s), cap(s)) fmt.Println(sa, len(sa), cap(sa)) fmt.Println(sb, len(sb), cap(sb))
|
- 长度为已存放个数,容量为可存放个数
- 对数组来说,长度和容量总是相等的
- slice的容量可以大于长度,如果容量不足,将动态分配新的数组空间
- array[i:j:k],k - i为容量,k默认为数组长度
陷阱
当Slice的容量还有空闲的时候,append进来的元素会直接使用空闲的容量空间,但是一旦append进来的元素个数超过了原来指定容量值的时候,内存管理器就是重新开辟一个更大的内存空间,用于存储多出来的元素,并且会将原来的元素复制一份,放到这块新开辟的内存空间。
1 2 3 4 5
| a := []int{1, 2, 3, 4} sa := a[1:3] fmt.Printf("%p\n", sa) sa = append(sa, 11, 22, 33) fmt.Printf("%p\n", sa)
|
参考链接
map
声明
基本结构:map[keyType]valueType
1 2 3 4 5 6
| m1 := make(map[string]string) m1["aa"] = "bb" m2 := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 } m2["C++"] = 5 delete(m2, "C") len(m2)
|
特点
- map是无序的,每次打印出来的map都会不一样,它不能通过index获取,而必须通过key获取
- map的长度是不固定的,也就是和slice一样,也是一种引用类型
- map和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制
- map[key],有两个返回值,第一个是value,第二个是是否存在对应的值
make、new操作
make用于内建类型(map、slice 和channel)的内存分配。new用于各种类型的内存分配。
- new返回的是指针
- make返回初始化后的(非零)值
零值
关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为0。 此处罗列 部分类型 的 “零值”
1 2 3 4 5 6 7 8 9 10 11
| int 0 int8 0 int32 0 int64 0 uint 0x0 rune 0 byte 0x0 float32 0 float64 0 bool false string ""
|
流程与函数
流程控制
Go中流程控制分三大类:条件判断,循环控制和无条件跳转
if
Go的if有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示
1 2 3 4 5 6 7 8 9
| if x := computedValue(); x > 10 { fmt.Println("x is greater than 10") } else { fmt.Println("x is less than 10") } fmt.Println(x)
|
goto
Go有goto语句——请明智地使用它。用goto跳转到必须在当前函数内定义的标签(大小写敏感)。例如假设这样一个循环:
1 2 3 4 5 6 7
| func myFunc() { i := 0 Here: println(i) i++ goto Here }
|
for
Go里面最强大的一个控制逻辑就是for,它即可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作。它的语法如下:
1 2 3 4 5 6 7 8 9 10 11
| for expression1; expression2; expression3 { } for index:= 0; index < 10 ; index++ { } for a, b:= 0, 0; b < 10 ; b++ { }
|
while语句
1 2 3 4 5 6 7 8
| for ; sum < 1000; { sum += sum } for sum < 1000 { sum += sum }
|
break和continue
1 2 3 4 5 6 7 8 9
| for index := 10; index>0; index-- { if index == 5{ break } fmt.Println(index) }
|
range
1 2 3 4 5 6 7
| for k,v:=range map { fmt.Println("map's key:",k) fmt.Println("map's val:",v) }
|
switch
与java不同的是,默认每个case
执行完毕后会跳出switch语句
,如果希望继续执行下一个case
,需要加入fallthrough
关键字
1 2 3 4 5 6 7 8 9 10 11 12
| i := 10 switch i { case 1: fmt.Println("i is equal to 1") case 2, 3, 4: fmt.Println("i is equal to 2, 3 or 4") case 10: fmt.Println("i is equal to 10") fallthrough default: fmt.Println("------------------------") }
|
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| func funcName(input1 type1, input2 type2) (output1 type1, output2 type2) { return output1, output2 } func funcName(input1 type1, input2 type2) (type1, type2) { return "", "" } func funcName(input1 type1, input2 type2) type { } func funcName(input1 type1, input2 type2) { } func funcName(input1, input2 int, input3 string, input4, input5 float32) (output1, output2 int){ } func SumAndProduct(A, B int) (add int, Multiplied int) { add = A+B Multiplied = A*B return }
|
变参
基本结构:func myfunc(arg ...int) {}
- 参数的类型全部是int
- 变量arg是一个int的slice
1 2 3
| for _, n := range arg { fmt.Printf("And the number is: %d\n", n) }
|
传值与传指针
- 函数的参数,传入的都是copy
- 即使传入的是指针,也是指针的copy
指针的优点
- 传指针使得多个函数能操作同一个对象。
- 传指针比较轻量级 (8bytes),只是传内存地址,我们可以用指针传递体积大的结构体。如果用参数值传递的话, 在每次copy上面就会花费相对较多的系统开销(内存和时间)。所以当你要传递大的结构体的时候,用指针是一个明智的选择。
- Go语言中string,slice,map这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice的长度,则仍需要取地址传递指针)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main import "fmt" func add1(a *int) int { *a = *a+1 return *a } func main() { x := 3 fmt.Println("x = ", x) x1 := add1(&x) fmt.Println("x+1 = ", x1) fmt.Println("x = ", x) }
|
defer
1 2 3 4 5 6 7 8 9 10 11
| func ReadWrite() bool { file.Open("file") defer file.Close() if failureX { return false } if failureY { return false } return true }
|
- defer是采用后进先出模式,所以如下代码会输出4 3 2 1 0
1 2 3
| for i := 0; i < 5; i++ { defer fmt.Printf("%d ", i) }
|
函数作为值、类型
基本结构:type typeName func(input1 inputType1 , input2 inputType2) (result1 resultType1)
- 拥有相同参数列表和返回值列表的函数,属于同一函数类型。比如下面例子中的isOdd和isEven函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| package main import "fmt" type testInt func(int) bool // 声明了一个函数类型 func isOdd(integer int) bool { if integer%2 == 0 { return false } return true } func isEven(integer int) bool { if integer%2 == 0 { return true } return false } func filter(slice []int, f testInt) []int { var result []int for _, value := range slice { if f(value) { result = append(result, value) } } return result } func main(){ slice := []int {1, 2, 3, 4, 5, 7} fmt.Println("slice = ", slice) odd := filter(slice, isOdd) fmt.Println("Odd elements of slice are: ", odd) even := filter(slice, isEven) fmt.Println("Even elements of slice are: ", even) }
|