资讯专栏INFORMATION COLUMN

Golang什么时候该使用指针

社区管理员 / 781人阅读

Golang 什么时候使用指针(Pointer)?什么时候使用值(Value)?对于go开发者来说是一件头疼的事情, 而且这个问题似乎没有绝对的答案,那是否代表我们可以随意使用呢?答案当然是否定的。本文我将试图总结什么场景使用指针更合理。 在开始阅读前,建议读者先能够清晰理解 Golang 指针、类型和值等概念。

本文并不是标准更不是唯一答案,而是自己根据使用经验和社区的一些讨论而总结的实践

有下几种情形,我们是否需要考虑使用指针:

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}
  1. 结构体定义的字段

  2. 方法中接受者

  3. 函数传参

  4. 函数和方法返回值

这里我先给出使用一般准则,后面详细介绍不同场景的细节。

  • 方法通常使用指针作为接受者, 官方文档的建议是:如果犹豫,请使用指针;

  • Slices,maps,channels,strings,function value and interface value 这些类型内部通过指针实现,再定一个指针指向这些类型的变量是多余的;

  • 当一个结构体很复杂或者需要修改结构体使用指针,其他情况使用值,因为滥用指针会出现一些不可预料的情况;

什么不需要使用指针


  1. CodeReviewComments中建议传输(pass)小型结构体, 如type Point struct{ latitude,longtitude float64 },使用原始类型,除非需要修改它们,理由有以下几点


  • 值语义可以避免歧义,因为指针类型的赋值;

  • 牺牲干净的语义换取速度并不是Golang所推荐的做法,有时值传递性能更好,因为值传递能够避免缓存遗漏和队分配;

  1. 对于切片,不需要使用指针指向它,仍然可以改变其元素,这是因为切片内部是通过指针指向底层数组实现的, 标准库中io.Reader.Read(p []byte)函数中即通过传递值类型实现对切片 p 的修改。其实切片也可以当作是小型结构体,其定义如下, 在 64 位系统一个切片变量只占用 24 个字节。同样地,对于 map 和 channel 类型,通常也不需要使用指针

  2. 对于slices you'll reslice(更改其起始元素位置/长度/容量),如内置函数func append(slice []Type, elems ...Type) []Type, 接受一个切片,返回新的切片。返回一个新数组会让调用者更清晰地理解函数的语义。

什么时候必须使用指针

对于以下场景,使用指针是必须的:

  1. 如果结构体中包含sync.Mutex获取类似其他同步字段时,由于这类字段类型是禁止拷贝的,所以无论其方法的接受者, 还是其作为参数和返回值都应该使用指针:

  2. type FIFO struct {
    	lock sync.RWMutex
    	cond sync.Cond
    	items map[string]interface{}
    	queue []string
    	populated bool
    	initialPopulationCount int
    	keyFunc KeyFunc
    	closed bool
    }
    
    // Close the queue.
    func (f *FIFO) Close() {
    	f.lock.Lock()
    	defer f.lock.Unlock()
    	f.closed = true
    	f.cond.Broadcast()
    }
    
    // NewFIFO returns a Store which can be used to queue up items to
    // process.
    func NewFIFO(keyFunc KeyFunc) *FIFO {
    	f := &FIFO{
    		items:   map[string]interface{}{},
    		queue:   []string{},
    		keyFunc: keyFunc,
    	}
    	f.cond.L = &f.lock
    	return f
    }

结构中定义

结构体中,除了需要考虑是否内存的占用之外,还需要考虑结构体的用途,一般主要分为工具结构体资源结构体, 资源结构体很容易理解,主要包括VO,DAO,Entity等,这类结构体一般用于模块或分层之间的通信。对于这类结构体, 如用于序列化的结构体,根据序列化协议和库可能有所区别,如Golang默认的Json序列化协议对于是否显示字段, 定义为指针可以可好解决。下面是kubernetes IngressSpec的定义。

// IngressSpec describes the Ingress the user wishes to exist.
type IngressSpec struct {
	IngressClassName *string `json:"ingressClassName,omitempty" protobuf:"bytes,4,opt,name=ingressClassName"`
	Backend *IngressBackend `json:"backend,omitempty" protobuf:"bytes,1,opt,name=backend"`
	TLS []IngressTLS `json:"tls,omitempty" protobuf:"bytes,2,rep,name=tls"`
	Rules []IngressRule `json:"rules,omitempty" protobuf:"bytes,3,rep,name=rules"`
}

我们可以看到字段IngressClassName定义为指针类型,就是为了序列化时更方便处理。 工具结构体通常指非资源结构体,主要是controller,config,factory和自定义数据结构类型, 这些结构体往往更需要考虑内存占用。

性能

使用指针并不是总能提升性能。使用指针可以避免值拷贝,减少栈内存的占用,由于堆内存的分配会导致GC频繁地执行,从而降低性能, 而值传递则不会。我们通过下面的实例Demo验证。

以下两个函数分别通过值拷贝和指针共享结构体:

分别进行进行基准测试:

执行基准测试,benchstat工具需要下载,链接为perf

image.png

可以看出,这时候通过值传递的方式执行更快,内存占用也更少。关于如何分析Golang程序和代码段性能, 后续我会总结一篇博客多带带介绍。

总结

本篇博文,简单介绍怎样如何使用指针更合理,其实很多场景都没有标准答案,更多的是性能语义两者的权衡。 掌握Golang中类型,值,指针类型等基础概念才能优雅地使用指针。遇到疑惑的参考标准库中的实现和借鉴一些成熟的项目中的实践;


文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/127929.html

相关文章

  • golang学习笔记(一)——golang基础和相关数据结构

    摘要:小白前端一枚,最近在研究,记录自己学习过程中的一些笔记,以及自己的理解。此外,结构体也支持嵌套。在函数声明时,在函数名前放上一个变量,这个变量称为方法的接收器,一般是结构体类型的。 小白前端一枚,最近在研究golang,记录自己学习过程中的一些笔记,以及自己的理解。 go中包的依赖管理 go中的切片 byte 和 string go中的Map go中的struct结构体 go中的方...

    lyning 评论0 收藏0
  • golang 入门

    摘要:而且数组是定长的,而切片可以调整长度。也就是说类型的值可以用于表示任意语言类型的值。下划线让编译器接受这类导入,并且调用对应包内的所有代码文件里定义的函数。结构体可以当做是一种数据类型一般都默认函 缘起 之前下载视频用的you-get,但是b站一直下不了,优酷也经常出问题,所以接触到lulu https://github.com/iawia002/Lulu 这个也是基于you-get ...

    loonggg 评论0 收藏0
  • 对比学习:Golang VS Python3

    摘要:和都是目前在各自领域最流行的开发语言之一。在机器学习数据分析领域成为必学语言。 showImg(https://segmentfault.com/img/remote/1460000019167290); Golang和Python都是目前在各自领域最流行的开发语言之一。 Golang其高效而又友好的语法,赢得了很多后端开发人员的青睐,最适用于高并发网络编程的语言之一。 Python不...

    Jason 评论0 收藏0
  • 对比学习:Golang VS Python3

    摘要:在机器学习数据分析领域成为必学语言。不定长参数,支持不定长参数,用定义参数名,调用时多个参数将作为一个元祖传递到函数内返回函数结果。showImg(https://user-gold-cdn.xitu.io/2019/5/13/16ab0b937e7329d4); Golang和Python都是目前在各自领域最流行的开发语言之一。 Golang其高效而又友好的语法,赢得了很多后端开发人员的青...

    刘东 评论0 收藏0

发表评论

0条评论

社区管理员

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<