这篇文章已经放到腾讯智能工作台的知识库啦,链接在这里:ima.copilot-Go 入门到入土。要是你有啥不懂的地方,就去知识库找 AI 聊一聊吧。
1、if 条件判断
在 Go 语言中,if
条件判断是一种基本的控制流结构,用于根据条件的真假来执行不同的代码块。其语法简洁明了,易于上手。
1、基础语法
if
语句的基本语法格式如下:
go
if boolean_expression {
// 当布尔表达式为 true 时,执行这里的代码
}
关键点:
-
条件
boolean_expression
必须是一个结果为布尔值(true
或false
)的表达式。 -
与许多其他语言不同,Go 语言的
if
条件不需要 用括号()
包围,但执行体的花括号{}
是必需的。 -
按照 Go 的编码规范,左花括号
{
应当与if
关键字在同一行。
让我们来看一个判断年龄的简单示例:

在上面的代码中,因为 age < 18
的结果为 true
,所以程序会打印"未成年"。如果将 age
的值改为 22
,则条件不成立,if
块内的代码不会被执行。

2、复合条件
在实际开发中,我们常常需要判断多个条件。这时可以使用逻辑运算符 &&
(与) 和 ||
(或) 来构建复合条件。
例如,我们需要同时判断年龄和国籍:

只有当 age < 18
并且 country == "中国"
这两个条件都为 true
时,判断才会成立。如果其中任何一个条件为 false
,整个表达式的结果就是 false
。
当条件逻辑变得复杂时,可以使用括号 ()
来明确分组和优先级,确保逻辑的正确性。
3、嵌套 if
if
语句内部可以包含另一个 if
语句,形成嵌套结构,以实现更复杂的逻辑判断。

这个嵌套 if
的逻辑与前面使用 &&
的复合条件是等价的,但在某些场景下,嵌套结构能让代码层次更分明。
4、if-else
与 else if
为了处理条件不成立时的情况,我们可以使用 else
。当 if
的条件为 false
时,程序将自动执行 else
块中的代码。

如果需要处理多个互斥的条件,可以使用 else if
结构。程序会从上到下依次判断,一旦某个 if
或 else if
的条件成立,就会执行其对应的代码块,并跳过所有剩余的 else if
和 else
。

5、最佳实践:if-else if
vs. 多个 if
思考以下两种写法,它们在功能上似乎是等价的:
写法一:使用 if-else if-else
(推荐)

写法二:使用多个独立的 if

为什么推荐写法一?
-
逻辑清晰 :
if-else if
结构明确地表达了一系列互斥的条件,阅读代码时能立刻明白这些条件中只有一个会成立。 -
性能更优 :当
age
为16
时,写法一在判断age < 18
成立后,便不再执行后续的else if
和else
判断。而写法二则会继续执行后续的两个if
判断,进行了不必要的比较操作。虽然在简单场景下性能差异微乎其微,但在复杂逻辑或循环中,这种差异可能会被放大。
因此,在处理一系列互斥条件时,应优先使用 if-else if-else
结构。
2、for循环基础用法
Go 语言在循环结构的设计上秉持了其一贯的简洁风格,仅提供了 for
这一种循环关键字 。这与其他编程语言(如 C, Java, Python)同时提供 for
和 while
循环的做法不同。
Go 的设计者认为,for
循环足够灵活,可以完全覆盖 while
循环的所有使用场景。通过省略 for
循环的某些部分,可以轻松模拟出 while
的行为。这种设计减少了语言的关键字数量,使得语法更加统一。
1、for
循环的基础语法
for
循环的标准形式包含三个部分,由分号 ;
分隔,分别对应:初始化语句 、条件表达式 和后置语句。
go
for initialization; condition; post {
// 循环体代码
}
-
初始化语句 (initialization):在循环开始前执行,通常用于声明和初始化一个局部循环变量。
-
条件表达式 (condition) :在每次循环迭代开始前进行求值。如果为
true
,则执行循环体;如果为false
,则退出循环。 -
后置语句 (post):在每次循环迭代结束后执行,通常用于更新循环变量。
与 if
语句一样,for
循环的条件部分不需要括号。
让我们通过一个打印数字 0 到 2 的例子来了解其用法:

2、灵活的 for
循环 (模拟 while)
for
循环的三个部分都不是必需的,可以根据需要省略,这使得 for
循环非常灵活,能够轻松模拟 while
循环。
2.1 模拟 while (condition)
通过省略初始化语句和后置语句,只保留条件表达式,for
循环就等同于 while
循环。

这段代码的执行效果与前一个例子完全相同。
2.2 模拟无限循环 while (true)
如果将三个部分全部省略,就构成了一个无限循环,等效于其他语言中的 while (true)
或 for(;;)
。这在需要持续运行的服务或监听事件的场景中非常常见。

在无限循环中,通常需要一个明确的退出条件,可以使用 break
语句来跳出循环。例如,我们可以在循环体内加入一个 if
判断:

这个例子最终也只会打印 0, 1, 2,然后程序正常结束。这种 for
结合 if-break
的模式是 Go 中实现条件循环的常用方式。
3、for 循环打印 九九乘法表
掌握了 for
循环的基础之后,我们通过两个经典的编程练习来巩固所学知识:计算 1 到 100 的累加和,以及打印九九乘法表。
1、计算 1 到 100 的和
这是一个非常基础的循环应用。我们的思路是:
-
在循环外部定义一个用于累加求和的变量
sum
。 -
使用
for
循环,让循环变量i
从 1 遍历到 100。 -
在每次循环中,将当前的循环变量
i
的值累加到sum
上。 -
循环结束后,打印
sum
的值。

代码解析:
-
sum := 0
:初始化sum
为 0。这个变量必须在循环外部定义,否则每次循环都会被重置。 -
for i := 1; i <= 100; i++
:经典的for
循环结构,i
的取值范围是[1, 100]
。 -
sum += i
:这是 Go 中的复合赋值操作符,是sum = sum + i
的简写形式,代码更简洁。
2、打印九九乘法表
打印九九乘法表需要使用嵌套循环,一个外层循环控制行数,一个内层循环控制列数。
-
逻辑分析:
-
九九乘法表总共有 9 行。
-
第
i
行有i
个表达式。例如,第 2 行有两个(2*1
,2*2
),第 5 行有五个。 -
这表明内层循环的次数是受外层循环的当前变量控制的。

代码解析:
-
外层循环
for i := 1; i <= 9; i++
:控制从第 1 行到第 9 行。 -
内层循环
for j := 1; j <= i; j++
:是关键所在。它控制每一行需要打印的表达式数量。当外层循环i
为 3 时,内层循环j
会从 1 循环到 3,从而打印出三个表达式。 -
fmt.Printf
:用于格式化输出。%d
是数字的占位符,%-2d
表示输出一个左对齐且至少占 2 个字符宽度的数字,这能让乘法表看起来更整齐。 -
fmt.Println()
:在内层循环结束后,用于输出一个换行符,以便开始新一行的打印。
4、for range 的循环用法
除了标准的三段式 for
循环,Go 还提供了一种更强大、更便捷的 for range
结构,专门用于遍历各种数据集合,类似其他语言中的 foreach
。for range
可以用于遍历字符串 、数组 、切片 、map 和 channel。
1、for range
的语法
其基本语法格式如下:
go
for key, value := range collection {
// ...
}
-
collection
:表示待遍历的数据集合。 -
key
,value
:每次循环,for range
会从中collection
取出一对值,分别赋值给key
和value
。这对变量的含义取决于被遍历的数据类型。 -
:=
:这里必须使用短变量声明,它会为每次循环迭代创建新的key
和value
变量。
2、遍历字符串
当 for range
用于遍历字符串时:
-
key
是每个字符的起始字节索引 (index)。 -
value
是该字符对应的 Unicode 码点 (类型为rune
),而不是字符本身。

注意 :value
得到的是原值的拷贝 ,在循环中修改 value
不会影响原始的 collection
。
3、for range
的灵活用法
3.1 忽略变量
如果你在循环中不需要 key
或 value
,可以使用匿名变量 (下划线 _
)来忽略它,以避免编译器报"声明但未使用的变量"错误。

3.2 只接收一个返回值
如果 for range
只接收一个变量,那么该变量接收到的值是 key
(对于字符串、数组、切片来说就是索引)。

4、for range
与不同数据类型的行为
|数据类型|key
的含义|value
的含义|只接收一个变量时|
|---|---|---|---|
|字符串 |字节索引 (index)|Unicode 码点 (rune) 的拷贝|key
(索引)|
|数组 / 切片 |元素索引 (index)|元素值的拷贝|key
(索引)|
|Map |map 的键 (key)|map 的值的拷贝|key
(map 的键)|
|Channel |(无 key) |从 channel 接收到的值|value
(接收到的值)|
5、 处理中文字符
Go 语言的字符串是 UTF-8 编码的字节序列。一个英文字母占 1 个字节,而一个中文字符通常占 3 个字节。
如果使用传统的 for i := 0; i < len(str); i++
或单返回值的 for range
来遍历包含中文的字符串,由于是按字节索引,会导致中文字符被"撕裂"而产生乱码。

正确处理中文的方式:
for range
在设计上已经完美地处理了多字节字符。它能自动识别一个完整字符所占的字节数,并正确地返回其 Unicode 码点和起始索引。
5.1 方式一:使用双变量 for range
(推荐)

可以看到,索引值按 3 递增,for range
准确地跳过了每个中文字符的字节。
5.2 方式二:转换为 []rune
切片
如果确实需要在处理中文字符时获得连续的字符索引(0, 1, 2, ...),可以先将字符串转换为 rune
切片。

为了代码的简洁和高效,当需要遍历字符串中的字符时,强烈推荐直接使用 for key, value := range str
的形式。
5、continue 和 break 语句
在循环过程中,有时我们需要更精细地控制循环的执行流程,而不是简单地等待循环条件自然结束。Go 语言提供了 break
和 continue
两个关键字来实现这一目标。
1、break
:终止循环
break
语句用于立即终止 其所在的最内层 的 for
循环。一旦执行 break
,程序将跳出循环体,继续执行循环之后的代码。
这在无限循环或需要在满足特定内部条件时提前退出的场景中非常有用。

在上面的例子中,循环会一直进行,直到 round
的值达到 10,if
条件成立,break
语句被执行,从而强制结束这个无限循环。
2、continue
:跳过当次迭代
continue
语句用于跳过当前这次循环迭代中尚未执行的代码,并立即进入下一次循环的迭代。
当我们在循环中遇到某个特定条件,不希望执行后续的逻辑,而是想直接开始下一轮时,continue
就派上了用场。
例如,我们想打印 1 到 10,但跳过数字 5:

在这个例子中,当 i
等于 5 时,continue
语句被触发,它使得当次循环的 fmt.Println(i)
语句被忽略,程序直接跳转到 for
循环的下一次迭代(i++
之后,i
变为 6)。
6、goto语句 (谨慎使用)
goto
是 Go 语言中的一个跳转语句,它可以让程序无条件地跳转到函数内的指定标签 (label) 处继续执行。
由于 goto
会破坏程序的正常执行流程,过度使用会导致代码逻辑混乱,可读性极差(即所谓的"面条代码"),因此强烈不建议滥用 。绝大多数情况下,我们都可以通过 if
、for
、return
等结构来更好地实现逻辑。
1、goto
的基本用法
goto
的语法包含两部分:goto label;
和 label:
。
-
定义标签 :
label:
是一个标识符,后跟一个冒号,放置在函数内某行代码的前面。 -
执行跳转 :
goto label;
语句会使程序立即跳转到label:
所在的位置。
一个主要的应用场景是从嵌套的深层循环中一次性跳出 。我们知道,break
只能跳出其所在的最内层循环,如果想直接终止所有循环,goto
提供了一种解决方案。

在上面的例子中,当内层循环的 j
首次等于 2 时,goto Over
被执行,程序立即跳出两层循环,直接执行 Over:
标签下的 fmt.Println("任务结束")
。
2、goto
的合理使用场景
尽管 goto
名声不佳,但在一些特定场景下,合理使用可以简化代码,最常见的便是统一的错误处理。
当一个函数中有多个可能出错的步骤,且每个错误都需要进行相似的清理工作时,可以使用 goto
将所有错误处理逻辑集中到一处。

即便如此,在现代 Go 编程中,使用 defer
语句来处理资源释放通常是更推荐、更清晰的做法。
总而言之,请将 goto
视为一个最后的选择,仅在它能显著简化代码且不降低可读性的罕见情况下考虑使用。
7、switch 语句
switch
是 Go 中一种强大的分支控制语句。它提供了一种比一长串 if-else if
链更清晰、更易读的方式来对一个表达式的多种可能的值进行判断。
1、switch
的基础语法
go
switch expression {
case value1:
// expression 的值等于 value1 时执行
case value2:
// expression 的值等于 value2 时执行
...
default:
// 所有 case 都不匹配时执行
}
-
expression
:一个表达式,其结果将与各个case
的值进行比较。 -
case value
:每个case
定义了一个要比较的值。 -
default
:一个可选的子句,当没有任何case
匹配时执行,类似于if-else
中的else
。 -
自动
break
:与 C 或 Java 不同,Go 的switch
在每个case
块的末尾自动包含了一个break
。这意味着一旦匹配到一个case
,执行完其代码后就会退出switch
,不会"掉落"到下一个case
。
让我们来看一个根据星期输出对应英文的例子:

2、switch
的灵活用法
2.1 省略表达式
switch
后面的表达式可以省略。这种形式的 switch
等同于 switch true
,每个 case
作为一个独立的布尔表达式进行判断。这使得 switch
可以完全替代 if-else if-else
语句,并且通常更具可读性。
例如,我们可以用它来进行分数等级评定:

2.2 一个 case 匹配多个值
一个 case
后面可以跟多个值,用逗号分隔。只要表达式的结果匹配其中任意一个值,该 case
就会被执行。

至此,关于 Go 语言的基础流程控制语句(if
、for
、switch
)及其相关的跳转控制(break
、continue
、goto
)就介绍完毕了。掌握它们是编写任何复杂程序的基石。在下一篇的博客中,我们将开始探索 Go 语言更丰富的数据结构。