Go学习第4天:条件、循环语句+函数

Go 语言学习:流程控制 + 函数

目录

  1. 条件判断语句(if / if-else / switch / select)
  2. 循环语句与跳转控制(for / break / continue / goto)
  3. 函数基础与高阶用法(定义、调用、参数、返回值、闭包、方法)

一、条件判断语句

条件语句用于根据布尔表达式的结果(true/false)执行不同代码块。Go 语言不支持三目运算符 ?:,所有分支逻辑均使用标准条件语句实现。

1.1 if 系列语句

1.1.1 基础 if 语句

语法格式
go 复制代码
if 布尔表达式 {
    代码逻辑
}
语法说明
  1. 表达式结果必须是 bool 类型(true/false),不能直接使用数字充当条件;
  2. 左大括号 { 必须紧跟在表达式末尾,禁止单独换行(Go 强制语法规范);
  3. 条件表达式无需括号包裹(加括号也能运行,但不符合编码规范)。
代码示例
go 复制代码
package main
import "fmt"

func main() {
    age := 20
    if age >= 18 {
        fmt.Println("已成年")
    }
}
运行结果
复制代码
已成年
常见踩坑
  1. 错误写法:if (age >= 18) 不推荐(多余括号);if age >= 18 { 以外的换行格式会语法报错;
  2. 错误写法:if 1,Go 不允许数字当作布尔条件,和 C/C++ 语法不同。

1.1.2 if-else 语句(二分支)

语法格式
go 复制代码
if 布尔表达式 {
    条件成立执行
} else {
    条件不成立执行
}
代码示例
go 复制代码
package main
import "fmt"

func main() {
    score := 59
    if score >= 60 {
        fmt.Println("考试及格")
    } else {
        fmt.Println("考试不及格")
    }
}
运行结果
复制代码
考试不及格

1.1.3 if-else if-else 语句(多分支)

适用于多条件依次判断场景,从上到下匹配,命中后不再执行后续分支。

语法格式
go 复制代码
if 条件1 {
    // 条件1成立
} else if 条件2 {
    // 条件2成立
} else {
    // 所有条件都不成立
}
代码示例
go 复制代码
package main
import "fmt"

func main() {
    score := 85
    if score >= 90 {
        fmt.Println("优秀")
    } else if score >= 80 {
        fmt.Println("良好")
    } else if score >= 60 {
        fmt.Println("及格")
    } else {
        fmt.Println("不及格")
    }
}
运行结果
复制代码
良好

1.1.4 嵌套 if

if/else 代码块内部继续使用 if,适用于多层筛选场景。

代码示例
go 复制代码
package main
import "fmt"

func main() {
    age := 22
    gender := "男"
    if age >= 18 {
        if gender == "男" {
            fmt.Println("成年男性")
        } else {
            fmt.Println("成年女性")
        }
    }
}
常见踩坑

嵌套层级过多会降低代码可读性,复杂场景建议改用 switch 语句。

1.2 switch 语句

switch 用于多值匹配分支 ,相比多 else if 代码更简洁,是 Go 高频语法。

1.2.1 基础特性

  1. 默认匹配成功后自动终止 ,不需要手动写 break(和 C/C++/Java 区别极大);
  2. 支持任意数据类型(整型、字符串、布尔值等);
  3. 支持无表达式写法 ,等价于多 if-else
  4. fallthrough 关键字:强制执行下一个 case(不判断条件)。

1.2.2 标准 switch(带表达式)

语法格式
go 复制代码
switch 变量/表达式 {
case 值1:
    逻辑1
case 值2, 值3: // 一个case匹配多个值
    逻辑2
default: // 所有case都不匹配时执行(可选)
    默认逻辑
}
代码示例
go 复制代码
package main
import "fmt"

func main() {
    day := 3
    switch day {
    case 1:
        fmt.Println("星期一")
    case 2:
        fmt.Println("星期二")
    case 3, 4, 5: // 多值匹配
        fmt.Println("周三/周四/周五(工作日)")
    case 6, 7:
        fmt.Println("周末")
    default:
        fmt.Println("无效日期")
    }
}
运行结果
复制代码
周三/周四/周五(工作日)

1.2.3 无表达式 switch(等效 if-else)

不写 switch 后的表达式,每个 case 编写独立布尔条件,适合复杂逻辑判断。

代码示例
go 复制代码
package main
import "fmt"

func main() {
    score := 75
    switch {
    case score >= 90:
        fmt.Println("优秀")
    case score >= 60:
        fmt.Println("及格")
    default:
        fmt.Println("不及格")
    }
}

1.2.4 fallthrough 穿透(特殊用法)

fallthrough强制执行下一个 case,不会校验下一个 case 的条件,仅用于特殊业务场景,日常开发不推荐滥用。

代码示例
go 复制代码
package main
import "fmt"

func main() {
    num := 1
    switch num {
    case 1:
        fmt.Println("数字1")
        fallthrough // 穿透,继续执行下一个case
    case 2:
        fmt.Println("数字2")
    default:
        fmt.Println("其他数字")
    }
}
运行结果
复制代码
数字1
数字2

1.2.5 switch 常见踩坑

  1. 忘记 Go 默认自带 break,不要额外手写 break(语法允许,但多余);
  2. fallthrough 只能放在 case 代码块末尾,放在中间会报错;
  3. case 中多个值必须用逗号分隔,不能用 ||

1.3 select 语句

select 是 Go 独有语法,专门用于通道(channel)多路监听 ,语法类似 switch,仅在协程+通道场景使用。

1.3.1 核心规则

  1. 每个 case 必须是通道读写操作
  2. 多个 case 同时就绪时,随机选择一个执行(无优先级);
  3. default 且所有 case 都未就绪时,阻塞等待
  4. default 时,无就绪 case 则立即执行 default,不会阻塞。

1.3.2 基础示例

go 复制代码
package main
import "fmt"

func main() {
    // 创建两个缓冲通道
    ch1 := make(chan int, 1)
    ch2 := make(chan string, 1)

    ch1 <- 100
    ch2 <- "hello"

    select {
    case val := <-ch1:
        fmt.Println("读取通道1数据:", val)
    case val := <-ch2:
        fmt.Println("读取通道2数据:", val)
    default:
        fmt.Println("暂无通道数据")
    }
}
运行说明

多次运行会随机输出两个通道的内容,验证"随机选择"特性。

1.3.3 select 常见踩坑

  1. case 不能编写普通表达式,只能是通道读写;
  2. 不要依赖 case 顺序设置优先级,Go 会随机执行就绪分支;
  3. 死循环+无 defaultselect 会永久阻塞程序。

二、循环语句与跳转控制

Go 仅支持 for 循环 ,没有 while/do-while,通过 for 变体实现所有循环场景;搭配 break/continue/goto 实现跳转控制。

2.1 for 循环四大写法

2.1.1 标准 for 循环(初始化;条件;后置)

最常用写法,适用于已知循环次数的场景。

语法格式
go 复制代码
for 初始化语句; 循环条件; 后置语句 {
    循环体
}
代码示例(遍历 0~4)
go 复制代码
package main
import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Printf("当前索引:%d\n", i)
    }
}
运行结果
复制代码
当前索引:0
当前索引:1
当前索引:2
当前索引:3
当前索引:4

2.1.2 条件 for 循环(模拟 while)

省略初始化后置 语句,仅保留循环条件,等价于 while 循环,适用于未知循环次数

语法格式
go 复制代码
for 循环条件 {
    循环体
}
代码示例
go 复制代码
package main
import "fmt"

func main() {
    i := 0
    for i < 3 {
        fmt.Println(i)
        i++
    }
}

2.1.3 无限循环

省略所有语句,for {} 代表无限循环,需配合 break 手动退出。

语法格式
go 复制代码
for {
    循环体
}
代码示例
go 复制代码
package main
import "fmt"

func main() {
    count := 0
    for {
        fmt.Println("无限循环", count)
        count++
        if count >= 2 {
            break // 手动退出循环
        }
    }
}

2.1.4 for-range(遍历集合)

专门用于遍历字符串、数组、切片、map、通道,是 Go 遍历容器的标准写法。

语法格式
go 复制代码
for 索引, 值 := range 容器 {
    循环体
}
代码示例(遍历切片)
go 复制代码
package main
import "fmt"

func main() {
    arr := []string{"Go", "Java", "Python"}
    for idx, val := range arr {
        fmt.Printf("索引:%d,值:%s\n", idx, val)
    }
}
忽略索引/值:使用空白标识符 _
go 复制代码
// 只取值,忽略索引
for _, val := range arr {
    fmt.Println(val)
}

2.2 循环嵌套

for 内部嵌套 for,常用于二维数据、九九乘法表等场景。

代码示例(九九乘法表)
go 复制代码
package main
import "fmt"

func main() {
    for i := 1; i <= 9; i++ {
        for j := 1; j <= i; j++ {
            fmt.Printf("%d*%d=%d  ", j, i, j*i)
        }
        fmt.Println()
    }
}

2.3 跳转控制语句

2.3.1 break 语句

  1. 作用:终止当前循环/switch/select,跳出代码块;
  2. 普通用法:仅跳出当前单层循环
  3. 标签用法:配合标签 label 跳出多层嵌套循环(Go 推荐写法)。
示例1:单层循环 break
go 复制代码
package main
import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        if i == 2 {
            break // 终止循环
        }
        fmt.Println(i)
    }
}
示例2:标签跳出多层循环
go 复制代码
package main
import "fmt"

func main() {
    // 定义标签
    Outer:
    for i := 1; i <= 3; i++ {
        for j := 1; j <= 3; j++ {
            if j == 2 {
                break Outer // 直接跳出外层循环
            }
            fmt.Printf("i=%d,j=%d\n", i, j)
        }
    }
}

2.3.2 continue 语句

作用:跳过当前轮循环,直接进入下一轮迭代,不会终止整个循环。

代码示例(只打印奇数)
go 复制代码
package main
import "fmt"

func main() {
    for i := 1; i <= 10; i++ {
        if i%2 == 0 {
            continue // 偶数跳过,进入下一轮
        }
        fmt.Println("奇数:", i)
    }
}
补充:continue 也支持标签,用于指定跳转到外层循环。

2.3.3 goto 语句

作用:无条件跳转到指定标签位置 ,破坏代码结构,日常开发禁止滥用,仅用于异常处理等特殊场景。

代码示例
go 复制代码
package main
import "fmt"

func main() {
    fmt.Println("开始执行")
    goto End // 跳转到 End 标签
    fmt.Println("这段代码不会执行")
End: // 定义标签
    fmt.Println("跳转到此处")
}
踩坑:滥用 goto 会造成代码逻辑混乱,正规项目尽量不用。

2.4 循环通用踩坑总结

  1. 无限循环忘记加 break,程序卡死;
  2. for-range 遍历中文字符串时,索引为字节位置,建议使用 rune
  3. 嵌套循环中 break 仅跳出当前层,多层退出必须使用标签;
  4. 循环条件书写错误(如 i < 5 写成 i <= 5),导致循环次数异常。

三、函数基础与高阶用法

函数是可复用的代码块 ,Go 程序入口 main() 本身也是函数。Go 函数支持多返回值、值传递、指针传参、可变参数、闭包、方法等特性。

3.1 函数基础定义与调用

3.1.1 标准语法

go 复制代码
// 无返回值
func 函数名(参数列表) {
    函数体
}

// 单个返回值
func 函数名(参数列表) 返回值类型 {
    函数体
    return 返回数据
}

// 多个返回值
func 函数名(参数列表) (返回值1类型, 返回值2类型) {
    函数体
    return 数据1, 数据2
}
语法规则
  1. 函数必须以 func 关键字开头;
  2. 参数、返回值均需声明数据类型;
  3. 无返回值可省略返回值定义;
  4. return 用于终止函数并返回数据。

3.1.2 基础示例(单参数 + 单返回值)

go 复制代码
package main
import "fmt"

// 定义函数:求两个整数的最大值
func max(num1, num2 int) int {
    if num1 > num2 {
        return num1
    }
    return num2
}

func main() {
    a, b := 10, 20
    res := max(a, b) // 调用函数
    fmt.Println("最大值:", res)
}
运行结果
复制代码
最大值: 20

3.2 函数多返回值

Go 核心特性,常用于"结果 + 错误信息"的组合返回(标准库高频用法)。

代码示例(交换两个字符串)

go 复制代码
package main
import "fmt"

// 两个返回值
func swap(x, y string) (string, string) {
    return y, x
}

func main() {
    a, b := "Go", "Java"
    a, b = swap(a, b)
    fmt.Println(a, b)
}
运行结果
复制代码
Java Go
错误返回示例(实战常用)
go 复制代码
package main
import "errors"
import "fmt"

// 除法:返回结果 + 错误
func div(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("除数不能为0")
    }
    return a / b, nil
}

func main() {
    res, err := div(10, 0)
    if err != nil {
        fmt.Println("错误:", err)
    } else {
        fmt.Println("结果:", res)
    }
}
忽略部分返回值:使用空白标识符 _
go 复制代码
res, _ := div(10, 2) // 忽略错误返回值

3.3 函数参数传递方式

Go 所有参数默认值传递 ,如需修改原始数据,使用指针实现引用传递

3.3.1 值传递(默认)

传递参数的副本,函数内部修改不会影响原始变量。

代码示例
go 复制代码
package main
import "fmt"

func modify(x int) {
    x = 100 // 修改副本
}

func main() {
    num := 10
    modify(num)
    fmt.Println(num) // 原始值不变:10
}

3.3.2 指针传递(模拟引用传递)

传递变量内存地址,函数内部通过地址修改原始数据。

代码示例
go 复制代码
package main
import "fmt"

// 接收指针类型
func modify(x *int) {
    *x = 100 // 通过地址修改原值
}

func main() {
    num := 10
    modify(&num) // 取地址传参
    fmt.Println(num) // 原始值被修改:100
}

3.3.3 可变参数

使用 ...类型 声明可变参数,函数内等价于切片,支持传入任意数量同类型参数。

语法 + 示例
go 复制代码
package main
import "fmt"

// 可变参数:计算多个整数之和
func sum(nums ...int) int {
    total := 0
    for _, v := range nums {
        total += v
    }
    return total
}

func main() {
    fmt.Println(sum(1, 2))
    fmt.Println(sum(1, 2, 3, 4))
}

3.4 匿名函数与闭包

3.4.1 匿名函数

无函数名的函数,可直接定义并调用,也可赋值给变量。

示例
go 复制代码
package main
import "fmt"

func main() {
    // 1. 定义并立即调用
    func() {
        fmt.Println("匿名函数执行")
    }()

    // 2. 赋值给变量,重复调用
    f := func(a int) int {
        return a * 2
    }
    fmt.Println(f(5))
}

3.4.2 闭包(Closure)

引用了外部局部变量的匿名函数,闭包会"捕获"外部变量,变量生命周期延长。

经典示例(累加器)
go 复制代码
package main
import "fmt"

// 返回一个闭包
func counter() func() int {
    count := 0 // 外部变量,被闭包捕获
    return func() int {
        count++
        return count
    }
}

func main() {
    c1 := counter()
    fmt.Println(c1()) // 1
    fmt.Println(c1()) // 2

    c2 := counter()   // 新闭包,拥有独立count
    fmt.Println(c2()) // 1
}
闭包踩坑:循环中使用闭包易出现变量复用问题。

3.5 方法(Method)

带有接收者的函数,绑定到结构体/自定义类型上,是 Go 实现面向对象的核心方式。

语法格式

go 复制代码
func (接收者 类型) 方法名(参数) 返回值 {
    方法体
}
代码示例
go 复制代码
package main
import "fmt"

// 定义结构体
type User struct {
    Name string
    Age  int
}

// 绑定到 User 的方法
func (u User) sayHello() {
    fmt.Printf("你好,我是%s,年龄%d\n", u.Name, u.Age)
}

func main() {
    u := User{Name: "张三", Age: 20}
    u.sayHello() // 调用方法
}

3.6 函数常见踩坑总结

  1. 多返回值调用时,必须接收所有返回值 ,不需要的用 _ 忽略;
  2. 值传递无法修改原始变量,修改变量必须使用指针传参;
  3. 可变参数必须放在参数列表最后
  4. 函数名小写仅当前包可见,大写可跨包调用(Go 访问规则);
  5. main 函数无参数、无返回值,不能手动调用。

相关推荐
tedcloud1231 小时前
Supermemory部署教程:打造Agent记忆与RAG环境
服务器·人工智能·学习·自动化·powerpoint
骑士雄师1 小时前
18.1 星系案例:多智能体宇宙探索系统(学习langgraph 的存储知识)
windows·python·学习
lizhihai_991 小时前
股市学习心得-六月的股市怎么应对
大数据·人工智能·科技·学习·区块链
数智工坊2 小时前
机器人控制总线深度解析:CAN与EtherCAT,谁在决定机器人的稳定性?
嵌入式硬件·学习·机器人
旅僧2 小时前
机械臂学习笔记(更新中)
笔记·学习
qingwufeiyang_5302 小时前
Python学习笔记3-项目实战-AI应用
笔记·学习
-To be number.wan2 小时前
计算机组成原理 | 虚拟存储器
学习·计算机组成原理
暖馒3 小时前
WPF-Prism学习入门步骤记录
学习·wpf
MartinYeung53 小时前
[论文学习]透过增强式 Few-Shot Learning 实现高效 PII 从大型语言模型中提取
人工智能·学习·语言模型