商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > Go 中结构体指针与值类型字段赋值的关键区别

Go 中结构体指针与值类型字段赋值的关键区别

  发布于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 代码的时候就能避免很多莫名其妙的错误,也更容易写出符合语言惯用法的代码。

本文转载于:https://www.php.cn/faq/2332335.html 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注