发布于2026-07-17 阅读(0)
扫一扫,手机访问
在 Go 中,只有可寻址的变量才能作为赋值操作的左值;函数返回的结构体值(非指针)是临时、不可寻址的,因此无法直接对其字段赋值,而返回指针则可寻址并支持字段修改。
在 Go 中,只有可寻址的变量才能作为赋值操作的左值;函数返回的结构体值(非指针)是临时、不可寻址的,因此无法直接对其字段赋值,而返回指针则可寻址并支持字段修改。
Go 语言里有一条挺硬的规则:赋值语句的左边,必须是一个在内存里“有固定位置”的东西,也就是能通过 &x 拿到地址的变量。这不是随便定的,而是为了保证每次赋值都有实实在在的副作用——你要是能随便改一个临时值,那改完就丢,谁也没法知道,毫无意义。
来,看个简单例子就明白了:
package main
type Employee struct {
ID int
Salary float64
}
func EmployeeByID(id int) *Employee {
return &Employee{ID: id, Salary: 5000.0}
}
func EmployeeByIDValue(id int) Employee {
return Employee{ID: id, Salary: 5000.0}
}
func main() {
id := 123
// ✅ 合法:*Employee 是可寻址的(指向堆/栈上的实际结构体)
EmployeeByID(id).Salary = 0 // 修改生效,影响底层对象
// ❌ 编译错误:Employee 值是临时的、不可寻址的
// EmployeeByIDValue(id).Salary = 0 // compile error: cannot assign to EmployeeByIDValue(id).Salary
// ✅ 合法(但效果不同):先绑定到变量,再赋值
emp := EmployeeByIDValue(id)
emp.Salary = 0 // 修改的是 emp 的副本,原返回值已丢弃,无外部影响
}
这里的关键就两点:
*Employee,解引用之后(比如 (*ptr).Salary)其实是在访问底层那个实实在在的 Employee 实例,它当然是可寻址的,所以直接赋值完全没问题。Employee 本身,这个表达式的结果是一个临时值——Go 语言规范明确说了,临时值不可寻址,所以 x.f = v 这种写法根本过不了编译。⚠️ 有几点需要特别提醒:
&struct{} 字面量(比如 &Employee{})返回指针是安全的,Go 会在必要时把字面量分配到堆上,或者延长其生命周期,保证指针一直有效。e := f(); e.field = v。但要注意:这时候改的是副本,不是原始返回值,调用方那边不会受影响。总结一下:Go 通过强制“可寻址性”这个机制,把内存安全和语义清晰都照顾到了——只有真正持有状态的对象(变量、切片元素、解引用后的指针等等)才允许你对它的字段进行赋值。理解了这一点,写 Go 代码的时候就能避免很多莫名其妙的错误,也更容易写出符合语言惯用法的代码。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8