基本概念:
为完成某一功能的程序指令(语句)的集合,称为函数,在Go中,函数分为:自定义函数,系统函数;
复用,维护性强;
基本语法:
func 函数名 (形参列表)(返回值列表){
执行语句
return 返回值列表
}
1.形参列表:表示函数的输入;
2.函数中的语句:表示为了实现某一功能代码块
3.函数可以有返回值,也可以没有;
函数的调用机制:
代码执行需要加载到内存中,所以一般再内存会自动分配:
栈区:基本数据类型一般说分配到栈区,编译器存在一个逃逸分析也有肯能不在;
堆区:引用数据类型一般说分配到堆区,编译器存在一个逃逸分析;
代码区:专门放代码的
1.在调用一个函数时,会给该函数分配一个新的空间,编译器会通过自身的处理让这个新的空间和其他的栈的空间区分开来;
2.在每个函数对应的栈中,数据空间时分开的,不会混淆;
3.当一个函数调用完毕(执行完毕)后,程序会销毁这个函数对应的栈空间;
函数递归调用:
一个函数在函数体内又调用了本身,我们称为递归调用;
递归调用需要遵守的重要原则:
1.执行一个函数时,就创建一个新的受保护的独立空间(名字虽一样但是会开辟新的函数栈);
2.函数的局部变量是独立的,不会相互影响;
3.递归必须向退出递归的条件逼近,否则就是无限递归,死龟了;
4.当一个函数执行完毕,或者遇到return,就会返回,遵守谁调用,就将结果返回给谁,同时当函数执行完毕或者返回时,该函数本身也会被系统销毁;
注意事项和细节:
1.函数中的变量时局部的,函数外不生效;
2.基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内部修改,不会影响到原来的值;
3.如果希望函数内的变量能修改函数外的变量,可以传入变量的地址&,函数内以指针的方式操作变量。从效果上看类似应用
例如: var num int = 20
fmt.Printf("num 的地址==%v", &num) ---> 0xc0000a0090
使用指针类型来接受这个地址:
var nl *int
nl = &num
fmt.Printf("nl的地址:%v 和 nl的值=%v", &nl, *nl ) --> nl的地址:0xc000006030 nl的值=30
// 此时的 *nl 就是拿到的num的 值 30; 而 nl的值就是 num的地址
*nl += 10
fmt.Printf("num的值==%v nl的值--%v *nl==%v", num, nl, *nl) --> num的值==40 nl的值--0xc0000140b8 *nl==40
4.Go函数不支持重载;即名字相同的函数不能重复定义,即使传参返回值不同;
5.在Go中,函数也是一种数据类型,可以赋值给一个变量,则该变量就是一个函数类型的变量,通过该变量可以对函数调用;
6.函数既然是一种数据类型,因此在Go中,函数可以作为形参,并且调用;
7.为了简化数据类型定义,Go支持自定义数据类型;
基本语法:
type 自定义数据类型名 数据类型 // 相当于一个别名
例如:
type myInt int // 这时myInt 就等价于 int来使用了
type mySum func(int, int) int // 这时 mySum 就等价一个函数类型 func(int, int) int
8.支持对函数返回值命名;
例如:
func getRes(num1 int, num2 int) (sum int, sub int) {
sum = num1 + num2
sub = num2 - num1
return
}
9.Go中支持可变参数;
func sum(args...Int) sum Int {
}
func sum(n1 Int, args...Int) sum Int {
}
1.args 是slice切片,通过args[index] 可以访问到各个值;
2.如果一个函数的形参列表中有可变参数,则可变参数需要放在形参列表的最后;
示例:
// 支持多个参数 可变参数的演示
func allSum(n1 int, args...int) int {
total := n1
for i:=0; i < len(args); i++ {
total += args[i]
}
return total
}