0

0

Go语言中结构体方法接收器:值与指针的深度解析

霞舞

霞舞

发布时间:2025-09-13 18:07:01

|

629人浏览过

|

来源于php中文网

原创

Go语言中结构体方法接收器:值与指针的深度解析

本文深入探讨Go语言中结构体方法接收器的核心概念,重点区分值接收器和指针接收器在修改结构体字段时的行为差异。通过具体代码示例,详细阐述为何在需要修改结构体状态时必须使用指针接收器,而在仅读取或不需修改时可选用值接收器,旨在帮助开发者正确理解和应用这两种接收器类型,编写出高效且符合预期的Go代码。

理解Go语言中的结构体与方法

go语言中,结构体(struct)是一种聚合类型,它将零个或多个任意类型的值组合在一起。方法(method)是附着在特定类型上的函数,它可以通过该类型的实例来调用。方法的定义形式为 func (receiver type) methodname(parameters) (results),其中 receiver 是方法的接收器,它决定了方法操作的是类型值的一个副本还是类型值本身。

值接收器的问题:为何无法修改结构体字段

考虑以下一个简单的Foo结构体及其方法定义:

type Foo struct {
    name string
}

func (f Foo) SetName(name string) { // 值接收器
    f.name = name // 尝试修改接收到的副本
}

func (f Foo) GetName() string { // 值接收器
    return f.name
}

当我们尝试使用上述代码创建Foo实例并设置其name字段时,会发现name字段并未被修改:

package main

import "fmt"

type Foo struct {
    name string
}

func (f Foo) SetName(name string) {
    f.name = name
}

func (f Foo) GetName() string {
    return f.name
}

func main() {
    p := new(Foo) // p 是 *Foo 类型,指向一个 Foo 零值实例
    p.SetName("Abc")
    name := p.GetName()
    fmt.Println(name) // 输出为空,因为 name 字段未被修改
}

出现这种情况的原因在于SetName方法使用了值接收器(f Foo)。在Go语言中,当一个方法使用值接收器时,它会接收到该类型值的一个副本。这意味着SetName方法内部对f.name的修改,实际上是修改了Foo实例的一个独立副本的name字段,而原始的p所指向的Foo实例并未受到影响。因此,当GetName方法被调用时,它读取的是原始Foo实例中未被修改的name字段,其值仍然是零值(空字符串)。

解决方案:使用指针接收器修改结构体字段

要解决上述问题,使方法能够修改原始结构体实例的字段,我们需要使用指针接收器。当一个方法使用指针接收器时,它接收到的是指向原始结构体实例的指针,因此可以通过该指针直接访问并修改原始实例的字段。

立即学习go语言免费学习笔记(深入)”;

修改SetName方法以使用指针接收器:

func (f *Foo) SetName(name string) { // 指针接收器
    f.name = name // 修改原始结构体实例的字段
}

现在,SetName方法接收的是Foo类型的一个指针。通过这个指针,我们可以直接修改p所指向的Foo实例的name字段。

EduPro
EduPro

EduPro - 留学行业的AI工具箱

下载

对于仅需要读取结构体字段而不需要修改的方法,使用值接收器是完全可以的,甚至在某些情况下是推荐的,因为它避免了不必要的指针操作,并且可以暗示该方法不会改变结构体的状态。

修改后的完整示例代码如下:

package main

import "fmt"

type Foo struct {
    name string
}

// SetName 使用指针接收器,可以修改原始 Foo 实例的 name 字段。
func (f *Foo) SetName(name string) {
    f.name = name
}

// GetName 使用值接收器,因为它只需要读取 name 字段,不需要修改。
func (f Foo) GetName() string {
    return f.name
}

func main() {
    // 实例化 Foo 结构体。
    // Foo{} 是创建 Foo 零值实例的字面量语法。
    // new(Foo) 也会返回 *Foo 类型,指向一个 Foo 零值实例,与 &Foo{} 等价。
    // 这里使用 Foo{} 更加简洁,但实际效果对于后续调用 SetName 没有影响。
    p := Foo{} 

    // 调用 SetName 方法,传入的是 p 的地址(Go 会自动将值类型 p 转换为 &p 传递给指针接收器方法)。
    p.SetName("Abc") 

    // 调用 GetName 方法,传入的是 p 的副本。
    name := p.GetName() 

    fmt.Println(name) // 输出: Abc
}

关键概念与注意事项

  1. 值接收器 vs. 指针接收器:

    • 值接收器(func (f Foo)): 方法操作的是结构体的一个副本。对副本的任何修改都不会影响原始结构体实例。适用于只读操作或当方法需要独立于原始实例的数据时。
    • *指针接收器(`func (f Foo)`): 方法操作的是指向原始结构体实例的指针**。通过该指针可以修改原始结构体实例的字段。适用于需要修改结构体状态(字段值)的操作。
  2. 实例化结构体:Foo{} 与 new(Foo):

    • Foo{}:创建一个Foo类型的零值实例。它的类型是Foo。
    • new(Foo):分配一个Foo类型的零值内存,并返回其地址(即*Foo类型的一个指针)。它等价于&Foo{}。
    • 在调用方法时,Go语言会根据接收器类型自动处理:
      • 如果方法接收器是指针类型(*Foo),你可以用值类型(Foo)或指针类型(*Foo)的实例来调用它。Go会自动取地址。
      • 如果方法接收器是值类型(Foo),你可以用值类型(Foo)或指针类型(*Foo)的实例来调用它。Go会自动解引用(如果实例是指针)或复制值(如果实例是值)。
  3. 何时选择哪种接收器?

    • 需要修改结构体实例状态时,使用指针接收器。 这是最常见的场景,例如设置字段、更新计数器等。
    • 不需要修改结构体实例状态时(只读),使用值接收器。 这样可以避免不必要的指针解引用,并且明确表示该方法不会改变结构体。
    • 结构体较大时,考虑使用指针接收器,即使是只读操作。 传递大型结构体的副本会带来性能开销。然而,对于大多数小结构体,值接收器的开销可以忽略不计。
    • 方法集: 带有指针接收器的方法只能通过指针类型或可取地址的值类型调用;带有值接收器的方法可以通过值类型或指针类型调用。

总结

正确选择Go语言中结构体方法的接收器类型是编写高效、可维护代码的关键。当方法需要修改结构体实例的内部状态时,必须使用指针接收器。而对于只读操作或不涉及状态修改的场景,值接收器是更简洁和安全的默认选择。理解这两种接收器的工作原理及其对内存和行为的影响,将帮助Go开发者更好地设计和实现结构体及相关方法。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1497

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

622

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

572

2024.03.22

php中定义字符串的方式
php中定义字符串的方式

php中定义字符串的方式:单引号;双引号;heredoc语法等等。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

586

2024.04.29

go语言字符串相关教程
go语言字符串相关教程

本专题整合了go语言字符串相关教程,阅读专题下面的文章了解更多详细内容。

170

2025.07.29

c++字符串相关教程
c++字符串相关教程

本专题整合了c++字符串相关教程,阅读专题下面的文章了解更多详细内容。

82

2025.08.07

c++ 根号
c++ 根号

本专题整合了c++根号相关教程,阅读专题下面的文章了解更多详细内容。

70

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 4.2万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号