

defer放在循环里是后进先出

验证defer语句下函数参数会在defer语句执行的时候被立即计算,而不是在最终执行时



在Go语言中,defer语句会在当前函数返回之前执行被延迟的函数或方法。defer语句在声明时会捕获当前变量的值或引用,具体行为取决于变量的类型:
-
值类型变量(如整数、浮点数、字符串等):
defer会在声明时复制变量的当前值。- 之后对变量的修改不会影响
defer捕获的值。
-
引用类型变量(如slice、map、指针等):
defer会在声明时捕获变量的引用。- 之后对变量所指向的数据进行修改会影响
defer的输出,因为defer使用的是同一个引用。
示例1:整数值类型
package main
import "fmt"
func main() {
s := 1
defer fmt.Println(s) // 捕获s的值1
s = 3
fmt.Println("最终s的值:", s)
}
输出:
最终s的值: 3
1
解释:
defer fmt.Println(s)在声明时捕获了s的值1。- 之后
s被赋值为3,但defer已经保存了原来的值1,所以打印的是1。
示例2:slice引用类型
package main
import "fmt"
func main() {
s := []int{1, 2}
defer fmt.Println(s) // 捕获s的引用
s[0] = 3
fmt.Println("最终s的值:", s)
}
输出:
最终s的值: [3 2]
[3 2]
解释:
defer fmt.Println(s)捕获了s的引用,指向底层数组[1, 2]。- 之后修改
s[0]为3,改变了底层数组的内容。 defer执行时,使用的是同一个引用,打印的是修改后的[3, 2]。
总结:
- 对于值类型变量,
defer捕获的是变量的值副本,修改变量不会影响defer的输出。 - 对于引用类型变量,
defer捕获的是变量的引用,修改底层数据会影响defer的输出。
通过理解defer对不同类型变量的捕获方式,可以更好地控制和预测程序的行为。
package main
import "fmt"
func main() {
s := []int{1, 2}
s[0] = 3
defer fmt.Println(s)
s = append(s, 4)
}
在这个Go代码中,s是一个slice,初始值为[]int{1, 2}。执行defer fmt.Println(s)时,s的值被捕获。当append(s, 4)执行后,s的底层数组发生了变化,但defer捕获的是append之前的slice副本。因此,defer fmt.Println(s)输出的是[3, 2]。
步骤解释:
-
初始化
slice:s := []int{1, 2}s指向一个底层数组[1, 2],长度和容量均为2。
-
修改
slice的元素:s[0] = 3- 修改了底层数组的第一个元素,底层数组变为
[3, 2]。 s仍然指向同一个底层数组。
- 修改了底层数组的第一个元素,底层数组变为
-
defer捕获slice的副本:defer fmt.Println(s)defer捕获了当前slice的副本,包括指向底层数组的指针、长度和容量。- 此时,
s指向的底层数组是[3, 2]。
-
append操作:s = append(s, 4)s的容量为2,长度为2,append会导致底层数组重新分配。- 新的底层数组创建,容量可能增加到4,内容为
[3, 2, 4]。 s现在指向新的底层数组。
-
defer执行:defer fmt.Println(s)执行时,使用的是defer捕获的slice副本,指向旧的底层数组[3, 2]。- 因此,输出为
[3, 2]。
总结:
defer在声明时捕获了slice的副本,包括指向底层数组的指针。append操作可能导致底层数组重新分配,但defer捕获的副本仍然指向旧的底层数组。- 因此,
defer fmt.Println(s)输出的是append之前的slice内容[3, 2]。
defer和return执行时机

return先计算出结果,defer语句执行,函数带着return计算出的结果真正返回