Online Go tutorial(1) - 变量,循环,基本类型
主要记录下 6.824 课程中关于 Go 的学习教程 Online Go tutorial 的内容,同时也作为笔者学习 Go 的笔记。Go 安装可以参考官方教程Installing Go。
基本代码规范
这里主要列举下和传统的 C/C++ 规范不一致的地方:
1.语句的末尾不需要使用 ';' 作为语句的结束标记( 编译时会自动添加,添加规则可以参考 Effective Go - Semicolons );
2.对于函数的定义而言,函数体开始的左括号应与对应函数名在同一行,不允许换行( 这一点实际和第一点有一定关系,若进行换行的话,函数名所在行会被自动加入 ';',从而出现错误);
3.在条件语句,循环语句开始位置可以插入一个初始化语句,该初始化语句在条件/循环判断之前执行,并仅可在条件/循环语句内可见;
4.if / for 等语句的条件部分不需要使用 "()" 将条件包围,同时不支持 C/C++ 中使用单条语句从而省略 "{}" 的写法,即执行体必须位于 "{}" 内;
index
1.变量
2.控制语句
3.复合类型
3.1 struct
3.2 array
3.3 slice
3.4 map
使用关键字 var 定义一个或多个变量,使用形如 var x int 的 var + 标识符 + 类型 的方式进行变量定义,多个变量同类型时可以在最后进行统一的类型声明。
var x int //定义整形变量 x var x, y int //定义整形变量 x, y
变量在定义的同时可以进行初始化,当变量定义指定了初始值时,变量的类型被定义为初始值的类型,此时不再需要进行变量类型的声明。
Go 同时提供了变量定义以及赋值的语法糖,即使用 ':=' 符号,该符号仅可在函数体内部使用,无法在全局作用域使用,表示定义一个变量并对其进行赋值,此时的变量定义不再需要使用 var 关键字。
k := 3 //函数体内部定义整形变量 k,初始值为 3
Go 中可使用的基本数据类型如下所示,这里直接参考的 A Tour of Go 的记录。对应的数据类型在未被初始化时,初始值为 0(整形),false(bool型),""(字符串类型)以及 nil(指针/slice/map类型)。在 Go 中指针类型不支持算数运算,其他操作与 C 中基本一致。
bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // 与 uint8 相同 rune // 与 int32 相同,represents a Unicode code point? float32 float64 complex64 complex128 // 复数类型 *T // 指针类型
多个不同类型的变量的定义可以通过 var() 的形式进行联合定义。
var( x int y bool = false z float32 = 3.14 //使用 var() 联合定义多个变量 )
在 Go 中,不同类型的变量需要使用强制类型转换进行类型转换(貌似常量是不能强制类型转换的)。 Go 中强制类型转换的操作为 type() 的形式。下列示例表示从 double 到 int 类型的转换
var x float32 = 3.14 var y int = int(x) // double 类型强制类型转换为 int
Go 中的常量使用 const 关键字定义,常量需要在定义时给出初始值,后续无法修改,故而定义时不需要额外说明类型,同时常量定义无法使用 ':=' 操作符定义。
const a, b = "a", false //定义常量 a 和 b
循环
Go 中仅存在 for 循环语句,其控制语句中包含有 "初始化;控制条件;循环后操作" 三个部分。上述这三个部分均为可选项。若想在初始化部分定义变量,则必须使用 ":=" 操作符,且定义的变量仅在 for 循环内部可见。与其他语言不同的是 Go 的循环语句不需要使用 "()" 包括 for 的控制语句,同时循环语句块必须位于 "{}" 中。
for i := 1; i < 10; i++{
//statements
}
在 Go 中也可使用类似 C 中 while 语句的写法,达到 while 语句的目的,此时控制语句中仅包含有控制条件判断语句。甚至 Go 可以完全省略 for 循环语句中的三个部分,此时为一个无限循环。
i := 1
for i < 100 { // Go 中的 while 语句
// statements
}
for{ // Go 中的无限循环
// statements
}
条件语句
使用 if 作为条件语句关键字,Go 中的 if 语句同样不需要使用 "()" 包围判断条件,但是需要使用 "{}" 来包围函数体。另外 Go 中的 if 语句支持在判断条件之前加入独立的初始化语句,该初始化部分在 if 判断执行之前执行,并仅在 if 语句内部可见(与 if 配对的 else 关键字对应的函数体中也可见)。
i := 3 if i < 10 { // 判断语句 // statements } if i :=3; i < 10 { // 条件语句前加入初始化语句 // statements } else { // if 语句加入 else 时,else 关键字需与 "}" 同一行 // statements }
switch
Go 中的 switch 语句与 if 语句一样,可以在进行匹配的语句前插入一个初始化语句。与 C/C++ 所不同的是,Go 中只会执行匹配的 case 条件对应的语句,而不会执行后续相邻的 case 条件的语句(相当于每个 case 语句后默认存在 break )。
switch i:=3; i { // 可插入一个初始化语句
case 1:
//statements
case 2:
//statements
default:
//statements
}
同时 switch 语句的 case 条件并不需要是常量,甚至可以是函数。
switch x{
case 0:
case f(): // x != 0 时,函数 f 会被调用...写法略浮夸
}
switch 甚至可以忽略比较对象,实现类似 if-else 的逻辑。
switch x:=3;{ // 初始化语句可省略 case x < 5: case x < 10: default: }
struct 结构体为一个数据元素的集合,通过 type 关键字定义,通过 "." 来进行访问( 在 Go 中允许使用结构体变量或者变量的指针直接通过 "." 进行结构体元素访问)。
type test struct{
x int
y int
}
对于定义的 struct 类型的变量,其结构体成员的初始值为对应类型的初始值,如 int 类型初始值为 0。用户也可以在定义时进行初始化。
a := test{} // 定义 struct 变量 a,其成员元素均为 0 b := test{1, 2} // 定义 struct 变量 b,其中 b.x = 1, b.y = 2 c := test{ x:1} // 定义 struct 变量 c,其中 c.x = 1, c.y = 0
Go 中数组类型为 [n]T,其中 n 为数组中的元素个数,T 为数组元素的类型,n 为数组类型的一部分,数组的大小后续不能修改。
var arr [3]int // 3 个元素的整形数组 arr2 := [3]int{ 0, 1, 2 } // 另一种定义方式
Go 中提供 slice 类型,用于访问数组中的元素。slice 类型为 []T,T 为其访问的元素的类型。slice 类型变量通过 arr[ low : high ] 的方式定义,其中 arr 为对应的数组,low 和 high 分别表示数组中索引,slice 类型可以访问数组中 [ low : high ) 范围的变量。当 low 被省略时,默认视为 0,high 被省略时,默认为数组长度。slice 类型可以看作对于数组中元素的引用,通过 slice 对元素的修改会对应修改数组中对应的元素。
// 数组 var arr [5]int = { 1, 2, 3, 4, 5 } //slice var a []int = arr[1:3] // a[0]=2, a[1]=3 两个元素 a[:] a[0:5] // 均可索引整个数组 b := []int{ 1, 2, 3 } // 定义数组,并建立了对应的 slice b
slice 类型变量包含有长度(length)和容量(capacity)两个信息,其中 length 表示 slice 类型可以访问的元素的个数,capacity 表示 slice 对应的数组从 slice 开始位置到数组结束位置的长度。可通过 len(a) 和 cap(a) 的方式查看 slice 的这两个属性。slice 未被初始化时,其默认值为 nil,length 与 capacity 属性均为 0,没有数组与之对应。
a := []int{ 1, 2, 3, 4, 5, 6 } a[ : 0 ] // len = 0, cap = 6 a[ : 4 ] // len = 4, cap = 6 a[ 2 : ] // len = 2, cap = 4
slice 变量还可以通过 Go 中内置的 make 函数进行创建,make 函数的使用形式为 make( T, args ) 。其中 T 为构建的类型,后接构建的参数。
a := make( []int, 4 ) // 构建一个长度为 4 的数组的 slice,数组元素默认为 0 b := make( []int, 3, 5 ) // 构建一个长度为 5 的数组的 slice,索引的slice 的 length 为 3
slice 变量可以通过内置的 append 函数进行扩充。 append 函数签名为 func append( s []T, vs ... T ) []T,其中第一个参数为进行 append 操作的 slice 变量,后面为具体进行 append 的元素。append 支持逐元素的添加,也支持添加另一个 slice.当进行 append 操作的 slice 所对应的底层数组 capacity 足够时,append 操作对应会修改底层数组的值,若 capacity 不足时,append 操作会进行数组的重新分配,并返回新的 slice 结果。
v := []int{} v := append( v, 1 ) // 添加一个元素 v := append( v, []int{1,2,3}... ) // 添加另一个 slice 中的元素,此时 append 的 slice 后需要加上 '...'
在 for 循环中,可以使用 range 关键字来简化对 slice 元素的遍历操作,每次循环过程中,range 会返回两个值,第一个为元素对应 slice 中的 index,第二个为 slice 在该 index 位置的值的拷贝。若不需要使用 range 返回的值的内容,可以使用 "_" 来忽略。同时,在 range 用法中,若只需要使用 slice 的索引,则可以省略第二个参数,直接使用 i := range a 即可。
a := []int{ 1, 2, 3, 4, 5 } for index, _ := range a { // statements } for i := range a { // 若只是用索引,则可以直接省略第二个参数 // statements }
更多关于 slice 介绍可以参考 Go Slices: usage and internals 。
Go 中提供 map 来建立 key 与 value 之间的关系。map 变量的默认值为 nil,无法进行任何操作。用户可以通过赋值或 make 函数获取可操作的 map。
var m map[int]int // m 为 nil,无法进行操作 m := map[int]int{} // m 为空的 map,可以进行操作 m := make( map[int]int ) m := make[int]int{ 1:2, 3:4 } // m 为一个 int 到 int 的 map
map 可支持的操作如下所示:
a := map[int]int{} // 获取一个空的 map 变量 a a[ 1 ] = 3 // 添加/修改 key / value 关系 b = a[ 1 ] // 返回 key 对应的 value 值,若对应的 key 不存在,直接返回对应类型的默认零值(注意不存在对应元素时不会自动创建,与 C++ map 不同) a[1] ++ // 更新 key = 1 所对应的 value 值,若 key = 1 不存在,则会创建一个值为 0 的映射并继续操作 delete( a, 1 ) // 删除 map 中的 key value, ok := a[3] // 测试 map 中是否含有对应的 key,若存在,则 ok 为 true,否则为 false,对应的 value 中包含有 key 对应的 value 或对应类型的初始零值