控制结构
在go中代码控制的关键字只有三种,if,for和switch。
if
没什么好说的。
稍微特殊的是:go的if表达式中可以声明变量,称为if语句的自用变量。该变量的作用范围是这个if代码块。
1 | a, b := 1, 1 |
for
几个特点
go的for循环支持多循环变量
1
2
3
4
5
6
7
8for i, j := 1, 2; (i < 10) && (j < 10); i, j = i+1, j+2 {
t.Log(i, j)
}
---------------------------------------------------------------------------
loop_test.go:8: 1 2
loop_test.go:8: 2 4
loop_test.go:8: 3 6
loop_test.go:8: 4 8可以仅仅保留循环判断条件表达式
1
2
3
4
5i := 0
for i < 10 {
println(i)
i++
}无限循环
1
2
3for {
// 循环体代码
}- 支持continue和break,加label跳转
for range形式
对切片循环
1 | slc := []string{"a", "b", "c"} |
要重点说一下对string的循环
1 | func TestString(t *testing.T) { |
结果是
1 | loop_test.go:23: 0 20320 你 |
对于string的循环,每次得到的v是一个unicode字符码点。index是该字符码点在该字符串中的位置。一个汉字的unicode编码用utf8形式存储占3个字节。
重点说一个数组循环的一个大坑
1 | func TestArray(t *testing.T) { |
结果:
1 | loop_test.go:41: arr: [1 12 13 4 5] |
原因是:参与 for range 循环的是 range 表达式的副本。也就是说参与循环的是arr的副本,和原始的arr是完全两个东西。
使用切片就不会有这个问题,因为切片传递的是引用。
对map循环的坑
之前的文章说过map的循环顺序具有随机性,每次循环的顺序都不一样。
所以不要在map的循环里做任何修改或增删的操作。结果会是不确定的
switch
几个特点
case语句可以接多个表达式
1
2
3
4
5
6
7
8
9
10
11switch initStmt; expr {
case expr1:
// 执行分支1
case expr2:
// 执行分支2
case expr3_1, expr3_2, expr3_3:
// 执行分支3
default:
// 执行默认分支
}不需要写break,和java不一样,go不会默认执行后面的分支。
如果需要继续执行下一个case,使用fallthrough关键字
go的swaitch表达式支持各种类型,只要这个类型可以进行比较操作就可以。不像java,只支持整型,枚举和字符串。
如果case的表达式都是布尔型的,可以省略swicth的表达式
1 | // 带有initStmt语句的switch语句 |
type switch
“type switch”这是一种特殊的 switch 语句用法。用来根据变量类型的不同而执行不同的分支。
1 | func TestType(t *testing.T) { |
switch 关键字后面跟着的表达式为x.(type),这种表达式形式是 switch 语句专有的,而 且也只能在 switch 语句中使用。
这个表达式中的 x 必须是一个接口类型变量,表达式的求 值结果是这个接口类型变量对应的动态类型。
Go 中所有类型都实现了 interface{}类型,所以case可以接各种类型。
如果x是指定的某个接口类型,那么case接的必须是实现了这个接口的类型。
另外还可以拿到x的具体值
1 | func TestType(t *testing.T) { |
这里的 v := x.(type),v得到的就是具体的值,不是x的类型。这点千万注意。这种写法也是诡异。