文章目录
- [Go 语言函数入门:定义、参数、返回值](#Go 语言函数入门:定义、参数、返回值)
-
- 一、前置准备
- 二、函数核心概念(新手必懂)
- 三、函数的定义(重中之重,语法规范)
-
- [1. 基础定义语法](#1. 基础定义语法)
- [2. 4种常用函数定义场景(覆盖新手所有基础需求)](#2. 4种常用函数定义场景(覆盖新手所有基础需求))
- 四、函数参数详解(核心用法,避免踩坑)
- 五、函数返回值详解(核心用法,多返回值是特色)
- 六、综合实战案例(结合之前知识点)
- 七、新手常见错误(避坑指南)
- 八、本篇总结
Go 语言函数入门:定义、参数、返回值
大家好~前面我们已经掌握了 if else、switch 条件语句和 for 循环语句,能实现简单的分支判断和重复执行逻辑。但如果我们需要多次使用某一段代码(比如多次计算两个数的和、多次判断成绩等级),逐行重复编写会非常繁琐,也不利于维护。本篇我们学习 Go 语言的核心模块化工具------函数,它能将一段实现特定功能的代码封装起来,实现代码复用、简化逻辑,是 Go 语言开发中不可或缺的基础[superscript:1]。
和之前一样,全程搭配可直接运行的代码示例,零基础也能轻松上手,重点讲解函数的定义规范、参数传递、返回值用法这三大核心内容,同时结合前文知识点,让大家学完就能用函数优化代码,迈出模块化编程的第一步!
核心提示:Go 语言中函数是"一等公民",可以赋值给变量、作为参数传递,还支持多返回值等特色用法,本篇先掌握基础的定义、参数和返回值,后续再逐步进阶高阶用法[superscript:3]。
一、前置准备
继续使用我们的 GoStudy 项目,新建文件夹 functiondemo,创建 main\.go 文件,所有测试代码均在此文件中编写运行。
基础代码结构(复制即可):
go
package main
import "fmt"
func main() {
// 函数调用代码写在这里
}
// 自定义函数写在 main 函数外部(Go 语言不允许函数嵌套,除非是匿名函数)
// 下面将逐步编写自定义函数
核心说明:函数是实现特定功能的代码块,就像一个"工具"------我们提前定义好这个工具的功能,后续需要使用时,直接"调用"它即可,无需重复编写工具内部的代码[superscript:4]。比如我们定义一个"求和工具"(函数),后续需要求和时,直接调用这个工具,传入两个数字,就能得到结果。
举个直观例子(未定义函数前,重复代码繁琐):
go
package main
import "fmt"
func main() {
// 多次计算两个数的和,重复编写求和逻辑
a1, b1 := 10, 20
sum1 := a1 + b1
fmt.Printf("%d + %d = %d\n", a1, b1, sum1)
a2, b2 := 30, 40
sum2 := a2 + b2
fmt.Printf("%d + %d = %d\n", a2, b2, sum2)
a3, b3 := 50, 60
sum3 := a3 + b3
fmt.Printf("%d + %d = %d\n", a3, b3, sum3)
}
用函数优化后(代码简洁,可重复调用):
go
package main
import "fmt"
// 定义求和函数(工具)
func add(a int, b int) int {
return a + b
}
func main() {
// 多次调用求和函数,无需重复编写求和逻辑
fmt.Printf("10 + 20 = %d\n", add(10, 20))
fmt.Printf("30 + 40 = %d\n", add(30, 40))
fmt.Printf("50 + 60 = %d\n", add(50, 60))
}
对比可见,函数能极大简化代码,提升复用性,这就是函数的核心价值[superscript:1]。
二、函数核心概念(新手必懂)
在学习函数的定义和用法前,先搞懂3个核心概念,避免后续混淆:
-
函数定义:就是"创建工具"的过程,明确函数的名称、功能、输入(参数)和输出(返回值),告诉程序这个工具能做什么、需要什么输入、会给出什么输出[superscript:4]。
-
函数调用:就是"使用工具"的过程,通过函数名+参数,触发函数执行,获取函数的输出结果(如果有返回值)[superscript:1]。
-
函数签名:由函数名和参数列表组成,用于区分不同的函数,Go 语言不允许函数重载(即不允许有相同签名的函数)[superscript:1]。
补充:Go 语言中的函数分为3种:普通函数(我们本篇重点学习)、匿名函数、方法(定义在结构体上的函数),后续会逐步讲解后两种[superscript:1]。
三、函数的定义(重中之重,语法规范)
Go 语言函数的定义有严格的语法规范,必须遵循固定格式,否则会编译报错。核心格式包含:关键字、函数名、参数列表、返回值类型、函数体、return 语句(如有返回值)[superscript:3]。
1. 基础定义语法
go
func 函数名(参数列表) (返回值类型列表) {
// 函数体:实现具体功能的代码
return 返回值列表 // 如有返回值,必须有 return 语句;无返回值可省略
}
各部分详细说明(通俗好记,必看):
-
func:定义函数的关键字,固定开头,不能省略(类似定义变量的var)[superscript:4]。 -
函数名 :自定义名称,遵循 Go 命名规范:由字母、数字、下划线组成,不能以数字开头;首字母大写表示公开(可在其他包调用),首字母小写表示私有(仅在当前包调用)[superscript:2]。建议命名见名知意(如求和函数叫
add,求最大值函数叫max)。 -
参数列表 :可选,用于接收外部传入的数据,格式为
参数名1 类型1, 参数名2 类型2, \.\.\.;如果没有参数,括号内留空即可[superscript:3]。 -
返回值类型列表:可选,用于指定函数执行完成后返回的数据类型;如果没有返回值,可省略括号和类型;如果有多个返回值,需要用括号包裹,用逗号分隔[superscript:4]。
-
函数体 :用
\{\}包裹,是函数的核心逻辑,实现具体功能;左大括号\{必须和函数定义的最后一部分在同一行(和 if、for、switch 语法要求一致),否则编译报错[superscript:2]。 -
return:如果函数有返回值,必须用return语句返回对应类型的值;如果没有返回值,return可省略,或单独写return(用于提前终止函数)[superscript:1]。
2. 4种常用函数定义场景(覆盖新手所有基础需求)
根据"有无参数""有无返回值",函数主要分为4种场景,我们逐一讲解,结合示例掌握,重点关注语法细节。
场景1:无参无返回值(最简单,仅执行任务)
场景:不需要接收外部数据,也不需要返回结果,仅执行一段固定逻辑(如打印问候语、打印日志)[superscript:3]。
go
package main
import "fmt"
// 定义无参无返回值函数:打印问候语
func sayHello() { // 无参数(括号空),无返回值(省略返回值类型)
fmt.Println("Hello, Go 函数!")
}
func main() {
// 调用函数:函数名 + ()
sayHello()
sayHello() // 可重复调用
}
运行结果:
Plain
Hello, Go 函数!
Hello, Go 函数!
场景2:有参无返回值(接收外部数据,执行任务)
场景:需要接收外部传入的数据,执行对应逻辑,但不需要返回结果(如根据传入的姓名打印问候语、根据传入的数字打印信息)[superscript:4]。
go
package main
import "fmt"
// 定义有参无返回值函数:接收姓名,打印问候语
// 参数列表:name string(name是参数名,string是参数类型)
func greet(name string) {
fmt.Printf("你好,%s!欢迎学习Go函数~\n", name)
}
func main() {
// 调用函数:传入参数(参数值的类型必须和函数定义的参数类型一致)
greet("小明")
greet("小红")
// 错误示例:greet(123) // 编译报错,参数类型不匹配(需要string,传入了int)
}
运行结果:
Plain
你好,小明!欢迎学习Go函数~
你好,小红!欢迎学习Go函数~
场景3:无参有返回值(不接收数据,返回固定结果)
场景:不需要接收外部数据,执行逻辑后返回一个或多个结果(如返回固定的数字、返回当前时间等)[superscript:2]。
go
package main
import "fmt"
// 定义无参有返回值函数:返回一个随机的整数(简化示例)
// 返回值类型:int(用括号包裹,单个返回值可省略括号,但建议加上,规范)
func getRandomNum() (int) {
return 666 // 返回值,类型必须和定义的返回值类型一致
}
func main() {
// 调用函数:接收返回值,赋值给变量
num := getRandomNum()
fmt.Printf("获取到的随机数:%d\n", num)
// 也可以直接使用返回值,不赋值
fmt.Printf("再次获取随机数:%d\n", getRandomNum())
}
运行结果:
Plain
获取到的随机数:666
再次获取随机数:666
场景4:有参有返回值(最常用,接收数据,返回结果)
场景:接收外部传入的数据,执行逻辑后返回结果,是开发中最常用的函数类型(如求和、求最大值、判断成绩等级等)[superscript:1]。
go
package main
import "fmt"
// 定义有参有返回值函数:接收两个int类型参数,返回它们的和(int类型)
func add(a int, b int) (int) {
sum := a + b
return sum // 返回计算结果,类型必须是int
}
// 简化写法:直接返回表达式结果,无需定义临时变量
func add2(a int, b int) int { // 单个返回值可省略括号
return a + b
}
func main() {
fmt.Printf("10 + 20 = %d\n", add(10, 20))
fmt.Printf("30 + 40 = %d\n", add2(30, 40))
}
运行结果:
Plain
10 + 20 = 30
30 + 40 = 70
小贴士:多个连续参数类型相同时,可简化写法,比如 add\(a int, b int\) 可简化为 add\(a, b int\),代码更简洁[superscript:2]。
四、函数参数详解(核心用法,避免踩坑)
参数是函数接收外部数据的"入口",新手容易在参数的写法、传递方式上出错,我们重点讲解参数的3个核心知识点:参数的简化写法、参数传递方式、可变参数[superscript:4]。
1. 参数的简化写法(同类型参数合并)
当函数的多个参数类型相同时,无需重复写每个参数的类型,只需在最后一个参数后写一次类型,这是 Go 语言的语法优化,提升代码简洁度[superscript:3]。
go
package main
import "fmt"
// 传统写法:每个参数都写类型
func add(a int, b int, c int) int {
return a + b + c
}
// 简化写法:同类型参数合并,只写一次类型
func add2(a, b, c int) int { // a、b、c 都是int类型
return a + b + c
}
// 不同类型参数,不能合并
func showInfo(name string, age int) {
fmt.Printf("姓名:%s,年龄:%d\n", name, age)
}
func main() {
fmt.Println(add(1, 2, 3)) // 输出6
fmt.Println(add2(1, 2, 3)) // 输出6
showInfo("小明", 18) // 输出:姓名:小明,年龄:18
}
2. 参数传递方式(值传递,Go 唯一传递方式)
Go 语言中,函数参数的传递方式只有一种:值传递,即传递的是参数的"副本",而不是参数本身[superscript:1]。
通俗理解:把参数的值复制一份,传入函数内部;函数内部修改这个副本的值,不会影响外部原始参数的值(就像复印一份文件,修改复印件,不会改变原件)[superscript:4]。
go
package main
import "fmt"
// 定义函数:修改参数的值
func changeNum(num int) {
num = 100 // 修改的是参数副本的值
fmt.Printf("函数内部num的值:%d\n", num)
}
func main() {
var num = 10 // 外部原始参数
fmt.Printf("函数调用前num的值:%d\n", num)
changeNum(num) // 传递num的副本
fmt.Printf("函数调用后num的值:%d\n", num) // 原始参数值不变
}
运行结果:
Plain
函数调用前num的值:10
函数内部num的值:100
函数调用后num的值:10
重点提醒:值传递是 Go 函数参数传递的唯一方式,无论参数是基本类型(int、string、bool 等)还是复合类型,传递的都是副本;后续学习切片、指针后,会有类似"引用传递"的效果,但本质还是值传递[superscript:4]。
3. 可变参数(接收任意数量的同类型参数)
场景:不确定需要接收多少个参数(如求和函数,可能需要求2个、3个甚至更多数字的和),此时可以使用可变参数,灵活接收任意数量的同类型参数[superscript:2]。
语法格式:
go
func 函数名(参数名 ...类型) 返回值类型 {
// 函数内部,可变参数会被当作切片(slice)处理
}
关键注意点:
-
可变参数的格式是
参数名 \.\.\.类型,\.\.\.是固定语法,不能省略[superscript:4]。 -
一个函数只能有一个可变参数,且必须放在参数列表的最后一位(否则编译报错)。
-
函数内部,可变参数会被当作切片处理,可以用 for range 遍历[superscript:2]。
go
package main
import "fmt"
// 定义可变参数函数:求任意数量int类型数字的和
func sumAll(nums ...int) int {
total := 0
// 可变参数nums是切片,用for range遍历
for _, num := range nums {
total += num
}
return total
}
func main() {
fmt.Println(sumAll(1, 2)) // 传入2个参数,输出3
fmt.Println(sumAll(1, 2, 3, 4)) // 传入4个参数,输出10
fmt.Println(sumAll()) // 传入0个参数,输出0(合法)
// 传递切片作为可变参数:在切片后加 ...
arr := []int{10, 20, 30}
fmt.Println(sumAll(arr...)) // 输出60
}
运行结果:
Plain
3
10
0
60
五、函数返回值详解(核心用法,多返回值是特色)
返回值是函数执行完成后,向外部返回的结果,Go 语言的返回值支持单返回值、多返回值、命名返回值,其中多返回值是 Go 的特色用法,常用于错误处理[superscript:3]。
1. 单返回值(最基础)
场景:函数执行后只返回一个结果,语法简单,返回值类型可加括号(推荐)或不加,return 语句返回一个对应类型的值[superscript:4]。
go
package main
import "fmt"
// 方式1:返回值类型加括号(推荐,规范)
func max(a, b int) (int) {
if a > b {
return a
}
return b
}
// 方式2:返回值类型不加括号(简洁,单个返回值可用)
func min(a, b int) int {
if a < b {
return a
}
return b
}
func main() {
fmt.Printf("10和20的最大值:%d\n", max(10, 20))
fmt.Printf("10和20的最小值:%d\n", min(10, 20))
}
运行结果:
Plain
10和20的最大值:20
10和20的最小值:10
2. 多返回值(Go 特色,重点掌握)
场景:函数执行后需要返回多个结果(如返回计算结果和错误信息、返回总和和平均值等),Go 语言原生支持多返回值,无需额外处理,语法简洁[superscript:3]。
语法格式:
go
func 函数名(参数列表) (返回值1类型, 返回值2类型, ...) {
// 函数体
return 返回值1, 返回值2, ... // 多个返回值用逗号分隔
}
关键注意点:
-
多返回值必须用括号包裹,每个返回值类型用逗号分隔[superscript:4]。
-
return 语句后必须跟上所有返回值,数量和类型必须和定义的一致,否则编译报错。
-
调用多返回值函数时,可以接收所有返回值,也可以用
\_忽略不关心的返回值(\_是 Go 中的空白标识符,用于忽略不需要的变量)[superscript:3]。
go
package main
import "fmt"
// 定义多返回值函数:接收切片,返回总和和平均值
func calculate(nums []int) (sum int, average float64) {
sum = 0
for _, num := range nums {
sum += num
}
// 计算平均值(避免除以0)
if len(nums) > 0 {
average = float64(sum) / float64(len(nums))
}
return sum, average // 返回两个值,顺序和定义一致
}
func main() {
data := []int{10, 20, 30, 40, 50}
// 方式1:接收所有返回值
total, avg := calculate(data)
fmt.Printf("总和:%d,平均值:%.2f\n", total, avg)
// 方式2:忽略平均值(用 _ 忽略)
onlySum, _ := calculate(data)
fmt.Printf("只获取总和:%d\n", onlySum)
}
运行结果:
Plain
总和:150,平均值:30.00
只获取总和:150
小贴士:多返回值在 Go 中非常常用,尤其是错误处理场景,通常会将"结果"作为第一个返回值,"错误信息"作为第二个返回值(后续会详细讲解)[superscript:4]。
3. 命名返回值(简化代码,提升可读性)
Go 语言允许为返回值命名,命名后的返回值会在函数体内部被自动初始化为对应类型的零值(如 int 为 0,float64 为 0.0),可以直接使用,无需再次声明[superscript:1]。
命名返回值的核心优势:函数逻辑较长时,能清晰区分每个返回值的含义,同时可以使用"裸返回"(return 后不写返回值,自动返回命名返回值的当前值)[superscript:3]。
语法格式:
go
func 函数名(参数列表) (返回值名1 类型1, 返回值名2 类型2, ...) {
// 直接使用命名返回值,无需声明
返回值名1 = 具体值
返回值名2 = 具体值
return // 裸返回,自动返回所有命名返回值
}
go
package main
import "fmt"
// 命名返回值:sum(总和)、avg(平均值)
func calculate(nums []int) (sum int, avg float64) {
// 直接使用命名返回值,无需声明 sum 和 avg
for _, num := range nums {
sum += num // sum 是命名返回值,初始值为0
}
if len(nums) > 0 {
avg = float64(sum) / float64(len(nums)) // avg 初始值为0.0
}
return // 裸返回,等价于 return sum, avg
}
func main() {
data := []int{5, 10, 15, 20}
total, average := calculate(data)
fmt.Printf("总和:%d,平均值:%.2f\n", total, average)
}
运行结果:
Plain
总和:50,平均值:12.50
重点提醒:命名返回值的名称不能和函数参数名称重复,否则会报错"变量重复定义";裸返回虽然简洁,但函数逻辑较长时,不建议使用,避免可读性变差[superscript:1]。
六、综合实战案例(结合之前知识点)
结合「函数定义、参数、返回值」和前文的「for 循环、switch、if else」,实现一个"学生成绩管理"完整程序,巩固本篇所有知识点,同时衔接前文内容。
需求:
-
定义3个函数:
-
inputScore\(\):无参,返回一个 int 类型的成绩(接收用户输入,确保成绩在 0-100 之间); -
getLevel\(score int\):接收一个 int 类型成绩,返回一个 string 类型的成绩等级(优秀≥90、良好80-89、及格60-79、不及格<60); -
calculateAvg\(scores \.\.\.int\):接收可变参数(多个成绩),返回总和(int)和平均分(float64)。
-
-
在 main 函数中,用 for 循环录入3个学生的成绩;
-
调用函数,获取每个学生的成绩等级,以及所有学生的总和和平均分;
-
输出所有学生的成绩、等级,以及总和、平均分。
完整代码:
go
package main
import "fmt"
// 1. 输入成绩函数:无参,返回合法成绩(0-100)
func inputScore() int {
var score int
for { // 无限循环,直到输入合法成绩
fmt.Print("请输入学生成绩(0-100):")
fmt.Scan(&score)
if score >= 0 && score <= 100 {
break // 成绩合法,终止循环
}
fmt.Println("成绩无效,请重新输入!")
}
return score
}
// 2. 成绩分级函数:接收成绩,返回等级
func getLevel(score int) string {
switch {
case score >= 90:
return "优秀"
case score >= 80:
return "良好"
case score >= 60:
return "及格"
default:
return "不及格"
}
}
// 3. 计算总和和平均分函数:可变参数,多返回值
func calculateAvg(scores ...int) (sum int, avg float64) {
for _, s := range scores {
sum += s
}
if len(scores) > 0 {
avg = float64(sum) / float64(len(scores))
}
return // 裸返回
}
func main() {
var scores []int // 存储3个学生的成绩
// 循环录入3个学生成绩
for i := 1; i <= 3; i++ {
fmt.Printf("------------------- 第%d个学生 -------------------\n", i)
score := inputScore()
scores = append(scores, score) // 将成绩添加到切片中
level := getLevel(score)
fmt.Printf("成绩:%d 分,等级:%s\n", score, level)
}
// 计算总和和平均分
total, average := calculateAvg(scores...)
fmt.Println("------------------- 成绩统计 -------------------")
fmt.Printf("3个学生总成绩:%d 分\n", total)
fmt.Printf("3个学生平均分:%.1f 分\n", average)
}
运行结果示例:
Plain
------------------- 第1个学生 -------------------
请输入学生成绩(0-100):95
成绩:95 分,等级:优秀
------------------- 第2个学生 -------------------
请输入学生成绩(0-100):78
成绩:78 分,等级:及格
------------------- 第3个学生 -------------------
请输入学生成绩(0-100):85
成绩:85 分,等级:良好
------------------- 成绩统计 -------------------
3个学生总成绩:258 分
3个学生平均分:86.0 分
七、新手常见错误(避坑指南)
-
函数嵌套定义错误 :Go 语言不允许在一个函数内部定义另一个普通函数(只能嵌套匿名函数),比如在 main 函数内部定义 add 函数,会编译报错[superscript:1]。
错误示例:
func main\(\) \{ // 错误:不能在main函数内部定义普通函数 func add\(a, b int\) int \{ return a \+ b \} \} -
大括号位置错误 :函数定义后,左大括号
\{没有和函数定义的最后一部分在同一行,导致编译报错(和 if、for、switch 语法要求一致)[superscript:2]。 -
参数类型不匹配:调用函数时,传入的参数类型和函数定义的参数类型不一致,编译报错(比如函数参数是 string 类型,传入 int 类型)[superscript:4]。
-
返回值数量/类型不匹配:return 语句返回的数量、类型,和函数定义的返回值数量、类型不一致,编译报错(比如函数定义返回2个 int 类型,return 只返回1个)[superscript:1]。
-
可变参数位置错误 :可变参数没有放在参数列表的最后一位,编译报错(比如
func sum\(nums \.\.\.int, a int\)是错误的)[superscript:4]。 -
命名返回值与参数重名:命名返回值的名称和函数参数名称重复,导致报错"变量重复定义"[superscript:1]。
-
忽略返回值时使用错误 :调用多返回值函数时,不能只接收部分返回值而不忽略其他返回值(比如
total := calculateAvg\(data\)会报错,必须用total, \_ := calculateAvg\(data\))[superscript:3]。 -
函数名重复(函数重载):定义两个名称相同、参数列表不同的函数,编译报错(Go 不允许函数重载)[superscript:1]。
八、本篇总结
-
核心作用 :函数是封装特定功能的代码块,核心价值是代码复用、简化逻辑、提升可维护性,是 Go 语言模块化编程的基础[superscript:4]。
-
函数定义 :固定格式
func 函数名\(参数列表\) \(返回值类型列表\) \{ 函数体 \},必须遵循语法规范,左大括号不能换行,函数不能嵌套定义[superscript:3]。 -
参数用法:
-
同类型参数可合并简化写法;
-
Go 只有值传递,传递的是参数副本,不影响外部原始值;
-
可变参数用
\.\.\.类型表示,只能有一个,且在参数列表最后,内部当作切片处理。
-
-
返回值用法:
-
单返回值:可加括号或不加,return 返回对应类型值;
-
多返回值:用括号包裹,return 需返回所有值,可用
\_忽略不关心的返回值(Go 特色); -
命名返回值:可直接使用,支持裸返回,提升可读性,但避免与参数重名。
-
-
实战技巧:结合 for 循环、switch、if else 等知识点,将复杂逻辑拆分为多个函数,实现"模块化编程";函数命名见名知意,参数和返回值类型清晰,提升代码可读性[superscript:2]。
本篇我们掌握了函数的基础用法,下一篇我们将学习函数的进阶内容------匿名函数、闭包、递归函数,进一步发挥函数的灵活性,解锁更多实用场景,继续进阶!
(注:文档部分内容可能由 AI 生成)