Lazy loaded image
Lazy loaded imagego-流程控制
字数 4935阅读时长 13 分钟
2025-7-30
2025-7-30
type
status
date
slug
summary
tags
category
icon
password

流程控制

流程控制主要用于设定计算执行的次序,建立程序的逻辑结构。Go 语言的流程控制和其他编程语言类似,支持如下几种流程控制语句: 条件语句:用于条件判断,对应的关键字有 if、else 和 else if; 分支语句:用于分支选择,对应的关键字有 switch、case 和 select; 循环语句:用于循环迭代,对应的关键字有 for 和 range; 跳转语句:用于代码跳转,对应的关键字有 goto。

分支

分支控制if

让程序有选择的执行,分支控制有三种:单分支、双分支、多分支
if 后面必须是布尔类型的表达式。如果条件表达式为真,则执行 {} 之间的代码。if-else 语句之间可以有任意数量的 else if。条件判断顺序是从上到下。如果 if 或 else if 条件判断的结果为真,则执行相应的代码块。 如果没有条件为真,则 else 代码块被执行。
我们可以编写一个简单的条件语句示例代码如下:
执行输出结果为:良好
在 Go 语言中,else 必须紧跟在 if 的 } 后面的同一行,不能单独换行,否则会导致编译错误。比如,在下面的代码中,else 语句不是从 if 语句结束后的 } 同一行开始。而是从下一行开始。这是不允许的
如果运行这个程序,编译器会输出错误:syntax error: unexpected keyword else, expected }。这是因为 Go 语言在解析代码时,编译器会在行尾自动插入分号,如果该行以 } 结尾。在编译器眼里,其实被解析成了:
由于 if{…} else {…} 是一个单独的语句,它的中间不应该出现分号。因此,需要将 else 语句放置在 } 之后处于同一行中。
Go 支持在 if 的判断条件前,先执行一个变量声明或赋值语句,这个变量只在当前的 if/else 代码块中有效
我们可以编写一个简单的条件语句示例代码如下:
age := 20 是初始化语句,age >= 18 是条件判断表达式。变量 age 的作用域只在 if 和 else 的代码块中,外部无法访问。执行如上代码输出结果为:成年人

分支语句switch

在 Go 语言中,switch 是一种多分支条件语句,用于根据表达式的结果值,执行匹配的 case 分支。在选项列表中,case 不允许出现重复项。它通常是 if-else 的简洁替代方案。基本语法:
我们可以编写一个简单的示例代码,如下:
switch day 将 day 的值与每个 case 语句进行比较。通过从上到下对每一个值进行对比,并执行与选项值匹配的第一个逻辑。在上述样例中, day 值为 3,因此打印的结果是:星期三
Go 中的多表达式 case 一对多匹配:在 Go 的 switch 语句中,一个 case 后可以同时列出多个值,用逗号分隔。这表示只要匹配其中任意一个值,就会执行该 case 代码块。示例代码:
输出结果为:良好
在 switch 语句中,表达式是可选的,可以被省略。如果省略表达式,则表示这个 switch 语句等同于 switch true,并且每个 case 表达式都被认定为有效,相应的代码块也会被执行。当 switch 有表达式时,case 后的值必须和 switch 表达式的类型一致
输出结果为:num 在 51 到 100 之间
在 Go 语言中,如果你在 switch 语句中使用初始化语句来声明变量,那么这个变量的作用域仅限于当前 switch 块中,包括所有 case 和 default 子句
当 switch 省略了表达式时,Go 会隐式写入 true,因此它相当于 switch true,定义的变量 x 只在整个 switch 语句块内可见
输出结果为:x is 5
在 Go 中,每执行完一个 case 后,会从 switch 语句中跳出来,不再做后续 case 的判断和执行。使用 fallthrough 语句可以在已经执行完成的 case 之后,把控制权转移到下一个 case 的执行代码中
fallthrough 只穿一层,所以执行的结果为

for循环

Go 是一门简洁的语言,在循环语句方面,只保留了 for 这一种关键字,统一替代了其他语言中的 while 和 do-while 循环。for 循环的标准语法格式:
initialisation:初始化语句,通常用于定义和初始化循环变量,只执行一次 condition:条件表达式,每轮循环开始前进行判断;为 true 时执行循环体 post:循环变量的更新语句,在每次循环体执行后执行
初始化语句、条件表达式、post 语句都是可选的。循环执行流程:执行一次初始化语句initialisation;循环初始化后,将检查循环条件:如果条件的计算结果为 true ,则 {} 内的循环体将执行,接着执行 post 语句。post 语句将在每次成功循环迭代后执行。在执行 post 语句后,条件将被再次检查。如果为 true, 则循环将继续执行,否则 for 循环将终止。
让我们用 for 循环写一个打印出从 1 到 10 的程序:
i := 1:i 变量被初始化为 1,在循环开始之前只能执行一次。 i <= 10:条件语句会检查 i 是否小于等于 10。如果条件成立,i 就会被打印出来,否则循环就会终止。 i++:循环语句会在每一次循环完成后自增 1。一旦 i 变得比 10 要大,循环中止。
上面的程序会打印出:1 2 3 4 5 6 7 8 9 10

无限循环

Go 语言为了简洁性,不支持 while 和 do-while 等传统循环语句,而是统一使用 for 关键字来完成所有类型的循环操作,包括有限循环和无限循环。如果你在 for 语句中省略条件表达式部分,Go 编译器会默认将其视为 true,因此可以很方便地实现一个无限循环(死循环)。
死循环标准写法,条件被省略:
下面这几种写法,逻辑上是等价的,都表示无限循环:
无限循环一般都带中断条件,否则就成死循环了
输出:

for-range

在 Go 中,针对可迭代的数据结构(如数组、切片、map、字符串、通道),可以使用 for-range 结构进行遍历。基本语法格式:
key 是索引、键或通道接收到的值 value 是对应的元素值 collection 可以是数组、切片、map、字符串、通道等
遍历字符串示例:
这里的k是字节索引,表示当前字符(rune)在字符串中的 起始位置(按 UTF-8 字节计) v是 rune 类型,即 Unicode 码点,表示字符本身; range 遍历的是 字符(rune) 而非字节(byte); 汉字在 UTF-8 编码下通常占用 3 个字节,所以你会看到 "语" 的起始索引是 2,而 "言" 是 5。"G" 和 "o" 各占 1 个字节;"语" 和 "言" 各占 3 个字节;所以总长度是 1 + 1 + 3 + 3 = 8 字节;但用 range 遍历时,得到的是 4 个字符(rune)。
Ascii 是1个字节,字符串是utf-8,3个字节。把一个字符拿出来,字符类型是rune,即int32,是unicode码,2字节。 for-range 太智能了,汉字从utf-8自动转换成unicode
循环过程中,要忽略索引/键,可以这么做:
要忽略元素值,可以这么做:

跳转语句

continue

continue 语句用来跳出 for 循环中当前循环。在 continue 语句后的所有的 for 循环语句都不会在本次循环中执行。循环体会在一下次循环中继续执行。让我们使用 continue 写一个程序,打印出 1 到 10 之间的奇数。
if i%2==0 会判断 i 除以 2 的余数是不是 0,如果是 0,这个数字就是偶数然后执行 continue 语句,从而控制程序进入下一个循环。因此在 continue 后面的打印语句不会被调用而程序会进入一下个循环。上面程序会输出 1 3 5 7 9。

break

break 语句用于在完成正常执行之前突然终止 for 循环,之后程序将会在 for 循环下一行代码开始执行。让我们写一个从 1 打印到 5 并且使用 break 跳出循环的程序。
在循环过程中 i 的值会被判断。如果 i 的值大于 5 然后 break 语句就会执行,循环就会被终止。上面程序会输出为
一条break语句可以用来提前跳出包含此break语句的最内层for循环。 下面这段代码同样逐行打印出0到9十个数字。

goto

goto 顾名思义,是用于跳转语句执行位置的指令。虽然在很多编程语言中 goto 被视为不推荐使用的语法,甚至被直接取消,但 Go 语言仍然保留了它的使用。这说明 goto 在特定场景下仍有其存在的合理性。只不过目前我还没有亲身体会到它的优势或适用场景。
在 Go 语言中,goto 语句用于无条件跳转到代码中的某个标签位置。该标签指示了程序下一步要执行的代码位置。因此,标签的命名和放置位置非常关键,错误或随意的使用可能会导致代码结构混乱,增加调试难度。
最简单的示例
执行结果,并不会输出 B ,而只会输出 A
goto 语句通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。下面我们用 goto 的方式来实现一个打印 1到5 的循环
输出结果如下
再举个例子,使用 goto 实现 类型 break 的效果
举个例子,使用 goto 实现 类型 continue的效果,打印 1到10 的所有偶数。

异常

error

Go 语言的错误处理机制设计得非常简单直接,不像某些语言那样引入复杂的异常类层级结构。Go 使用的是一种基于返回值的错误处理方式,并为此定义了一个统一的接口:error 接口。error 是 Go 标准库中内置的一个接口,定义如下:
只要某个类型实现了 Error() 方法,它就实现了 error 接口。在函数返回值中,通常会约定以 result, err 的形式返回执行结果和可能发生的错误。调用者通过判断 err 是否为 nil 来决定程序是否继续执行。对于大多数函数或类方法,如果要返回错误,基本都可以定义成如下模式:将错误类型作为第二个参数返回:
然后在调用返回错误信息的函数/方法时,按照如下「卫述语句」模板编写处理代码即可:

延迟调用defer

defer 的用法很简单,只要在后面跟一个函数的调用,就能实现将这个函数的调用延迟到当前函数执行完后再执行。
下面我们看一段简单的 defer 示例代码,可以很快帮助你理解 defer 的使用效果。
输出如下

panic

大多数编程语言都会提供异常捕获机制。在 Go 语言中,一些错误可以在编译阶段被捕捉,比如语法错误、类型不匹配等;但仍有不少错误只能在运行时才会暴露出来,比如数组越界访问、空指针解引用等,这些属于运行时错误,通常会导致程序立即崩溃。
不过,引发程序崩溃的不一定都是系统错误,我们也可以在程序中主动触发错误。当某些业务逻辑判断失败,或当前运行环境不满足程序继续执行的前提条件时,比如监听端口已被占用,可以手动调用 Go 提供的 panic() 函数,触发一个运行时错误,让程序有意识地中止运行,以避免更严重的问题。
手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数即可
运行后,直接报错宕机。为了更好的展示效果,下面我们以除数为 0 的示例代码为例,在代码中显式抛出 panic,以便对错误和异常信息进行自定义
这样一来,当我们执行这段代码时,就会触发
。一旦触发 panic,Go 会立即中断当前协程(goroutine)后续的代码执行,并按照先进后出(LIFO)的顺序执行所有已经注册的 defer 语句。defer 的执行过程类似于临终处理,无论是正常结束还是出现异常,都会被调用。在所有 defer 执行完毕后,程序将输出 panic 的错误信息,并附带一份堆栈跟踪(stack trace),用于帮助开发者定位问题。最后,程序非正常退出。也就是下面红框中的内容:
notion image
第一行表示出问题的协程,第二行是问题代码所在的包和函数,第三行是问题代码的具体位置,最后一行则是程序的退出状态,通过这些信息,可以帮助你快速定位问题并予以解决。

recover

我们还可以通过 recover() 函数对 panic 进行捕获和处理,从而避免程序崩溃然后直接退出,而是继续可以执行后续代码,它可以让程序在发生宕机后起生回生。但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。
 
===未完待续==
上一篇
go-数据类型
下一篇
go-函数