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 了。因为 p 和 x 指向的是同一栋房子 ,所以通过 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 就默认:"把当前 result 和 err 的值,原样打包返回给调用方。"

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

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

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

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

  4. 返回:(0, error)。

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

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

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

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

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

2.闭包中的变量捕获

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

闭包捕获的是变量本身 ,不是当时的值
  • 在错误写法中,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 还是指向那个旧的、没扩容的空仓库。

避免的陷阱

总结:

相关推荐
1024奇点3 小时前
【仓颉语言入门 · 第二篇 第 22 课】
开发语言·ide·开源
Warren2Lynch4 小时前
C4 模型详解:利用 Visual Paradigm 可视化软件架构的综合指南
开发语言·uml
Knight_AL5 小时前
Java 文件下载时文件名中的 `#` 被转换成 `%23`?一次 URL 编码与解码问题排查
java·开发语言
利威尔xu5 小时前
Go IM 源码实战(14):本地缓存快 3000 倍,失效却难 10 倍
后端·缓存·微服务·面试·golang
FRing035 小时前
vue2干货总结2(v-bind、v-for、v-model、指令修饰符、v-bind操作class和style)
开发语言·前端·vue.js·vue2
by209996 小时前
C++ 智能指针的所有权与使用
开发语言·c++·经验分享
砚底藏山河6 小时前
python量化自定义周期 K 线合成
java·开发语言·数据库·python·金融
利威尔xu6 小时前
Go IM 源码实战(13):30 个会话只有 2 个红点,未读数错在三条路径
redis·后端·微服务·面试·golang
IT召唤狮6 小时前
开源企业级零信任项目Pangolin,零信任能挡住PHP核弹漏洞吗?
开发语言·开源·php
知已难寻6 小时前
03 · OpenStack的计算管理 Nova
运维·服务器·开发语言·云原生·云计算·openstack·开源软件