引入
为什么使用函数?
提高代码复用型,减少代码冗余,提高代码维护性
函数定义?
为完成某一功能的程序指令的集合
对特定的功能进行提取,形成一个代码片段,这个代码片段就是我们所说的函数
基本语法
func 函数名 (形参列表)(返回值类型列表){
执行语句
return+返回值列表
}
如果返回值类型只有一个的话可以省略括号
函数与函数是并列关系,所以我们定义的函数不能写在main函数的里面
//编写一个应用于计算求和的函数
package main
import "fmt"
func main() {
sum := cal(10, 30) //调用函数
fmt.Println(sum)
num1 := 100
num2 := 300
sum1 := cal(num1, num2) //调用函数
fmt.Println(sum1)
}
func cal(num1 int, num2 int) int {
var sum int = 0
sum += num1
sum += num2
//sum=num1+num2
return sum
}
详细
函数名
遵循标识符命名规范
见名知意、驼峰式命名addNum
首字母不能是数字
首字母大写的函数可以被本包文件和其他包文件使用(类似public)
首字母小写的函数只能被本包使用,其他包不能使用(类似private)
形参列表
1、个数:可以是一个参数,也可以是多个参数,也可以是零个参数
2、形式参数列表:可以用于接收外来数据(如num1,num2)
3、实际参数:实际传入的数据(如100,300)
返回值类型列表
函数的返回值对应的类型应该写在列表中
func cal1(num1 int, num2 int) {
//没有返回值
var sum int = 0
sum += num1
sum += num2
//return sum
fmt.Println(sum)
}
func cal2(num1 int, num2 int) int {
//有一个返回值,可以省略括号
var sum int = 0
sum += num1
sum += num2
//sum=num1+num2
return sum
}
func cal3(num1 int, num2 int) (int,int) {
//有两个返回值,不可以省略括号
var sum int = 0
sum += num1
sum += num2
//sum=num1+num2
var result int = num1-num2
return sum,result
}
package main
import "fmt"
//自定义函数:功能:交换两个数
func exchangeNum(num1 int, num2 int) {
var t int
t = num2
num2 = num1
num1 = t
}
func main() {
//调用函数:交换10和20
var num1 int = 10
var num2 int = 20
fmt.Printf("交换前的两个数: num1 = %v,num2 = %v \n", num1, num2)
exchangeNum(num1, num2)
fmt.Printf("交换后的两个数: num1 = %v,num2 = %v \n", num1, num2)
}
但这个输出不对,int是按照值来赋值的。
在函数exchangeNum中确实交换了num1和num2的值,但无法影响main函数中num1和num2的值,所以main中的值不变。
exchangeNum中的num1和num2和main中的num1和num2完全没有关系。
除非向exchangeNum中传入main的num1和num2的指针,这样exchangeNum才能操作main中num1和num2的值。
因此用指针解决该问题
package main
import "fmt"
func main() {
//调用函数:交换10和20
var num1 int = 10
var num2 int = 20
fmt.Printf("交换前的两个数: num1 = %v,num2 = %v \n", num1, num2)
exchangeNum(&num1, &num2) //取地址传入exchangeNum中
fmt.Printf("交换后的两个数: num1 = %v,num2 = %v \n", num1, num2)
}
//自定义函数:功能:交换两个数
func exchangeNum(num1 *int, num2 *int) {
//靠地址找到对应的数值并进行更改
var t int
t = *num1
*num1 = *num2
*num2 = t
}
虽然函数的编写顺序是无关紧要的,但鉴于可读性的需求最好把main()函数写在文件的最前面,其他函数按照一定逻辑顺序进行排序(例如按照函数被调用的顺序进行编写)
同一个代码可以被调用多次
当代码执行到最后一行或者return语句时会退出
return语句后可以带零个或者多个参数,这些参数将作为返回值供调用者使用。简单的return语句也可以用于退出死循环,或者结束一个协程(goroutine(后续学习))
func g(){
}//中括号的格式只能长成这个样子
pack1.Function(arg1,arg2,....,argn)
Function是pack1包中的一个函数,括号里是被调用的实参,这些值被传递给被调用函数的形参。简单而言在函数被调用时这些实参将会被复制然后传递给被调用的函数。
函数一般是在其他函数里被调用,这个其他函数被称作调用函数
理论上来讲函数能多次调用其他函数,这些函数按顺序执行
理论上,函数调用其他函数的次数是无穷的,直到函数调用栈被用尽
函数重载(function overloading)指的是可以编写多个同名函数,只要它们拥有不同的形参与/或者不同的返回值,在 Go 里面函数重载是不被允许的。
函数为数据类型
在go语言中函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量了。通过该变量可以对函数进行调用。
package main
import "fmt"
//定义一个函数,作用是打印传过来的值
func test(num int) {
fmt.Println(num)
}
func main() {
//函数也是一个数据类型,可以赋值给一个变量
a := test
fmt.Printf("a的类型是:%T,test函数的数据类型是:%T\n", a, test)
//通过该变量可以对函数进行调用
a(10) //等价于test(10)
}
由于函数是一个数据类型,因此在go语言中函数可以作为形参,并调用
package main
import "fmt"
//定义一个函数,作用是打印传过来的值
func test(num int) {
fmt.Println(num)
}
//定义一个函数,形参列表为:整型、浮点型、test函数
func test02(num1 int, num2 float32, testFunc func(int)) {
fmt.Println("--------test02")
}
func main() {
//函数也是一个数据类型,可以赋值给一个变量
a := test
fmt.Printf("a的类型是:%T,test函数的数据类型是:%T\n", a, test)
//通过该变量可以对函数进行调用
a(10) //等价于test(10)
//调用test02函数
test02(1, 4.5, test)
test02(87, 0.99, a)
}
自定义数据类型
为了简化数据类型定义,go支持自定义数据类型
基本语法:type 自定义数据类型名称 数据类型
相当于给数据类型起了一个小名
type myInt int //定义数据类型命名为myInt
var num1 myInt = 199定义一个myInt数据类型num1
fmt.Println(num1)
var num2 int = 30
fmt.Println(num2)
num2 = int(num1)
//虽然myInt是int的别名,但是在go语言中编译时还是认为int和myInt不是同一个数据类型
fmt.Println(num2)
支持对函数返回值进行命名
传统写法:返回值和返回值的类型对应,顺序不能出差错
升级写法:对返回值进行命名,这样顺序就不用一一对应了
func test03(num1 int,num2 int)(int,int){
result01 := num1+num2
result02 := num1-num2
return result01,result02
}
func test03(num1 int,num2 int)(sum int,sub int){
sum := num1+num2
sub := num1-num2
return
}