首页 > 其他分享 >Golang: 解引用 赋值 时发生了什么

Golang: 解引用 赋值 时发生了什么

时间:2024-07-05 15:20:44浏览次数:20  
标签:string com1 fmt com2 Golang ComplexStruct 引用 寄存器 赋值

Golang: 解引用 赋值 时发生了什么

示例代码

package main

import "fmt"

type ComplexStruct struct {
	A int
	B string
	C float64
	D bool
	E []int
	F map[string]string
	G *ComplexStruct
}

func main() {
	com1 := ComplexStruct{
		A: 1,
		B: "com1",
		C: 3.0,
		D: true,
		E: []int{4, 5, 6},
		F: map[string]string{"7": "8", "9": "10"},
		G: nil,
	}
	a := &com1

	com2 := ComplexStruct{
		A: 1,
		B: "com2",
		C: 3.0,
		D: true,
		E: []int{4, 5, 6},
		F: map[string]string{"7": "8", "9": "10"},
		G: nil,
	}
	*a = com2
	aCopy := a

	(*a).A = 4
	// aCopy.A = 9

	b := *a
	b.A = 5

	fmt.Println(com1)
	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(aCopy)
}

简单来说, 这段代码构建了一个复杂结构体.

探究目的

  1. 解引用时发生了什么?
  2. 修改字段时发生了什么?

汇编分析

image

红框中使我们最需要注意的事情.

分析

解引用时(*a = com2​)发生了什么事情?

简单来说, 分为两步.

  1. 获取源变量/目标变量的地址, 分别存入BI, DI,(这两个寄存器可以认为是用来临时寄存器使用)
  2. CALL 复制函数, 函数的具体实现不太好找, 简单描述就是会将源结构体的每一个字段全部复制到目标字段. 直到完成

需要注意, 取址复制并不是一个原子操作.

修改变量时发生了什么?

这里分为两种情况讨论

引用修改

(*a).A = 5​ 在这种情况下, 从源码里看, 并没有做取址复制. 为什么? 因为没有目标变量, 所以此时的修改是对a​的有效修改.可以认为操作同 a.A = 5

操作有两步:

  1. 把堆栈指针寄存器(SP)偏移的值加载到SI寄存器中
  2. 把立即数存储到SI寄存器指向的内存地址

值修改:

b.A = 5

观察可知, 此时只有一步操作.

因为此时不需要在经历一步额外的寻址操作.

建议

  1. 如果要返回一个指针, 那么建议的方式是初始化时使用 s := Struct{}​, 返回时使用 &s​, 如果是复杂的变量赋值时, 能节省一个寻址的操作.
  2. 慎用解指针, 注意自己是否是真的需要. 当解指针时, 一般会带有复制的操作, 如果结构体本身字段较多/长度较大时, 这个消耗不容小觑.

标签:string,com1,fmt,com2,Golang,ComplexStruct,引用,寄存器,赋值
From: https://www.cnblogs.com/pDJJq/p/18285908/golang-what-happened-when-solving-the-assignment-

相关文章

  • golang go-bindata打包配置文件嵌入到二进制文件
    go-bindata打包配置文件嵌入到二进制文件项目中难免会用到一些静态资源和配置文件,但是常规打包的二进制文件无法再其他目录正常运行(静态资源和配置文件不存在)有类似需求的可以安装使用:go-bindata进行编译处理配置文件go-bindata(go-bindata)包实现将项目静态配置文件嵌......
  • C++中的引用:深入理解与高效利用
    引言在C++编程世界中,引用(References)是一种强大的语言特性,它提供了一种直接访问另一个变量的方式,而无需复制其数据。相比于指针,引用在语法上更加直观、安全,是C++标准库以及现代C++编程风格中不可或缺的一部分。本文将带你深入理解C++中的引用,包括其基本用法、与指针的区别、......
  • golang 中 Jwt 的验证及续期使用
    创建Utils的Jwt文件,用于创建JwtToken 和  验证并继期packageutilsimport("errors""fmt""github.com/dgrijalva/jwt-go""strings""time")//生成JwtToken//@ParamsecretKey表示jwtsecretKey【***......
  • 微信SDK与Unity的Addressables发生引用冲突的解决办法
    当我使用Unity的Addressables和微信的minigame-SDK时,会发生一个CS0433的报错,如下图所示: 关于CS0433错误,微软的官方文档中是这么描述的: 因此,根据报错信息,我揣测是Unity的Compat与mscorlib发生了重复,所以将mscorlib.dll文件全部删除了,但是问题没有得到解决,后面在一个大佬的帮......
  • golang 内存逃逸 你应该知道的知识
    逃逸分析目录1.为什么要了解内存逃逸2.什么是逃逸分析3.内存逃逸的影响-性能和稳定性4.内存逃逸的原因5.内存逃逸的检测6.如何避免内存逃逸7.内存逃逸代码示例原文链接:一文弄懂Golang中的内存逃逸1.为什么要了解内存逃逸-内存逃逸是Go语言编程中一个特别需要注意的问......
  • golang 打印类型和switch case选择
    因为需要打印传输信息,要用到pion的stats,而stats返回报告的又包含多个type,对于不同type有不同的数据统计,所以需要打印类型和switchcase针对于不同的type执行不同的命令。针对于返回变量类型,golang采用reflect包,对于变量valua,可以利用reflect.TypeOf(valua)返回变量类型,而这只能做......
  • Java 基本数据类型和引用数据类型有什么区别?
    在Java世界里,数据类型是构建程序的基石,它们决定了变量可以存储什么类型的值以及如何操作这些值。Java的数据类型大致可以分为两大类:基本数据类型(PrimitiveDataTypes)和引用数据类型(ReferenceDataTypes)。理解它们之间的区别,对于编写高效、健壮的Java代码至关重要。现在,我们......
  • Golang面试:泛型
    Go语言在1.18版本中引入了泛型(Generics),这是Go语言发展中的一个重要里程碑。泛型允许你编写更通用和可复用的代码,而无需牺牲类型安全性。以下是对Go中泛型的详细介绍,包括其语法、使用场景和示例代码。1.泛型的基本概念泛型允许你定义可以处理多种数据类型的函数和数据结构,而无需......
  • Golang开发:构建支持并发的网络爬虫
    Golang开发:构建支持并发的网络爬虫随着互联网的快速发展,获取网络数据成为了许多应用场景中的关键需求。网络爬虫作为一种自动化获取网络数据的工具,也因此迅速崛起。而为了应对日益庞大的网络数据,开发支持并发的爬虫成为了必要的选择。本文将介绍如何使用Golang编写一个支持......
  • Golang 依赖注入设计哲学|12.6K 的依赖注入库 wire
    一、前言线上项目往往依赖非常多的具备特定能力的资源,如:DB、MQ、各种中间件,以及随着项目业务的复杂化,单一项目内,业务模块也逐渐增多,如何高效、整洁管理各种资源十分重要。本文从“术”层面,讲述“依赖注入”的实现,带你体会其对于整洁架构&DDD等设计思想的落地,起到的支撑作用。......