流程控制
- if
- switch
- for
- range
- select
- goto,break,continue
提示
- Go中不支持三目运算符
- 不支持while
if
- if 语句 由一个布尔表达式后紧跟一个或多个语句组成。
- 花括号不能换行
- 支持多层嵌套
语法
- 可省略条件表达式括号。
- 持初始化语句,可定义代码块局部变量。
- 代码块左 括号必须在条件表达式尾部。
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
代码示例
if n := "abc"; x > 0 { // 初始化语句未必就是定义变量, 如 println("init") 也是可以的。
println(n[2])
} else if x < 0 { // 注意 else if 和 else 左大括号位置。
println(n[1])
} else {
println(n[0])
}
switch
- switch 语句用于基于不同条件执行不同动作,每一个 case 分支都是唯一的,从上直下逐一测试,直到匹配为止。
- Golang switch 分支表达式可以是任意类型,不限于常量。==可省略 break==,默认自动终止。
语法
switch var1 {
case val1:
...
case val2:
...
default:
...
}
变量 var1 可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。 您可以同时测试多个可能符合条件的值,使用逗号分割它们,例如:case val1, val2, val3。
/* 定义局部变量 */
var grade string = "B"
var marks int = 90
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
}
switch {
case grade == "A" :
fmt.Printf("优秀!\n" )
case grade == "B", grade == "C" :
fmt.Printf("良好\n" )
case grade == "D" :
fmt.Printf("及格\n" )
case grade == "F":
fmt.Printf("不及格\n" )
default:
fmt.Printf("差\n" )
}
fmt.Printf("你的等级是 %s\n", grade )
Stdout
优秀!
你的等级是 A
其他用法 - Type Switch
switch 语句还可以被用于 type-switch 来判断某个 interface 变量中实际存储的变量类型。
语法
switch x.(type){
case type:
statement(s)
case type:
statement(s)
/* 你可以定义任意个数的case */
default: /* 可选 */
statement(s)
}
var x interface{}
//写法一:
switch i := x.(type) { // 带初始化语句
case nil:
fmt.Printf(" x 的类型 :%T\r\n", i)
case int:
fmt.Printf("x 是 int 型")
case float64:
fmt.Printf("x 是 float64 型")
case func(int) float64:
fmt.Printf("x 是 func(int) 型")
case bool, string:
fmt.Printf("x 是 bool 或 string 型")
default:
fmt.Printf("未知型")
}
for
for循环是一个循环控制结构,可以执行指定次数的循环。
语法
for init; condition; post { } for condition { } for { } init: 一般为赋值表达式,给控制变量赋初值; condition: 关系表达式或逻辑表达式,循环控制条件; post: 一般为赋值表达式,给控制变量增量或减量。 for语句执行过程如下: ①先对表达式 init 赋初值; ②判别赋值表达式 init 是否满足给定 condition 条件,若其值为真,满足循环条件,则执行循环体内语句,然后执行 post,进入第二次循环,再判别 condition;否则判断 condition 的值为假,不满足条件,就终止for循环,执行循环体外语句。
s := "abcd"
for i, n := 0, len(s); i < n; i++ { // 常见的 for 循环,支持初始化语句。
println(s[i])
}
n := len(s)
for n > 0 { // 替代 while (n > 0) {}
n--
println(s[n]) // 替代 for (; n > 0;) {}
}
i := 0
for { // 替代 while (true) {}
println(s) // 替代 for (;;) {}
if i > 10 {
break
}
i = i + 1
}
for true { // 替代 while (true) {}
}
提示:在初始化语句中计算出全部结果
func length(s string) int {
println("call length.")
return len(s)
}
func main() {
s := "abcd"
for i, n := 0, length(s); i < n; i++ { // 避免多次调用 length 函数。
println(i, s[i])
}
}
range
- for range 支持对数组、切片、字符串、map、通道进行遍历操作。
- 数组、切片、字符串返回索引和值。
- map 返回键和值。
- 通道(channel)只返回通道内的值。
- 在需要时,可以使用匿名变量对 for range 的变量进行选取。
语法
for key,val := range string|array|map|chan {
}
==val==始终为集合中对应索引的==值拷贝==,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值。一个字符串是 Unicode 编码的字符(或称之为 rune )集合,因此也可以用它来迭代字符串
示例
s := "abc"
// 忽略 value,支持 string/array/slice/map。
for key := range s {
println(s[key])
}
// 忽略 index。
for _, value := range s {
println(value)
}
// 忽略全部返回值,仅迭代。
for range s {
}
// map
m := map[string]int{"a": 1, "b": 2}
// 返回 (key, value)。
for key, value := range m {
println(key, value)
}
// 忽略 value
for key := range m {
println(m[key])
}
// chan
c := make(chan int)
go func() {
c <- 1
c <- 2
c <- 3
close(c)
}()
for v := range c {
fmt.Println(v)
}
提示 map 是无序的,因此多次遍历的结果顺序可能不一致
select
- 类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
- select 是Go中的一种控制结构,类似用于通信的switch语句。每个case必须是==IO操作==,要么是发送要么是接收。
语法
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
每个case都必须是一个通信 所有channel表达式都会被求值 所有被发送的表达式都会被求值 如果任意某个通信可以进行,它就执行;其他被忽略。 如果有多个case都可以运行,Select会随机公平地选出一个执行。其他不会执行。 否则: 如果有default子句,则执行该语句。 如果没有default字句,select将阻塞,直到某个通信可以运行;Go不会重新对channel或值进行求值。
Select语句和deadlock死锁
我们将学习如何使用默认情况来避免死锁。但是首先,我们了解什么是deadlock?
死锁:当您试图从通道读取或写入数据但通道没有值时。因此,它阻塞goroutine的当前执行,并将控制传递给其他goroutine,但是如果没有其他goroutine可用或其他goroutine睡眠,由于这种情况,程序将崩溃。这种现象称为死锁。
示例 1 - 没有其他Goroutine参与
func main() {
//创建通道
//出现死锁是因为没有goroutine在写
//因此,select语句被永远阻塞
c := make(chan int)
select {
case <-c:
}
}
Stdout
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
示例 2 - 当select语句只有nil通道时,也允许使用默认情况。如下面的实例所示,通道c是nil,所以默认情况下执行,如果这里的默认情况是不可用的,那么程序将永远被阻塞,死锁出现。
func main() {
//创建通道
var c chan int
select {
case x1 := <-c:
fmt.Println("Value: ", x1)
default:
fmt.Println("Default case..!")
}
}
这里还是有疑问,表象上看似通了,但是到底为什么会造成deadlock呢?