函数
基本介绍
是为了完成一个功能的代码集合
系统已经有提供了一些函数,但我们也可以自己写
好处
- 方便重复使用,降低重复率和篇幅
- 可以使代码的维护性,可读性更高,更简洁
基本语法
func 函数名(形参列表) (返回值列表){
执行语句
return 返回值列表
}
- 形参列表在使用函数时加入
- 返回值可以有也可以没有
函数的调用机制
例子:罗斯福给美国军方下命令,向日本本土投掷原子弹,美国军方会去执行命令,然后向罗斯福返回一个结果
现实:程序员给函数提供输入,函数去运行,返回一个结果给程序员
程序运行的一个大致流程
分为
- 栈区(一般的基本数据类型会放在这里)
- 在调用函数时,编译器通常会给这个函数分配一个新的栈空间,并且会和其它栈空间区分开来
- 每个函数的栈空间都是独立的,不会混淆
- 当一个函数调用完毕后,程序会销毁这个函数的栈空间
- 堆区(引用数据类型一般会放在这里)
- 代码区(存放代码)
return语法
结构:
func 函数名(形参列表)(返回值列表){
语句
return (返回值列表)
}
注意事项:
- 如果返回值只有一个,返回值列表可以不加括号,但是多个一定要加
- 如果在接收返回值时,不需要某个返回值,可以使用_来接收
代码示例:
func getss(a, b int) (int, int) {
sum := a + b
sub := a - b
return sum, sub
}
func main() {
num1, num2 := getss(9, 1)
fmt.Printf(“%v,%v”, num1, num2)
}
函数的注意事项和细则,系统整理
- 函数的形参和返回值列表都是可以有多个的
- 形参列表和返回值列表的数据类型可以是值类型也可以是引用类型
- 函数的命名规范:遵循标识符的命名规则,首字母不能是数字,如果首字母是大写,代表这个函数可以被其它包文件和本包文件使用,就是public公开的意思;如果首字母是小写,代表只能被本包文件使用,不能被其它包文件使用,类似于private私有的
- 函数内部的变量为局部变量,对函数外的变量无影响
- 基本数据类型和数组都是采用值传递,即进行值拷贝。在函数内部修改,不会影响到原来的值(示例08-4)
func test(n int) {
n = n + 1
fmt.Println(n)
}
func main() {
var n = 10
fmt.Println(n)
test(n)
fmt.Println(n)
}
返回结果是
10
11
10 - 如果想在函数内修改函数外变量的值(以值传递的数据类型),可以通过向函数中传入该参数地址的方式,通过地址来修改值,类似引用
func test2(n1 *int) {
*n1 = *n1 + 10
}
func main() {
var n = 10
fmt.Println(n)
test2(&n)
fmt.Println(n)
} - go语言不支持函数的重载
重载的定义:在同一个包内,存在两个函数,函数名相同,参数列表不同(参数数量或者类型不同都可以),但是和返回值列表无关 - 函数也是一种数据类型,可以赋值给一个变量,那么该变量就是一个函数类型的变量,可以通过该变量对函数调用
代码:
func getsum(a, b int) int {
return a + b
}
func main() {
a := getsum
fmt.Printf(“a的类型%T,getsum的类型%T\n”, a, getsum)
res := a(1, 1)
fmt.Printf(“%v”, res)
}
输出结果为:
a的类型func(int, int) int,getsum的类型func(int, int) int
2
不难看出,a完全就是一个函数类型的参数 - 既然函数是一种数据类型,那么,函数就可以作为形参来调用
代码:
func getsum(a, b int) int {
return a + b
}
func myway(onefun func(int, int) int, num1 int, num2 int) int {
return onefun(num1, num2)
}
func main() {
a := getsum
fmt.Printf(“a的类型%T,getsum的类型%T\n”, a, getsum)
res := myway(getsum, 1, 1)
fmt.Printf(“%v”, res)
}
输出结果:
a的类型func(int, int) int,getsum的类型func(int, int) int
2
10. go支持自定义数据类型,用于简化数据类型定于(沿用c语言),但是比较奇怪的是,这个自定义的数据类型,并不是完全就等于原来的数据类型,还是看作两个不同的类型
基本语法:type 自定义数据类型名 数据类型 //可以当作是别名
案例:
type my int//此时my可以代替int
type my2 func(int, int) int//此时my2可以代替 func(int, int) int
代码示例:
func getsum(a, b int) int {
return a + b
}
func main() {
a := getsum
fmt.Printf(“a的类型%T,getsum的类型%T\n”, a, getsum)
res := myway(getsum, 1, 1)
fmt.Printf(“%v”, res)
type my int
var b my = 12
fmt.Printf(“b的类型%T\n”, b)
}
func myway(onefun my2, num1 int, num2 int) int {
return onefun(num1, num2)
}
type my2 func(int, int) int //此时my2可以代替 func(int, int) int
这样子的代码是可以的,带式要注意,这个这个声明语句要是全局的,放在全局的哪都无所谓,但是不能放在别处,哪怕在main函数中,放在比调用所用到函数之前也不行!!!
11. go语言中支持直接对返回值命名
代码样例:
func getsumandsub(a, b int) (sum, sub int) {
sum = a + b
sub = a - b
return
}
好处:当返回值类型不同时,可以不用在意return时的顺序
12. 使用_标识符可以忽略函数的返回值
13. 支持可变参数
代码示例:
func sum(num1 int, args …int) int {
sum := num1
for i := 0; i < len(args); i++ {
sum += args[i] //args[i]表示提取args切片的第i+1个位上的内容
}
return sum
}
func main() {
a := sum(1, 2, 13, 1, 0)
fmt.Println(a)
}
说明:
- 这个示例中,表示sum函数是一个参数可变的函数,它至少需要接收一个的int类型数据,因为它的参数列表中写到num1 int。如果没有num1 int,那么它就是一个可以接收0个参数的函数。因为args切片的范围是0-无穷
- 这个args只是切片名字,可以更改的
- 这个args切片这个可变参数,必须要放在参数列表的最后,不能放在num1 int之类的前面