一、map作为参数
makemap :返回的是一个 *hmap(指向hmap结构体的指针)。
- Map变量本身就是一个指针。
 
makeslice :返回的是一个 slice 结构体(包含array指针、len、cap三个字段)。
- Slice变量本身是一个包含指针的结构体,而不是一个指针。
 
            
            
              Go
              
              
            
          
          package main
import "fmt"
func modifyMap(m map[string]int) {
	m["key"] = 100 // 这个操作会影响外部的原始map
	m = nil        // 这个操作不会影响外部的map变量
}
func main() {
	myMap := make(map[string]int)
	modifyMap(myMap)
	fmt.Println(myMap) // 输出:map[key:100]
}
# 输出
map[key:100]
myMap 是一个指针(假设指向地址 0x1234)。
调用 modifyMap(myMap) 时,将 0x1234 这个指针值复制一份传递给函数。
函数内的 m 是外部指针的副本,但它指向同一个hmap结构体。
通过 m["key"] = 100 修改的是 0x1234 地址处的map内容,因此外部可见。
m = nil 只是让函数内的副本指向nil,不影响外部的 myMap 指针。
        slice作为参数,我在上一篇有讲过,下面列举一下区别:
| 特性 | Map | Slice | 
|---|---|---|
| 底层类型 | *hmap(指针) | 
slice(结构体) | 
| 函数传参 | 传递指针的副本 | 传递结构体的副本 | 
| 修改元素 | 影响原始map | 影响底层数组(如果未扩容) | 
| 修改长度 | Map无此概念 | 不影响原始slice的len | 
| 修改容量 | Map无此概念 | 不影响原始slice的cap | 
| 重新赋值 | 不影响 原始map变量(如m = nil) | 
不影响 原始slice变量(如s = newSlice) | 
| 扩容影响 | 自动处理,对使用者透明 | 可能指向新数组,与原始slice分离 | 
这种设计差异是有其合理性的:
- 
Map的指针设计:Map的实现非常复杂,涉及哈希桶、扩容等机制。通过返回指针,可以确保所有的map操作都作用于同一个map实体,避免复制整个map结构的开销。
 - 
Slice的结构体设计:Slice被设计为数组的视图或描述符。传递结构体副本是轻量级的(只复制三个机器字),并且这种设计允许对同一底层数组进行多个不同的视图操作(比如切片操作),而不会相互干扰。
 
二、go语言中的defer函数
defer 是 Go 语言中用于延迟执行函数调用的关键字,常用于资源释放、错误处理等场景。其核心特性是:被 defer 的函数会在当前函数返回前执行,无论函数是正常返回、抛出错误还是 panic。
(1)基础用法:延迟执行函数
defer 后的函数调用会被压入一个栈中,当前函数执行完毕前,按「后进先出(LIFO)」顺序执行。
            
            
              Go
              
              
            
          
          package main
import "fmt"
func basicDefer() {
    fmt.Println("函数开始")
    
    // 延迟执行的函数
    defer fmt.Println("延迟执行 1")
    defer fmt.Println("延迟执行 2") // 后 defer 的先执行
    
    fmt.Println("函数结束")
}
func main() {
    basicDefer()
    // 输出顺序:
    // 函数开始
    // 函数结束
    // 延迟执行 2
    // 延迟执行 1
}
        (2)defer 与函数参数:参数值在声明时确定
        
            
            
              Go
              
              
            
          
          package main
import "fmt"
func deferParam() {
    x := 10
    // defer 函数的参数在声明时计算(此时 x=10)
    defer fmt.Printf("defer 执行:x=%d\n", x)
    
    x = 20 // 修改 x 不影响 defer 函数的参数
    fmt.Printf("函数内:x=%d\n", x)
}
func main() {
    deferParam()
    // 输出:
    // 函数内:x=20
    // defer 执行:x=10
}
        defer 函数的参数会在 defer 语句压入栈时计算并缓存,而非执行时。
(3)defer 与返回值:可修改具名返回值
若函数返回值是具名的(有变量名),defer 函数可以修改该返回值(因为返回值在函数执行过程中已被声明)。
            
            
              Go
              
              
            
          
          package main
import "fmt"
// 具名返回值:result
func deferReturn() (result int) {
    defer func() {
        result += 10 // 修改具名返回值
    }()
    return 5 // 实际返回 5 + 10 = 15
}
func main() {
    fmt.Println(deferReturn()) // 输出:15
}
        函数返回过程是「先给返回值赋值,再执行 defer,最后返回」,因此 defer 可以修改具名返回值。
(4)defer 与匿名函数:捕获外部变量
defer 常与匿名函数配合使用,匿名函数可以访问并修改外部变量(注意变量作用域)。
            
            
              Go
              
              
            
          
          package main
import "fmt"
func deferAnonymous() {
    x := 10
    // 匿名函数捕获外部变量 x(引用传递)
    defer func() {
        x += 5
        fmt.Printf("defer 内:x=%d\n", x) // x=105
    }()
    
    x = 100
    fmt.Printf("函数内:x=%d\n", x) // x=100
}
func main() {
    deferAnonymous()
    // 输出:
    // 函数内:x=100
    // defer 内:x=105
}
        匿名函数捕获的是变量的引用,而非值,因此 defer 执行时会反映变量的最新值。
(5)defer 释放资源:文件 / 锁等场景
defer 确保资源释放操作(如关闭文件、解锁)在函数退出前执行,避免资源泄漏。
            
            
              Go
              
              
            
          
          package main
import (
    "fmt"
    "os"
)
func deferResource() {
    // 打开文件
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("文件打开失败:", err)
        return
    }
    // 需正确打开文件之后,才可以使用下面的语句
    // 延迟关闭文件(无论函数是否正常执行,都会关闭)
    defer file.Close()
    
    // 操作文件(示例)
    fmt.Println("文件操作完成")
}
func main() {
    deferResource()
    // 输出:文件操作完成(文件会被自动关闭)
}
        即使 file 操作过程中出现错误,defer file.Close() 仍会执行,确保文件描述符被释放。
(6)defer 与 panic:在 panic 后执行
panic 会终止函数执行,但在终止前会执行所有已声明的 defer 函数。
            
            
              Go
              
              
            
          
          package main
import "fmt"
func deferPanic() {
    defer fmt.Println("defer 1:在 panic 后执行")
    defer fmt.Println("defer 2:在 panic 后执行")
    
    fmt.Println("执行到这里")
    panic("发生异常") // 触发 panic
    fmt.Println("这里不会执行") // 被 panic 跳过
}
func main() {
    defer func() {
        if err := recover(); err != nil {
            fmt.Println("捕获 panic:", err)
        }
    }()
    
    deferPanic()
    // 输出:
    // 执行到这里
    // defer 2:在 panic 后执行
    // defer 1:在 panic 后执行
    // 捕获 panic:发生异常
}
        panic 后,defer 函数仍会按栈顺序执行,可用于在崩溃前记录日志、释放资源等。
(7)defer 与 recover:捕获 panic 恢复执行
recover 必须在 defer 函数中使用,用于捕获 panic 并恢复程序执行。
            
            
              Go
              
              
            
          
          package main
import "fmt"
func riskyOperation() {
    defer func() {
        // 捕获 panic
        if err := recover(); err != nil {
            fmt.Println("恢复程序:", err)
        }
    }()
    
    fmt.Println("执行危险操作")
    panic("操作失败") // 触发 panic
    fmt.Println("操作成功") // 不会执行
}
func main() {
    riskyOperation()
    fmt.Println("程序继续执行") // 被 recover 后,这里会执行
    // 输出:
    // 执行危险操作
    // 恢复程序:操作失败
    // 程序继续执行
}
        recover 只能在 defer 中生效,若没有 defer,panic 会一直向上传播直至程序退出。
(8)defer 的性能影响:避免在循环中滥用
        
            
            
              Go
              
              
            
          
          package main
import (
    "fmt"
    "time"
)
func deferPerformance() {
    // 循环中使用 defer(不推荐)
    start := time.Now()
    for i := 0; i < 1000000; i++ {
        defer func() {}() // 空的 defer 函数
    }
    fmt.Println("循环中使用 defer:", time.Since(start))
    
    // 优化:将 defer 移到循环外(若逻辑允许)
    start = time.Now()
    for i := 0; i < 1000000; i++ {
        // 无 defer
    }
    fmt.Println("无 defer:", time.Since(start))
}
func main() {
    deferPerformance()
    // 输出(示例):
    // 循环中使用 defer: 68.178292ms
    // 无 defer: 299.916µs
}
        循环中使用 defer 会导致大量函数被压栈,建议仅在必要时使用,或通过其他方式优化(如将 defer 移到循环外)。
(9)其他例子参考
            
            
              Go
              
              
            
          
          package main
import "fmt"
// 基础案例:展示defer与return的执行顺序
func basicCase() {
    fmt.Println("=== 基础案例 ===")
    
    defer func() {
        fmt.Println("defer 执行")
    }()
    
    fmt.Println("函数执行中")
    return
    // 输出顺序:
    // 函数执行中
    // defer 执行
}
// 案例1:带返回值的函数中defer的执行
func returnWithDefer() int {
    fmt.Println("=== 带返回值的函数 ===")
    
    defer func() {
        fmt.Println("defer 执行")
    }()
    
    fmt.Println("函数执行中")
    return 100
    // 输出顺序:
    // 函数执行中
    // defer 执行
}
// 案例2:defer修改具名返回值
func deferModifyReturn() (result int) {
    fmt.Println("=== defer修改具名返回值 ===")
    
    defer func() {
        result += 50 // 修改返回值
        fmt.Println("defer 执行,result变为", result)
    }()
    
    fmt.Println("函数执行中,准备返回50")
    return 50
    // 输出顺序:
    // 函数执行中,准备返回50
    // defer 执行,result变为 100
    // 实际返回值为100
}
// 案例3:多个defer的执行顺序(后进先出)
func multipleDefers() {
    fmt.Println("\n=== 多个defer的执行顺序 ===")
    
    defer fmt.Println("defer 1 执行")
    defer fmt.Println("defer 2 执行")
    defer fmt.Println("defer 3 执行")
    
    fmt.Println("函数执行中")
    // 输出顺序:
    // 函数执行中
    // defer 3 执行
    // defer 2 执行
    // defer 1 执行
}
// 案例4:defer函数参数的计算时机
func deferParamTiming() {
    fmt.Println("\n=== defer参数的计算时机 ===")
    
    x := 10
    // 参数在声明时计算
    defer fmt.Printf("defer 执行,x=%d\n", x)
    
    x = 20
    fmt.Printf("函数执行中,x=%d\n", x)
    // 输出顺序:
    // 函数执行中,x=20
    // defer 执行,x=10
}
// 案例4.1:defer函数参数的计算时机
func deferParamTiming1() {
    fmt.Println("\n=== 4.1 defer参数的计算时机 ===")
    
    x := 10
    //注意,这里跟上面有区别,此情况属于闭包捕获外部变量x,是引用传值
    defer func() {fmt.Printf("defer 执行,x=%d\n", x) }()
    
    x = 20
    fmt.Printf("函数执行中,x=%d\n", x)
    // 输出顺序:
    // 函数执行中,x=20
    // defer 执行,x=10
}
// 案例5:返回值是函数调用的
func modifiedOriginalExample() {
    fmt.Println("\n=== 返回值是函数调用的 ===")
    result := returnAndDefer()
    fmt.Printf("main函数中收到的返回值: %d\n", result)
}
func deferFunc() int {
    fmt.Println("deferFunc called")
    return 10 // 修改返回值,展示defer的返回值会被忽略
}
func returnFunc() int {
    fmt.Println("returnFunc called")
    return 20 // 修改返回值,方便观察
}
func returnAndDefer() int {
    defer deferFunc() // defer的返回值会被忽略
    return returnFunc()
    // 输出顺序:
    // returnFunc called
    // deferFunc called
    // 实际返回值为20
}
func calculate(a int, b int) int {
    fmt.Printf("计算: %d\n", a)
    return a
}
// 情况1:命名返回值被 defer 修改
func Example1(n int) (result int) {
    result = n * 2        // result = 10
    defer func() {
        result += 5       // 修改命名返回值:10 + 5 = 15
    }()
    return result         // 返回 15
}
// 情况2:非命名返回值,defer 修改不影响返回值
func Example2(n int) int {
    temp := n + 3         // temp = 8
    defer func() {
        temp *= 2         // temp = 16,但返回值已确定
    }()
    return temp           // 返回值在此时确定 = 8
}
// 情况3:defer 修改命名返回值(复杂计算)
func Example3(n int) (total int) {
    defer func() {
        total += n * 3    // total = 7 + 5*3 = 22
    }()
    total = n + 2         // total = 7
    return total          // 返回 22
}
// 情况4:defer 参数立即求值 vs 闭包捕获
func Example4() (value int) {
    value = 10
    defer func(x int) {
        fmt.Printf("参数x(立即求值): %d\n", x)      // 10
        fmt.Printf("闭包value(最新值): %d\n", value) // 30
    }(value)           // value 此时为 10
    
    value = 20
    return 30          // value = 30
}
// 情况5:多个 defer 修改同一个命名返回值
func Example5(n int) (sum int) {
    defer func() {
        sum += n        // sum = 6 + 3 = 9
    }()
    
    defer func() {
        sum *= 2        // sum = 3 * 2 = 6
    }()
    
    sum = n             // sum = 3
    return sum          // 返回 9
}
func main() {
    basicCase()
    // === 基础案例 ===
    // 函数执行中
    // defer 执行
  
    fmt.Printf("returnWithDefer返回值: %d\n", returnWithDefer())
    // === 带返回值的函数 ===
    // 函数执行中
    // defer 执行
    // returnWithDefer返回值: 100
  
    fmt.Printf("deferModifyReturn返回值: %d\n", deferModifyReturn())
    // === defer修改具名返回值 ===
    // 函数执行中,准备返回50
    // defer 执行,result变为 100
    // deferModifyReturn返回值: 100
  
    multipleDefers()
    // === 多个defer的执行顺序 ===
    // 函数执行中
    // defer 3 执行
    // defer 2 执行
    // defer 1 执行
  
    deferParamTiming()
    // === defer参数的计算时机 ===
    // 函数执行中,x=20
    // defer 执行,x=10
  
    deferParamTiming1()
    // === 4.1 defer参数的计算时机 ===
    // 函数执行中,x=20
    // defer 执行,x=20
  
    modifiedOriginalExample()
    // returnFunc called
    // deferFunc called
    // main函数中收到的返回值: 20
    fmt.Println("calculate开始")
    defer calculate(10, calculate(30, 0))
    defer calculate(20, calculate(40, 0))
    fmt.Println("calculate结束")
    // calculate开始
    // 计算: 30
    // 计算: 40
    // calculate结束
    // 计算: 20
    // 计算: 10
  
    fmt.Println("Example1(5):", Example1(5))     // 15
    fmt.Println("Example2(5):", Example2(5))     // 8
    fmt.Println("Example3(5):", Example3(5))     // 22
    fmt.Println("Example4():")
    Example4()                                   // 参数x: 10, 闭包value: 30
    // 参数x(立即求值): 10
    // 闭包value(最新值): 30
    fmt.Println("Example5(3):", Example5(3))     // 9
}