参考书:Go语言编程
1 变量声明使用关键字var ,如下
var v1 int
var v2 string
var v3 [10]int //数组
var v4 []int //数组切片
var v5 struct {
f int
}
var v6 *int //指针
var v7 map[string]int // map类型,key为string类型,value为int类型
var v8 func(a int) int
2 var可以将若干个同样需要声明的同种类型的变量放在一起,如下
var (
v1 int
v2 string
)
3 变量初始化
var v1 int = 10
var v2 = 10 // 编译器根据赋值自动定义变量类型
v2 := 10
4 用“:=”初始化变量时,左侧的变量必须是未声明过的变量,否则会导致编译错误
5 Go语言支持多重赋值,如下
i , j = j , i
6 Go语言支持匿名变量为了避免不会因为返回多个值的函数来定义一堆不需要的变量,如下
func GetName( ) ( firstName , lastName , nickName string) {
return "May" , "Chan" , "Chibi"
}
_ , _ , nickName := GetName( )
7 常量定义
const Pi float64 = 3.1415926535
const zero = 0.0
const (
size int64 = 1024
eof = -1
)
const u , v float32 = 0 , 3
const a , b , c = 3, 5.0 , "foo" // a = 3 , b = 5.0 , c = "foo"
8 常量定义的右值也可以是一个在编译期运算的常量表达式,如下
const mask = 1 << 3 // ok
const Home = os.GetEnv("HOME") //error , 因为os.GetEnv()只有在运行期才能知道返回结果,在编译期并不能确定
9 Go语言预定义了这些常量:true ,false和iota
10 在每一个const关键字出现时被重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1,如下
const ( // iota重置为0
c0 = iota // c0 = 1
c1 = iota // c1 = 2
c2 = iota // c2 = 3
)
const x = iota // x = 0
11 Go语言不支持enum关键字,Go常用const来实现枚举,如下
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
12 Go的基础类型,布尔类型(bool) ,整型(int8 , byte , int16 , int , uint , uintptr等) , 浮点类型(float32 , float64),复数类型(complex64 , complex128),字符串(string),字符类型(rune),错误类型(error),指针(pointer),数组(array),切片(slice),字典(map),通道(chan),结构体(struct),接口(interface)
13 布尔类型不能接受其他类型的赋值,也不支持自动或强制类型转换,如下
var b bool
b = true
c := (1 == 2)
b = 1 // 编译错误
b = bool(1) // 编译错误
14 整型
- int8 一个字节 -128 ~ 127
- uint8 一个字节 0 ~ 255
- int16 二个字节 -32768 ~ 32767
- uint16 二个字节 0 ~ 65535
- int32 四个字节 -2147483648 ~ 2147483647
- uint32 四个字节 0 ~ 4294967295
- int64 八个字节
- uint64
- int
- uint
- uintptr
注:int和int32在Go语言里是被认为两种不同的类型,编译器是不会帮助做类型转换的
var value1 int32 value2 := 64 // value2被自动推导为int类型 value1 = value2 // cannot use value2 (type int) as type int32 in assignmentvalue1 = int32(value2) // ok
15 两个不同类型的整型数不能直接比较,比如int8类型的数和int类型的数不能直接比较
16 Go语言定义了两个类型float32和float64,float32相当于C语言的float,float64相当于C语言的double
fvalue := 12.0 // 默认推导为float64
17 因为浮点数不是精确的表达方式,直接用==来判断两个浮点数是否相等会有风险
18 复数
var value1 complex64
value1 = 3.2+12i
value2 := 1 + 3i
value3 := complex(3.2 , 12) // value3 := 3.2 + 12i
19 字符串
var str string
str = "Hello world"
ch := str[0]
20 字符串操作
x + y 字符串拼接 "Hello" + "123" // "Hello123"
len(s) 字符串长度 len("hello") // 5
s[i] 取字符 "Hello"[0] // 'H'
21 字符串遍历,Go语言支持两种方式遍历字符串,如下
- 字节数组的方式遍历
str := "Hello,world"
n := len(str)
for i := 0 ; i < n ; i++ {
ch := str[i] // ch的类型为byte
fmt.Println(i , ch)
}
- 以Unicode字符遍历
str := "Hello,world"
for i , ch := range str {
fmt.Println(i , ch) // ch的类型为rune
}
注:Go语言支持两种字符类型,一个是byte,代表UTF-8字符串的单个字节的值,另一个是rune,代表单个Unicode字符。
22 数组的声明方式
- [32]byte
- [2*N] struct { x , y int32 } // 复杂类型数组
- [1000] *float64
- [3][5] int
- [2][2][2] float64
数组长度在定义后就不可更改,在声明时长度可以是常数也可以是变量。长度可以通过len( )函数获取
23 访问数组除了下标访问,可以使用关键字range,range会返回2个值,元素的数组下标和元素的值
for i , v := range array {
fmt.Println("Array element[" , i , "] = " , v)
}
24 在Go语言中数组是一个值类型,不是引用类型,在赋值或参数传递时都会生成一个副本,修改不会改变原始数组。
func main(){ array := [5]int{1,2,3,4,5} array2 := array array2[2] = 4 modify(array) fmt.Println("In main(), array values: ",array) fmt.Println("In main(), array2 values: ",array2) } func modify(array [5]int){ array[0] = 10 fmt.Println("In modify(), array values: ",array) }
输出结果In modify(), array values: [10 2 3 4 5]
In main(), array values: [1 2 3 4 5]
In main(), array2 values: [1 2 4 4 5]
25 创建数组切片的方法主要有两种 --- 基于数组和直接创建
- 基于数组创建
var myArray [10]int = [10]int {1,2,3,4,5,6,7,8,9,10}
var mySlice []int = myArray[:5]
- 使用内置函数make( )创建数组切片
mySlice1 := make([]int , 5) // 创建元素个数为5,元素值都为0的数组切片
mySlice2 := make([]int , 5 , 10) // 创建元素个数为5,元素值都为0,实际预留10个元素的存储空间。len(mySlice2)是5
mySlice3 := []int { 1 , 2 , 3 , 4 , 5 }
26 数组切片较数组多了一个存储能力
mySlice := make([]int , 5 , 10)
fmt.Println("len(mySlice): " , len(mySlice)) // 5,即元素个数
fmt.Println("cap(mySlice): " , cap(mySlice)) // 10 即数组切片分配的空间大小
27 数组切片新增元素可以使用append( ),如下
- mySlice = append(mySlice , 1 , 2 , 3) // 在切片尾新增3个元素1,2,3
- mySlice2 := []int { 8 , 7 , 9 }
mySlice = append(mySlice , mySlice2 . . . ) // 后面必须有省略号,表示将mySlice2的所有元素都传入
28 基于数组切片创建数组切片
oldSlice := []int { 1 , 2 , 3 , 4 , 5 }
newSlice := oldSlice[ : 3]
29 数组切片内容复制,如下
slice1 := []int { 1 , 2 , 3 , 4 , 5 }
slice2 := []int { 5 , 4 , 3 }
copy(slice2 , slice1) // 只会复制slice1的前3个元素到slice2中
copy(slice1 , slice2) // 只会复制slice2的3个元素到slice1的前3个位置
30 map
myMap := make(map[string] int) // 创建map
myMap := make(map[string] int , 100) // 创建一个初始存储能力为100的map
myMap := map[string] int { // 创建并初始化
"abc":123 ,
"bcd":333 ,
}
delete( myMap , "abc") // 删除某个key值的元素
31 map查找
value , ok := myMap["abc"] //查找会返回两个值,ok表示是否有这个元素
if ok {
// toDo
}
32 Go语言支持如下几种流程控制语句,还有break,continue和fallthrough
- 条件语句,关键字为if ,else 和else if
- 选择语句,关键字为switch,case和select
- 循环语句,关键字为for和range
- 跳转语句,关键字为goto
33 if语句,如下
if a < 5 { // 条件语句不用加小括号(),大括号{ }一定要有
return 0
} else {
return 1
}
34 switch语句,如下
i := 1 switch i { // 左大括号{一定要和switch同一行 case 0: fmt.Println(0) // 不需要用break来声明退出case case 1: fmt.Println(1) case 2: fallthrough // fallthrough表示继续执行紧跟的下一个case case 3: fmt.Println(3) case 4, 5, 6: // 单个case中可以出现多个结果选项 fmt.Println("4,5,6") default: fmt.Println("default") } switch{ // 可以不设定switch之后的条件表达式,只写一个switch case 0<=i && i < 1: // 条件表达式可以不只限制为常量或整数 fmt.Println(0) case 1<=i && i < 2: fmt.Println(1) }
35 Go语言循环语句只支持for,不支持while和do-while
for i := 0 ; i < 10 ; i ++ { // 条件语句不用加小括号( )
fmt.Println(i)
}
sum := 0
for {
sum ++
if sum > 100 {
break
}
}
a := []int{1,2,3,4,5,6,7}
for i , j := 0 , len(a) - 1 ; i < j ; i , j = i+1 , j-1 {
a[i] , a[j] = a[j] , a[i]
}
36 Go语言的for循环支持continue和break来控制循环
37 跳转语句
func myfunc( ) {
i := 0
HERE :
fmt.Println(i)
i++
if i < 10 {
goto HERE
}
}
38 函数定义
func Add(a int , b int) (ret int , err error)
func Add(a , b int) int
39 函数调用,先导入该函数所在的包,调用即可,如下
import "mymath" // 假设Add被放在一个叫mymath的包中
c := mymath.Add(1 , 2)
40 Go语言有这样的规则,小写字母开头的函数只能在本包内可见,即为private,大写字母开头的函数才能被其他包使用,即public
41 不定参数
func myfunc( args . . . int ) { // 该函数可以接受不定数量的参数,并且这些参数类型都是int
for _ , arg := range args {
fmt.Println(arg)
}
}
myfunc(2 , 3) // ok
myfunc(1, 2, 3) // ok
42 不定参数的传递
func myfunc( args . . . int) {
myfunc3(args . . . ) // 不定参数的传递必须在后面加上省略号
myfunc3(args[1 : ] . . . ) // 不定参数的传递必须在后面加上省略号
}
43 任意类型的不定参数,使用interface{ }
func Printf(format string , args . . . interface{ })
44 函数的一个实例
func MyPrintf(args ...interface{}){ for _,arg:= range args{ switch arg.(type){ case int: fmt.Println(arg," is an int value") case string: fmt.Println(arg, " is a string value") case int64: fmt.Println(arg, " is an int64 value") default: fmt.Println(arg, " is an unknown type") } } } func main(){ var v1 int = 1 var v2 int64 = 234 var v3 string = "hello" var v4 float32 = 1.234 MyPrintf(v1,v2,v3,v4) }
输出结果:
1 is an int value
234 is an int64 value
hello is a string value
1.234 is an unknown type
45 匿名函数由一个不带函数名的函数声明和函数体组成,可以直接赋值给一个变量或直接执行
f := func (a , b int , z float64 ) bool { // 没有函数名,直接给变量f赋值
return a * b < int(z)
}
func ( ch chan int ) {
ch <- ACK
} (reply_chan) // 函数后带括号即直接执行,将reply_chan值传入函数中
47 error接口,定义如下
type error interface {
Error() string
}
48 一个函数可以有多个defer语句。defer语句调用顺序为先进后出,最后一个defer语句先执行
49 当函数执行过程中调用panic( ),正常的函数执行流程将立即终止。recover( )用于终止错误处理流程