Go赋值操作的关键细节

一、:= 短变量声明的细节

1.作用域规则

2.重复声明规则

3类型推断

二、 = 赋值操作的细节

1.类型必须匹配

2.多重赋值特性

三、 零值初始化细节

1.Go为所有类型提供零值

四、指针赋值的细节

1.基本指针操作

理解这段代码的关键在于搞清楚两个概念:普通变量指针变量,以及它们之间的关系。

用"门牌号"和"房子"来理解

  • 变量 x 就像是一栋 房子 ,里面住着数字 10

  • 指针变量 p 就像是一张 纸条 ,上面写着这栋房子的 门牌号(内存地址)

现在看代码的每一步:

var x int = 10

var p *int = &x

1.建房子x 盖好了,里面放了 10

2.写纸条p 是张空白纸条,&x 就是去查 x 的门牌号,然后把门牌号 p 的纸条上。

*p = 20

3.改房子里的东西

  • *p 这个动作,叫 解引用

  • 意思是:拿起 p 这张纸条,顺着上面的门牌号找到 x 这栋房子 ,然后把里面的东西换成 20

  • 注意:纸条没变 (还是那个门牌号),但 房子里的东西变了

fmt.Println(x) // 输出 20

4.结果x 这栋房子里的内容已经是 20 了。因为 px 指向的是同一栋房子 ,所以通过 p 改,x 自己也会变。

为什么 var q *int 危险?

var q *int

// *q = 30 // ❌ 这里会崩溃

  • q 是一张 空白纸条nil 指针)

  • 纸上 没有写任何门牌号

  • 如果执行 *q = 30,就相当于:

    "拿着一张空白纸条去找房子,强行往里面塞东西。"

你根本不知道那个房子在哪儿(内存地址无效),操作系统为了保护其他程序的安全,会直接 终止程序(Panic)

2.结构体指针

五、特殊赋值情况

1.空白标识符 _

2.常量赋值

六、复合类型的赋值细节

1.切片赋值

2.映射赋值

七、 函数相关的赋值细节

1.命名返回值

一旦起了名字,这两个变量在函数一开始就被自动创建并初始化为零值result=0, err=nil)。

当你写一个return (后面不带东西)时,Go 就默认:"把当前 resulterr 的值,原样打包返回给调用方。"

场景一:b == 0(出错)

  1. 进入函数,Go 自动创建 result=0, err=nil

  2. 检测到 b == 0,执行 err = errors.New(...)

  3. 执行裸 return。此时 result 还是 0err 是新错误。

  4. 返回:(0, error)

场景二:b != 0(正常)

  1. 进入函数,Go 自动创建 result=0, err=nil

  2. 跳过 if,执行 result = a / b

  3. 执行裸 return。此时 result 是计算结果,err 还是 nil

  4. 返回:(计算结果, nil)

2.闭包中的变量捕获

为什么错误写法里,所有函数都输出 3i 不是每次循环都在变吗?

闭包捕获的是变量本身 ,不是当时的值
  • 在错误写法中,for 循环里的 i同一个变量

  • 每次循环,只是修改了这个变量的值(0 → 1 → 2 → 3 循环结束)。

  • 闭包函数 func(){ fmt.Println(i) } 记住的是 i 这个变量本身(门牌号),而不是创建时的数值。

  • 等到你真正执行 这些函数时(比如 f := funcs[0]; f()),循环早就结束了,i 已经变成了 3

i := i 到底干了什么?
  • 左边的 i :创建一个全新的、局部的变量(作用域仅在本次循环内)。

  • 右边的 i :是外层 for 循环的变量。

  • 效果 :把外层 i 当前的值 复制 给内层新的 i

  • 结果 :闭包捕获的是这个全新的内层变量,它只属于这一次循环,下次循环会再创建另一个新的。

  • funcs := make([]func(), 0) 相当于:你买了一个空的 "待办事项清单" 本子

  • funcs = append(funcs, func(){...}) 相当于:往这个本子里 "记下一条新任务"

核心连接点:类型 func()

这两行代码能配合工作的唯一桥梁 就是括号里的类型 func()

代码片段 中文直译 关键细节
make([]func(), 0) 制作一个切片,这个切片只能用来存放 func() 类型的东西 指明了仓库的"货架规格"。
func(){ fmt.Println(i) } 这是一个匿名函数,它的签名正是 func()(无参数、无返回值)。 生产了一个符合"货架规格"的货物。
append(funcs, ...) 把货物放到货架上。 类型必须严格匹配,否则编译报错。

如果把它们拆成三个时间点来看,逻辑会更清晰:

复制代码
// 第一步:声明与初始化(建立空仓库)
// 此时 funcs 的长度为 0,容量为 0,里面一个函数都没有。
funcs := make([]func(), 0)

// 第二步:循环制造与入库(append 操作)
for i := 0; i < 3; i++ {
    // 此时生成了一个匿名函数对象(货物)
    // append 函数检查:这个货物是不是 func() 类型? ✅ 是的。
    // 于是 append 将 funcs 扩容,并把货物放在索引位置。
    funcs = append(funcs, func() {
        fmt.Println(i)
    })
}

// 第三步:此时 funcs 变成了一个有 3 个元素的切片
// 结构类似于:[ 函数0 , 函数1 , 函数2 ]

为什么要用 = 接收?

你可能会注意到写的是 funcs = append(...) 而不是单纯的 append(...)

这是因为 Go 的切片在扩容时,底层数组的地址可能会变 (搬仓库)。append 函数会把新仓库的地址 作为返回值返回。如果你不用 funcs = 接住它,你就失去新仓库的钥匙了,funcs 还是指向那个旧的、没扩容的空仓库。

避免的陷阱

总结:

相关推荐
Sylvia-girl12 小时前
C++模板【上】
开发语言·c++
2zcode12 小时前
基于MATLAB多特征融合与SVM的金属表面缺陷检测系统
开发语言·支持向量机·matlab
2zcode12 小时前
基于MATLAB脑电信号的帕金森病抑郁症检测研究
开发语言·matlab·抑郁症·帕金森病
untE EADO12 小时前
Java进阶之路,Java程序员职业发展规划
java·开发语言
xyq202413 小时前
C++ 变量作用域
开发语言
SKY -dada13 小时前
Understand 使用教程
开发语言·c#·流程图·软件构建·敏捷流程·代码复审·源代码管理
xxjj998a13 小时前
Laravel3.x:奠定现代PHP框架的重要里程碑
android·开发语言·php
(Charon)13 小时前
【C++/Qt】Qt 实现 MQTT 测试工具:连接 Broker、订阅主题与发布消息
开发语言·c++·qt
Ulyanov13 小时前
《现代 Python 桌面应用架构实战:PySide6 + QML 从入门到工程化》:动态数据仪表盘与 NumPy 可视化 —— 从标量到向量的数据驱动进化
开发语言·python·qt·架构·numpy
小短腿的代码世界13 小时前
Qt序列化与持久化深度解析:从QDataStream到自定义二进制协议
开发语言·数据库·qt