函数
现代计算机的进程执行模型大部分是基于“堆栈”的,编译器不需要对函数做过多的转换就能让其在栈上运行
2.1 基本概念
2.1.1 函数定义
一个函数的定义包含如下几个部分: 函数声明关键字func、函数名、参数列表、返回列表和函数体。
首字母的大小写决定该函数在其他包的可见:大写时其他包可见,小写时只有相同的包可以访问。
-
函数可以没有输入参数、也可以没有返回值(默认返回0)
1func A(){ 2} 3 4func B() int{ 5 return 1 6}
-
多个相同类型的参数可以使用简写模式
1func add(a,b int) int{ // a int, b int 简写为 a,b int 2 return a+b 3}
-
支持有名的返回值,参数名相当于函数体最外层的局部变量,命名返回值变量会被初始化为类型零值最后的return可以不带参数名直接返回。
1func add(a,b int) (sum int){ // sum 相当于函数内部的局部变量,被初始化为0 2 sum = a+b 3 return // return sum 的简写模式 4 5 // sum := a+b 6 // return sum // 需要显式的调用return sum 7}
-
不支持默认值参数??
-
不支持函数重载
-
不支持函数嵌套,严格的说不支持命名函数的嵌套定义,但支持匿名函数
1func add(a, b int) (sum int){ 2 anonymous := func(x,y int) int{ 3 return x+y 4 } 5 6 return anonymous(a,b) 7}
2.1.2 多值返回
定义多值返回的返回参数列表时候要使用“()”,支持命名参数的返回
1func swap(a,b int) (int, int){
2 return b,a
3}
习惯用法:
如果多值返回有错误类型,则一般将错误类型作为最后一个返回值
2.1.2 实参到形参的传递
Go 函数实参到形参的传递永远是值拷贝,有时候函数调用后,实参指向的值发生了变化,那是因为参数传递的是指针值的拷贝,实参是一个指针变量,传递给形参的是这个指针变量的副本,二者指向同一个地址,本质上参数传递仍然是值拷贝。
1package main
2
3import "fmt"
4
5func chvalue(a int) int { // 实参传递给形参的是值拷贝
6 a = a + 1
7 return a
8}
9
10func chpointer(a *int) { // 实参传递给形参的仍然是只拷贝,只不过复制的是a的地址
11 *a = *a + 1
12 return
13}
14func main() {
15 a := 10
16
17 chvalue(a)
18 fmt.Println(a)
19
20 chpointer(&a)
21 fmt.Println(a)
22}
2.1.4 不定参数
Go 函数支持不定数目的形式参数,不定参数声明使用param …type的语法格式
-
所有的不定参数类型必须是相同的
-
不定参数必须是函数的最后一个参数
-
不定参数名在函数体重相当于切片,对切片的操作同样适合不定参数的操作
1func sum(arr ...int)(sum int){ 2 for _,v := range arr{ // 此时arr 就相当于切片,可以使用range访问 3 sum +=v 4 } 5 return 6}
-
切片可以作为参数传递给不定参数,切片名后面要加上“…”
1func sum(arr ...int) (sum int){ 2 for _,v := range arr{ 3 sum += v 4 } 5 return 6} 7 8func main(){ 9 slice :=[]{1,2,3,4} 10 array := [...]{1,2,3,4} 11 // 数组不可以作为实参传递给不定参数的函数 12 sum(slice...) 13}
注意 slice 和 array定义的区别
-
形参为
不定参数
的函数和为切片
的函数类型不相同1func suma(arr ...int)(sum int){ 2 for _,v := range arr{ 3 sum +=v 4 } 5 return 6} 7 8func sumb(arr [4]int) (sum int) { 9 for _,v := range arr{ 10 sum +=v 11 } 12 return 13} 14 15func main() { 16 slice := []int{2,3,4,5} 17 array := [4]int{2,3,4,5} 18 19 println(suma(slice...)) 20 println(sumb(array)) 21} 22 23// suma 和sumb 不一样
2.2 函数签名和匿名函数
2.2.1 函数签名
函数类型又叫做函数签名,一个函数的类型就是函数定义首行去掉函数名,参数名和{,可以使用fmt.Printf
的%T
格式化参数打印函数的类型
1func add(a,b int )(sum int){
2 sum = a +b
3 return sum
4}
5
6func main() {
7 fmt.Printf(" func type is:%T",add ) // func type is:func(int, int) int
8}
两个函数类型相同的条件是: 拥有相同的形参列表和返回值列表(列表元素的次序,个数和类型都相同),形参名可以不同。一下两个函数类型是完全一样的
1func add(a,b int) int{ return a+b}
2func sub(x int, y int)(z int){z = x-y; return z}
可以使用type定义函数类型,函数类型变量可以作为函数的参数或返回值。
1package main
2
3import "fmt"
4
5
6func add(a,b int )(sum int){
7 sum = a +b
8 return sum
9}
10
11func sub(a,b int) int {
12 return b-a
13}
14
15type Op func(int, int) int // 定义一个函数类型,输入是两个int,返回值是一个int类型
16
17
18func do(f Op, a,b int) int{ // 定义一个函数,第一个参数是函数类型Op
19 return f(a,b) // 函数类型变量可以直接用来进行函数调用
20}
21
22
23func main() {
24 a := do(add, 1,2)
25 fmt.Println(a) // 3
26
27 s := do(sub, 1,2) // 1
28 fmt.Println(s)
29}
函数类型和map、slice、chan一样,实际函数类型变量和函数名都可以当做指针变量,该指针指向函数代码的开始位置
。通常说函数变量类型是一种引用类型,未初始化的函数类型的变量默认是nil
。
Go 中函数是“第一公民(first class)”。有名函数的函数名可以看作函数类型的常量
,可以直接使用函数名调用函数。也可以直接赋值给函数类型变量,后续通过该变量来调用该函数。
1func sum(a,b int) int{
2 return a+b
3}
4func main(){
5 sum(3,4) // 直接调用
6 f := sum // 有名函数可以直接赋值给变量
7 f(1,2)
8}
2.2.2 匿名函数
https://zhuanlan.zhihu.com/p/71829933