1290 字
6 分钟
260714GO函数和指针
GO 函数与指针
一、函数基础
函数是可重复调用的代码块,用于拆分代码、提高复用性、增强可读性
1. 函数定义与调用
语法:func 函数名(参数列表) 返回类型 { 函数体 }
package mainimport "fmt"
// 有返回值函数:计算两数之和func add(a, b int) int { return a + b}
// 无返回值函数func greet(name string) { fmt.Println("你好," + name + "!")}
func main() { fmt.Println(add(3, 5)) // 调用函数 greet("小明")}func:函数关键字- 参数写法:可简写同类型参数
(a, b int) - 无返回值:不写返回类型即可
2. 多返回值
Go 支持函数返回多个值,常用于同时返回结果和错误信息
// 返回商、余数两个 int 值func divide(a, b int) (int, int) { return a / b, a % b}
func main() { q, r := divide(10, 3) fmt.Printf("商 = %d, 余数 = %d\n", q, r)
// 空白标识符 _ 忽略不需要的返回值,避免编译报错 _, remainder := divide(17, 5) fmt.Println("余数 =", remainder)
quotient, _ := divide(20, 7) fmt.Println("商 =", quotient)}_:空白标识符,用于丢弃不需要的返回值,解决未使用变量编译报错问题
3. 命名返回值
可直接为返回值命名,函数内可直接赋值,使用空 return 自动返回
func divide(a, b int) (quotient, remainder int) { quotient = a / b remainder = a % b return // 空 return,自动返回命名变量}
func main() { q, r := divide(10, 3) fmt.Printf("商 = %d, 余数 = %d\n", q, r)}二、指针
1. 指针基础
- 指针变量:存储另一个变量的内存地址
- 两个核心运算符:
&:取地址运算符,获取变量内存地址*:解引用运算符,通过地址访问/修改原变量- 指针类型写法:
*int、*string等,表示指向对应类型的指针 - ✅ Go 指针不支持指针算术运算(p++ 等),保证安全
package mainimport "fmt"
func main() { a := 42 var p *int = &a // p 是 *int 指针,保存 a 的内存地址 fmt.Println("a 的地址:", p) fmt.Println("*p =", *p) // 解引用读取原值
*p = 100 // 解引用修改原变量 a fmt.Println("a =", a) // a 变为 100
// 简写方式 b := 99 p2 := &b fmt.Println("*p2 =", *p2)}2. 值传递 vs 指针传参
- Go 默认参数是 值传递:传入变量副本,修改副本不会影响外部原变量
// 值传递:修改的只是副本,外部变量不变func tryChange(x int) { x = 999 fmt.Println("函数内 x =", x)}
// 指针传参:直接修改外部原变量func changeValue(p *int) { *p = 999}
// 交换两个变量(指针版本)func swap(a, b *int) { *a, *b = *b, *a}
func main() { a := 10 tryChange(a) fmt.Println("a =", a) // a 仍然是 10
changeValue(&a) fmt.Println("a =", a) // a 变为 999
x, y := 1, 2 swap(&x, &y) fmt.Printf("x = %d, y = %d\n", x, y) // x=2 y=1}3. slice、map 参数传递特性
slice 和 map 底层结构自带指针,传递时不需要额外加 *,函数内修改元素会直接影响外部原数据
package mainimport "fmt"
// 修改切片元素,直接影响原切片func doubleSlice(nums []int) { for i := range nums { nums[i] *= 2 }}
// 修改 map,直接影响原 mapfunc addEntry(m map[string]int, key string, value int) { m[key] = value}
func main() { nums := []int{1, 2, 3, 4, 5} doubleSlice(nums) fmt.Println(nums) // [2 4 6 8 10]
scores := map[string]int{"Alice": 90} addEntry(scores, "Bob", 85) fmt.Println(scores) // map[Alice:90 Bob:85]}map 初始化方式
// 方式1:字面量初始化scores := map[string]int{"Alice": 90}
// 方式2:make 初始化(推荐动态使用)scores := make(map[string]int)scores["Alice"] = 90- ❗ nil map 不能直接赋值,必须先用 make / 字面量初始化
- map 取值检查:
val, ok := scores["Alice"]- val:对应 value 值
- ok:bool 类型,表示 key 是否存在
三、错误处理(error)
- Go 没有 try/catch 异常机制,依靠多返回值 error 做错误处理
- 调用函数后主动检查 err 是否为 nil
常见多返回值函数
fmt.Scan→(n int, err error)strconv.Atoi→(int, error)os.Open→(*os.File, error)- map 取值:
val, ok := m["key"] - range 遍历:
i, v := range arr
四、匿名函数 & 闭包
1. 匿名函数
没有函数名的函数,可以赋值给变量、直接调用
package mainimport "fmt"
func main() { // 匿名函数赋值给变量 add := func(a, b int) int { return a + b } fmt.Println(add(3, 5)) // 8}2. 闭包
- 闭包 = 可以捕获外层变量的匿名函数,会保留引用外部变量,而非复制副本
package mainimport "fmt"
func main() { x := 10 addX := func(n int) int { return n + x // 捕获外部变量 x } fmt.Println(addX(5)) // 15
x = 100 // 修改外部变量,闭包可感知变化 fmt.Println(addX(5)) // 105}3. 闭包 + sort.Slice 自定义排序
sort.Slice(slice, less func(i,j int) bool)
- 参数1:目标切片
- 参数2:less 闭包函数:i,j 为索引,返回 bool
true:i 元素排在 j 前面
package mainimport ( "fmt" "sort")
func main() { nums := []int{3, 1, 4, 1, 5, 9, 2, 6} // 升序排序 sort.Slice(nums, func(i, j int) bool { return nums[i] < nums[j] }) fmt.Println(nums) // [1 1 2 3 4 5 6 9]
// 按绝对值从大到小排序 nums2 := []int{-3, 1, -4, 1, 5, -9, 2, -6} sort.Slice(nums2, func(i, j int) bool { a, b := nums2[i], nums2[j] if a < 0 { a = -a } if b < 0 { b = -b } return a > b }) fmt.Println(nums2) // [-9 -6 5 -4 -3 2 1 1]} 260714GO函数和指针
https://fuwari.vercel.app/posts/260714go函数和指针/