01、函数定义 -- 有返回值
--golang是强类型的语言,因此传参和返回时都需要定义参数类型·
--定义示例:
func 函数名(参数)(返回值){
函数体
}
--代码示例如下:
func sum(x int, y int)(ret int){ // 返回值有返回名 和 返回类型
return x+y
}
func intSum(x int, y int) int { // 返回仅类型也可 [会有两个空格在返回值类型两边]
return x + y
}
func main(){
r := sum(1, 2)
fmt.Println(r)
}
02、函数定义 -- 无返回值
--代码示例如下:
func sum(x int, y int){ // 无返回值时,只需要一个小括号即可,不需要定义返回值
fmt.Println(x + y)
}
03、函数定义 -- 无参数 无返回值
--代码示例如下:
func sum(){ // 无返回值,无参数就没有必要定义参数和返回值
fmt.Println(1+2)
}
04、函数定义 -- 无参数 有返回值
--代码示例如下:
func sum() int { // 有返回值,就必须有对应的返回类型
return 1 + 2
}
05、函数返回值 -- 定义了返回值 和 返回类型,其实只写一个return 也是可以的,前提是代码中使用了定义的返回值
--代码示例如下:
func sum(x int, y int)(ret int){
ret = x + y // 这里 ret 相当于已经定义直接使用即可不要定义赋值
return // 使用 return ret 也可以
}
06、函数多个返回值
--代码示例如下:
func sum()(int, string){
return 1, "liming"
}
func main(){
_, n := sum()
fmt.Printin(n)
}
07、函数参数类型简写
--代码示例如下:
// 参数类型一致时简写:
// 当参数中连续多个参数类型一致时,我们可以将非最后一个参数的类型省略
func sum(x, y, z int, m, n string, i, j bool){
fmt.Pringln("hello a")
}
08、可变长参数:必须放在参数列表最后,且可变长参数列表在函数中是切片类型
--代码示例如下:
package main
// 元素类型为map的字典,即列表中含字典
import (
"fmt"
)
// 定义一个加法函数
func multiprint(x string, y ...int) {
fmt.Println(x) // liming
fmt.Println(y) // [1 2 3 4 5]
fmt.Printf("%T\n", y) // []int
}
func f7(x string, y ...int) {
fmt.Println(x) // liming
fmt.Println(y) // [1 2 3 4]
}
func main() {
multiprint("liming", 1, 2, 3, 4, 5)
f7("liming", 1, 2, 3, 4)
}
09、defer的定义和使用
--在golang中函数中不能嵌套函数 和 python不同
--defer定义:defer语句会将其后面跟随的语句进行延迟处理。在defer归属的函数即将返回时,将延迟处理的语句按defer定义的逆序进行执行,也就是说,先被defer的语句最后被执行,最后被defer的语句,最先被执行。
--defer语句能非常方便的处理资源释放问题。比如:资源清理、文件关闭、解锁及记录时间等。这里将涉及到最基本的下面三个应用了:
1. 关闭文件句柄
2. 锁资源释放
3. 数据库连接释放
--这里defer类似于python中的__enter__ 和 __exit__上下文管理机制,但更加丰富。
--代码示例01:所以defer可以用来对需要处理,但容易忘记的操作提前定义好,避免出现漏操作
// 一般用在 文件资源释放 数据库连接 socket编程时
// 下面几个都是示例,自己没试编写一下练练手
func deferdemo() {
fmt.Println("start")
defer fmt.Println("嘿嘿嘿")
defer fmt.Println("哈哈哈")
fmt.Println("end")
}
--输出结果如下:
start
哈哈哈
嘿嘿嘿
heiheihei
--golang中函数的return操作不是原子操作,分为两步:
--01、返回值赋值
--02、将返回值真正传递出去
--defer执行时间,恰好在两者之间:
--01、返回值赋值
--02、执行defer函数
--03、将返回值真正传递出去
--代码示例01:
// 下面几个都是示例,自己没试编写一下练练手
func f1() {
// 01、这里的核心就是defer执行顺序,先将x结果赋值给返回值,即返回5
// 02、再对x进行+1操作,此时x变成6.但是已经不影响返回值了
// 03、将返回值真正返回
x := 5
defer func() {
x++ // 向外找,找到了x但是指向的表示return的那个x,所以也就没有指到返回值内存空间,因此无法修改。
}()
return x
}
func main() {
fmt.Println(f1)
}
--代码示例01打印结果:
-- 5
--代码示例结果02:
// 下面几个都是示例,自己没试编写一下练练手
func f2() (x int) {
// f1中,go返回值x和函数体中x其实不一样,分属于两片不同内存
// 函数体中x是局部的,返回值中x是要给全局的。所以不一样
// 函数定义时的返回()其实和return中返回的是同一片内存,属于外部的、全局的。所以f2就可以修改
defer func() {
x++ // 这里的x向外找找到了,x int 这是一个返回值的内存空间,对其操作影响了返回值
}()
return 5
}
func main() {
fmt.Println(f2())
}
--代码示例02打印结果:
-- 6
--代码示例结果03:
// 下面几个都是示例,自己没试编写一下练练手
func f3() (y int) {
x := 5
defer func() {
x++ // 由于没有传参,向外找找到了x,因此修改的是x。此时x已经被赋值了所以无法更改
}()
return x // 返回值 y=x=5
}
func main() {
fmt.Println(f3())
}
--代码示例03打印结果:
-- 5
--代码示例结果04:
func f4() (x int) {
defer func(x int) {
x++
}(x)
return 5
}
--代码示例04打印结果:
-- 5
10、作用域
--全局变量:定义在函数外部的变量,它在程序整个运行周期内都有效。 在函数中可以访问到全局变量。
--代码示例如下:
var num int = 100
func f1() {
// 函数中查找变量顺序:
// 01、先在函数内部查找
// 02、找不到就到函数外层查找,知道找到全局变量。
num := 15
fmt.Printf("%d\n", num)
}
func main() {
f1()
}
--局部变量:只在某一个代码块中生效
01、 函数内定义的变量无法在该函数外使用:
func f1() {
//定义一个函数局部变量x,仅在该函数内生效
var num int64 = 102
fmt.Printf("x=%d\n", num)
}
func main() {
f1()
fmt.Println(num) // 此时无法使用变量x,会报错
}
02、如果局部变量和全局变量重名,优先访问局部变量。
var num int = 100
func f1() {
num := 15
fmt.Printf("%d\n", num) // 优先打印num = 15
}
func main() {
f1()
}
--golang中有三种作用域:语句块作用域 函数作用域 全局作用域