Golang 学习笔记04-函数和指针

定义函数

关键字func

func sayHello(id int) {
	fmt.Println("hello world", id)
}

多个参数类型一致可以省略

func sayHello(id, age int) {
	fmt.Println("hello world", id)
}
func add(numberList ...int) {
	var sum int
	for _, number := range numberList {
		sum += number
	}
	fmt.Println(sum)
}
  • numberList: 可变参数,类型为 []int(整数切片)
  • range: Go 语言的关键字,用于遍历数组、切片、映射等数据结构
  • _: 空白标识符,用于忽略索引值(range 返回索引和值两个值)
  • number: 接收遍历到的每个元素值的变量名

函数返回值

和TS类似,括号外面写返回值类型

func add2() int {
	var sum int
	for i := 1; i <= 100; i++ {
		sum += i
	}
	return sum
}

func main() {
	var res = add2()
	fmt.Println(res)
}

返回多个值

返回值类型用括号括起来

func add3() (int, int) {
	var sum int
	for i := 1; i <= 100; i++ {
		sum += i
	}
	return sum, 100
}
func main() {
	var res2, res3 = add3()
	fmt.Println(res2, res3)
}

命名返回值

相当于先定义再赋值,返回值的类型前加上变量名

func add4() (sum int, age int) {
	for i := 1; i <= 100; i++ {
		sum += i
	}
	age = 18
	return
}
func main() {
	var res4, res5 = add4()
	fmt.Println(res4, res5)
}
5050 18

匿名函数

Go不能在函数体中定义函数,但是可以把函数赋值给变量,和js的箭头函数有点像

func main() {
	var getName = func() string {
		return "张三"
	}
	var name = getName()
	fmt.Println(name)
}

高阶函数

把一个函数作为参数传到另一个函数里面,或者把函数作为参数传到其他的数据类型里面去

例子:

根据用户的不同输入采取不同的操作

传统写法

func get() {
	fmt.Println("查询用户信息")
}

func addUser() {
	fmt.Println("添加用户信息")
}

func del() {
	fmt.Println("删除用户信息")
}

func main() {
	var id int
	fmt.Scan(&id)
	switch id {
	case 1:
		get()
	case 2:
		addUser()
	case 3:
		del()
	default:
		fmt.Println("输入错误")
	}
}

定义一个map,输入的id就是key,对应的value就是函数

var id int
fmt.Scan(&id)

var funMap = map[int]func(){
	1: get,
	2: addUser,
	3: del,
}
fun, ok := funMap[id]
if ok {
	fun()
}

闭包

设计一个函数,先传一个参数表示延时,后面再次传参数就是将参数求和

例如

fun(2)(1,2,3) // 延时2秒求1+2+3

写法一


package main

import (
  "fmt"
  "time"
)

func awaitAdd(awaitSecond int) func(...int) int {
	time.Sleep(time.Duration(awaitSecond) * time.Second)
	//	返回的是一个函数
	return func(nunberList ...int) int {
		var sum int
		for _, number := range nunberList {
			sum += number
		}
		return sum
	}
}

func main() {
	// 这里的timeRes是一个函数类型
	timeRes := awaitAdd(2)
	t1 := time.Now()
	sumRes := timeRes(1, 2, 3)
	sunTime := time.Since(t1)
	fmt.Println(sumRes, sunTime) //6 0s
}

写法二

func awaitAdd(awaitSecond int) func(...int) int {
	//time.Sleep(time.Duration(awaitSecond) * time.Second)
	//	返回的是一个函数
	return func(nunberList ...int) int {
		time.Sleep(time.Duration(awaitSecond) * time.Second)
		var sum int
		for _, number := range nunberList {
			sum += number
		}
		return sum
	}
}
func main() {
	// 这里的timeRes是一个函数类型
	timeRes := awaitAdd(2)
	t1 := time.Now()
	sumRes := timeRes(1, 2, 3)
	sunTime := time.Since(t1)
	fmt.Println(sumRes, sunTime) //6 0s
}

这时候内层函数捕获外层变量: 闭包捕获了外层函数的参数 awaitSecond

  • 内层匿名函数”记住”了外层的 awaitSecond 变量
  • 即使外层函数执行完毕,awaitSecond 的值仍被保留在闭包中

可以函数嵌套函数

值传递和引用传递

func copyName(name string) {
	fmt.Printf("%p\n", &name)
}
func main() {
	name1 := "张三"
	fmt.Printf("%p\n", &name1)
	copyName(name1)
}
0xc000022070
0xc000022080

可以看到两个变量的内存地址并不一样,函数内部的操作并不会影响到原本的变量

如果要在函数内部修改变量需要传递内存地址

func SetName(name *string) {
	fmt.Printf("%p\n", &name)
	//	通过内存地址去找这个值
	*name = "张三"
}
func main() {
	name1 := "李四"
	fmt.Printf("%p\n", &name1)
	//copyName(name1)
	SetName(&name1)
	fmt.Println(name1)
}
0xc000022070
0xc00005c040
张三

指针

&是取地址,*是解引用,去这个地址指向的值

init函数

init()函数是一个特殊的函数,存在以下特性:

  1. 不能被其他函数调用,而是在main函数执行之前,自动被调用
  2. init函数不能作为参数传入
  3. 不能有传入参数和返回值

一个go文件可以有多个init函数,谁在前面谁就先执行

package main

import "fmt"

func init() {
	fmt.Println("init1")
}
func init() {
	fmt.Println("init2")
}
func init() {
	fmt.Println("init3")
}
func main() {
	fmt.Println("main")
}
init1
init2
init3
main

可以用来初始化全局变量

package main

import "fmt"

var db int

func init() {
    db = 1
    fmt.Println("init1")
}
func init() {
    fmt.Println("init2")
}
func init() {
    fmt.Println("init3")
}
func main() {
    fmt.Println("main", db)
}
init1
init2
init3
main 1

初始化顺序

defer函数

  1. 关键字 defer 用于注册延迟调用
  2. 这些调用直到 return 前才被执。因此,可以用来做资源清理
  3. 多个defer语句,按先进后出的方式执行,谁离return近谁先执行
  4. defer语句中的变量,在defer声明时就决定了,即声明defer之前的变量
package main

import "fmt"

func main() {
	defer fmt.Println("defer1")
	defer fmt.Println("defer2")
	defer fmt.Println("defer3")
	return
}
defer3
defer2
defer1