Go基础
变量
Go变量使用方式和C语言接近,但具备更大的灵活性
变量声明
对于纯粹的变量声明,Go引入了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
为了避免重复写var,可以将若干个需要声明的变量放在一起,如下:
var (
v1 int
v2 string
)
变量初始化
初始化例子如下:
var v1 int = 10 // 用法1
var v2 = 10 // 用法2 编译器可以自动推导出v2的类型
v3 := 10 // 用法3 编译器可以自动推导出v2的类型
注意:
第三种方法不能用于声明全局变量
即使第三种初始化方法很像动态语言,比如Python,但是Go是强类型语言(静态语言)
变量赋值
Go支持多重复值,所以可以实现直接交换两个数的值操作
i, j = j, i
匿名变量
在一般强类型语言中,如果函数返回多个值,那么需要定义多个多余的变量,在Go中,可以使用匿名变量代替你不需要的变量
例如,GetName()返回三个值,但是你只需要最后一个值
-, -, nickName = GetName()
常量
常量指不可以更改的值
字面常量
程序中硬编码的常量,如:
-12
3.2132131231232 // 浮点型常量
3.2+12i // 复数型常量
true // 布尔型常量
"foo" // 字符串常量
常量定义
与C语言一样,使用const关键字定义常量
const Pi float64 = 3.14159263123213213
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整形常量
)
const u, v float32 = 0, 3 // 常量的多重赋值
Go可以定义常量的类型,也可以定义无类型常量.
常量的右值可以是一个常量表达式,因为常量的赋值是编译期行为,所以右值不可以是一个运行期表达式
预定义常量
Go预定义了这些常量: true、false和iota
true和false比较常见 用法也是类似的
这里详细说一下iota:
iota比较特殊,可以被认为是一个可被编辑器修改的常量,在每个const出现后iota重置为0,在下一个const出现之前,没出现一次iota,其代表的数字加1,下面举个例子:
const (
c0 = iota // c0 = 0
c1 = iota // c1 = 1
c2 = iota // c2 = 2
)
const (
c0 = iota*2 // c0 = 0
c1 = iota*2 // c1 = 2
c2 = iota*2 // c2 = 4
)
如果下面的赋值表达式相同,那么可以省略,比如上面第一个表达式可以写成:
const (
c0 = iota // c0 = 0
c1 // c1 = 1
c2 b// c2 = 2
)
枚举
枚举指一系列相关的常量,比如下面关于一个星期中每天的定义:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 这个常量没有导出
)
以大写字母开头的常量在包外可见,以上例子中numberOfDays为包内私有,其他符号可以被其它包访问
类型
Go包含以下基础类型:
- 布尔类型: bool
- 整形: int8, byte, int16, int, uint, uintptr等
- 浮点类型: float32, float64.
- 复数类型: complex64, complex128
- 字符串: string
- 字符类型: rune
- 错误类型: error
此外,还包含以下这些复杂类型: - 指针 ( pointer )
- 数组 ( array )
- 切片 ( slice )
- 字典 ( map )
- 通道 ( chan )
- 结构体 ( struct )
- 接口 ( interface )
布尔类型
关键字bool,可赋值为预定义的true和false
布尔类型不接受其他类型的赋值,不支持自动或强制的类型转换
整形
整形是所有语言中最基础的类型
包括有符号的整形 int8 int16 int32 int64以及对应的无符号整形数 uint8 uint16 uint32 uint64 这里面的数字 8,16,32,64对应的是计算机位数 。
这里对应有特定平台的有符号int型与无符号uint型 这两种类型是根据CPU的处理位数来确定对应大小的
uintptr 没有指定大小,但是可以容纳指针。
类型表示
int和int32在Go语言中是两种不同的类型,如果要进行赋值,必须进行强制转换
强制转换时需要注意
- 数据长度被截短导致的数据进度损失问题
- 值溢出(超过转换的目标类型的值范围)
数值运算
Go语言支持常规的整数运算:加减乘除和取余
比较运算
Go支持 >, <, ==, >=, <= 和 !=.
注意
两个不同类型的整数不能直接比较,但是各种类型的整形变量都可以直接和字面常量比较
位运算
运算 | 含义 |
---|---|
x << y | 左移 |
x >> y | 右移 |
x ^ y | 异或 |
x & y | 与 |
x | y |
^x | 取反 |
浮点型
浮点型用于表示包含小数点的数据
浮点数表示
float32等价于C中float类型,float64等价于C中double类型
浮点数比较
浮点数不是一种精确的表达方式,所以没有办法直接使用==来判断.
这里推荐一种比较的方法:
import 'math'
// 这样可以自己确认浮点数的比较精度
func IsEqual1(f1, f2, p float64) bool {
return math.Abs(f1-f2) < p
}
复数类型
复数实际上由两个实数构成,一个表示实部,一个表示虚部.
复数表示
复数表示示例如下:
var value1 complex64
value1 = 3.2 + 12i
value2 := 3.2 + 12i
vlaue3 := complex(3.2, 12)
实部和虚部
Go中可以通过real(z)获取复数的实部,imag(z)获取复数的虚部.
字符串
字符串也是基本类型之一.
字符串操作
运算 | 含义 |
---|---|
x + y | 字符串连接 |
len(s) | 字符串长度 |
s[i] | 取字符串 |
更多的字符串操作,可以参考标准库strings包
字符串遍历
字符串遍历有两种方法
第一种通过下标来访问字符串
str := "Hello, everyone"
n := len(str)
for i:= 0; i < n; i++ {
ch := str[i]
fmt.Println(i, ch)
}
第二种类似于python中的遍历
for i, ch := range str {
fmt.Println(i, ch)
}
字符类型
Go中包含两种字符类型,一个是byte,代表UTF-8字符串的单个字节的值;另一个是rune,代表单个Unicode字符
出于简化语言的考虑,Go语言的多数API都假设字符串为UTF-8编码.
数组
数组同样也是Go语言中最常用的数据结构之一.数组就是指一系列同一类型数据的集合.
数组的长度在定义后是无法更改的.
下面是一些常规的数组声明方法:
[32]byte // 长度为32的数组,每个元素为一个字节
[2*N] struct { x, y int32 } // 复杂类型数组
[1000]*float // 指针数组
[3][5]int // 二维数组
元素访问
可以使用数组下标来访问数组,同样也可以通过range函数来遍历数组.
range函数返回两个值,第一个是数组的下标,第二个是元素的值.
值类型
数组是值类型,如果在函数中传入数组,那么在函数中其实是数组的副本,是无法对原数组实行具体操作的.
数组切片
数据切片的数据结构可以抽象为以下3个变量:
- 一个指向原生数组的指针
- 数组切片中的元素个数
- 数组切片已分配的存储空间
创建数组切片
创建数组切片的方法主要有两种 --------- 基于数组和直接创建
基于数组
var Array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
var array []int = Array[:5]
直接创建
Go中make方法可以直接创建数组切片
mySlice := make([]int, 5)
mySlice := make([]int, 5, 10)
动态增减元素
Go中有cap和len函数
cap函数用于计算数组分配的空间大小
len函数用于计算数组实际元素个数
对于动态增加元素,下面示例:
mySlice := make([]int, 5, 10)
mySlice2 := []int{8, 9, 10}
mySlice = append(mySlice, mySlice2...) // 这里...用于把mySlice包含的元素打散
基于数组切片创建数组切片
可以直接通过对已有数组切片后创建数组切片,并且这个切片范围可以大于数组元素个数,但是不能大于数组分配的空间大小,当超出原有数组的元素,自动赋值为0
内容复制
Go语言中的copy函数可以用于数组内容的复制,如果加入的两个数组切片不一样大,那么就会按照其中较小的数组切片元素进行复制.
map
map(字典)是一对键值对的微排序集合.在python中dict数据结构类似.
变量声明
var myMap map[string] PersonInfo
myMap是声明的map变量名,string是键类型,PersonInfo是值类型
创建
通过make函数创建一个新的map.
myMap = make(map[string] PersonInfo, 100)
创建一个新的map,存储能力是100
初始化
myMap = map[string] PersonInfo{
"1234": PersonInfo{"1", "Jack", "Room 101...."},
}
元素赋值
赋值就直接将键值对应即可
元素删除
Go语言提供一个内置函数delete(),用于删除容器内的元素.
delete(myMap, "1234")
元素查找
在python中dict查找直接使用关键字in即可.
在Go中元素查找可以这样写:
value, ok := myMap["1234"]
if ok {
}
流程控制
Go语言支持下面几种流程控制语句:
- 条件语句, 对应关键字if, else 和 else if
- 选择语句, 对应关键字switch, case 和 select
- 循环语句, 对应关键字for和range
- 跳转语句, 对应关键字goto
Go还添加了如下关键字:break, continue, fallthrough
条件语句
条件语句,需要注意以下几点:
- 条件语句不需要使用括号将条件包含起来
- 无论语句体内有几条语句,花括号都是必须存在的
- 在if之后,条件语句之间,可以添加变量初始化语句,使用;间隔
- 在有返回值的函数中,不允许将"最终的"return语句包含在if…else…结构中,否则会编译失败
选择语句
选择语句与C语言大致,但是Go中switch语句后面的表达式甚至不是必须的.
注意:
- 做花括号必须与switch同一行
- 条件表达式不限制为常量或者整数
- 单个case中,可以出现多个结果选项
- 与C语言等规则相反,Go语言不需要用break退出一个case
- 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case
- 可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个if…else…的逻辑作用等同
循环语句
Go中只支持for关键字,不支持while和do-while结构.
Go中for循环同样支持continue和break来控制循环,但是Go提供了一个更高级的break,可以选择中断哪一个循环.
跳转语句
goto语句被多数人反对,但是Go语言仍然支持goto关键字.
函数
函数的基本组成为:关键字func,函数名,参数列表,返回值,函数体和返回语句
函数定义
func 函数名(参数列表) (返回值) {
函数体
返回值
}
其中参数列表和返回值都需要写清楚返回值和类型,如果类型统一,那么只需要写一次即可.
不定参数
形如…type格式的类型只能作为函数的参数类型存在,并且必须是最后一个参数.这其实是一种语法糖,为了方便程序员写代码.
不定参数的传递
func myfunc(args ...int) {
// 按原样传递
myfunc3(args...)
// 传递片段
myfunc3(args[1:]...)
}
任意类型的不定参数
例子:
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type) {
}
}
}
匿名函数与闭包
匿名函数
匿名函数由一个不带函数名的函数声明喝函数体组成,如下所示:
func(a, b int, z float64) bool {
return a*b <int(z)
}
匿名函数可以直接赋值给一个变量或直接执行
f = func(a, b int, z float64) bool {
return a*b <int(z)
}
func(ch chan int){
ch <- ACK
} (reply_chan) // 花括号后面跟参数列表表示函数调用
闭包
基本概念
闭包是可以包含自由(未绑定到特定对象)变量的代码块,这些变量不再这个代码块内或者任何全局上下文中定义,而是在定义代码块的环境中定义.要执行的代码块(由于自由变量包含在代码块中,所以这些自由变量以及他们引用的对象没有被释放)为自由变量提供绑定的计算环境(作用域)
闭包的价值
闭包的价值在于可以作为函数对象或者匿名函数
Go中的闭包
a := func() (func()) {
var i int = 10
return func() {
fmt.Printf("i, j: %d, %d\n", i, j)
}
}()
错误处理
error接口
Go语言引入了一个关于错误处理的标准模式,即error接口,该接口定义如下:
type error interface {
Error() string
}
defer
defer关键字不管在是否出现异常,最后都会执行defer后的表达式
defer后面还可以跟匿名函数 一个代码块中可以有多个defer 但是defer执行的时候遵循先进后出的规则
panic() 和 recover()
当一个函数执行过程中调用panic()函数时,正常的函数执行流程立即终止,并且返回调用函数执行panic()函数,直到所属的goroutine中所有正在执行的函数被终止.
recover()函数用于终止错误处理流程.一般情况下,recover()函数在defer关键字的函数中确保有效地截取错误信息
参考
- 《Go语言编程》——许式伟