1290 字
6 分钟
260714GO函数和指针

GO 函数与指针#

一、函数基础#

函数是可重复调用的代码块,用于拆分代码、提高复用性、增强可读性

1. 函数定义与调用#

语法:func 函数名(参数列表) 返回类型 { 函数体 }

package main
import "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 main
import "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 main
import "fmt"
// 修改切片元素,直接影响原切片
func doubleSlice(nums []int) {
for i := range nums {
nums[i] *= 2
}
}
// 修改 map,直接影响原 map
func 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

常见多返回值函数#

  1. fmt.Scan(n int, err error)
  2. strconv.Atoi(int, error)
  3. os.Open(*os.File, error)
  4. map 取值:val, ok := m["key"]
  5. range 遍历:i, v := range arr

四、匿名函数 & 闭包#

1. 匿名函数#

没有函数名的函数,可以赋值给变量、直接调用

package main
import "fmt"
func main() {
// 匿名函数赋值给变量
add := func(a, b int) int {
return a + b
}
fmt.Println(add(3, 5)) // 8
}

2. 闭包#

  • 闭包 = 可以捕获外层变量的匿名函数,会保留引用外部变量,而非复制副本
package main
import "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 main
import (
"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函数和指针/
作者
anh
发布于
2026-07-14
许可协议
CC BY-NC-SA 4.0