golang的引用和非引用总结

目录

概述

一、基本概念

指针类型(Pointer type)

非引用类型(值类型)

引用类型(Reference Types)

解引用(dereference)

二、引用类型和非引用类型的区别

三、golang数据类型系统里的引用类型和非引用类型

值类型(Value Types)

引用类型(Reference Types)

四、golang数据类型系统里的零值

五、Methods and pointer indirection

情况1:函数的形参是值类型,实参是值类型的处理情况

情况2:函数的形参是值类型,实参是指针类型的处理情况

情况3:函数的形参是指针类型,实参是值类型的处理情况

情况4:函数的形参是指针类型,实参是指针类型的处理情况

情况5:方法的接收者是值类型,方法的调用者是值类型的处理情况

情况6:方法的接收者是值类型,方法的调用者是指针类型的处理情况

情况7:方法的调用者是指针类型,方法的调用者是值类型的处理情况

情况8:方法的调用者是指针类型,方法的调用者是指针的处理情况

总结


概述

        本文主要介绍引用类型和非引用类型的基本概念,golang的数据类型系统里有哪些是引用类型和非引用类型,以及引用类型和非引用的区别,他们的优缺点,尤其重点介绍了golang的“Methods and pointer indirection”的含义,指针类型和值类型在函数参数传递和方法调用方面的区别。

一、基本概念

        编程语言中,引用类型和非引用类型(有时也被称为值类型)是两种主要的数据类型分类方式,它们主要区别在于数据在内存中的存储和传递方式。

指针类型(Pointer type)

        指针类型是指能够存储变量地址的数据类型。在 Go 中,使用 *T 表示指向 T 类型的指针,其中 T 是任意类型。指针类型允许我们直接操作变量的内存地址,可以用来传递变量的引用,以及在需要时间接访问变量的值。例如,*int 表示指向整数类型的指针。

非引用类型(值类型)

        非引用类型,也称为值类型,在创建变量时,会在内存中分配一个新的存储空间来存储该变量的值。每个值类型的变量都有自己独立的存储空间,并且变量的值会被直接复制。当你将一个值类型的变量赋值给另一个变量时,实际上是创建了这个值的一个副本。对副本所做的任何修改都不会影响原始变量。因此,值类型的变量在函数参数传递时也是按值传递的。在Go语言中,基本数据类型(如int、float64、bool、string等)和数组都是值类型。

引用类型(Reference Types

        引用类型在创建变量时,并不会在内存中直接存储数据本身,而是存储一个指向数据的引用(或指针)。这个引用是一个地址,指向在堆内存中存储的实际数据。多个引用类型的变量可以指向同一个数据。当你将一个引用类型的变量赋值给另一个变量时,你其实是在复制这个引用,而不是数据本身。因此,所有指向同一个数据的引用类型变量都会共享这个数据

解引用(dereference)

      解引用是指通过指针获取其所指向的值。换句话说,解引用是一种操作,允许我们通过指针变量访问和修改变量或对象的值。通过使用*操作符,我们可以对指针变量进行解引用操作,获取其所指向的值。

        解引用不仅适用于基础数据类型,也适用于结构体等复合类型。例如,我们可以解引用一个指向结构体的指针,以访问或修改结构体的字段。

需要注意的是golang对未初始化的引用类型,进行解引用操作会引发运行时错误(panic)。这是因为未初始化的引用类型变量在内存中没有有效的值或地址,尝试解引用这样的变量将导致未定义的行为

二、引用类型和非引用类型的区别

三、golang数据类型系统里的引用类型和非引用类型

        在 Go 语言中,类型可以分为值类型和引用类型。这两种类型的主要区别在于它们在内存中的存储方式和赋值操作的行为

值类型(Value Types)

值类型包括:

  1. 基本数据类型:如 intfloat64boolstringcomplex64complex128rune(即 int32 的别名,用于表示 Unicode 码点)等。

  2. 数组:数组是固定长度的序列,每个元素都是相同类型的值。例如 [5]int

  3. 结构体:结构体是由一组字段组成的值类型。字段可以具有不同的类型。

对于值类型的变量,赋值操作会创建该值的副本。这意味着如果你修改了一个值类型变量的值,它不会影响其他使用该类型值的变量。

引用类型(Reference Types)

引用类型包括:

  1. 切片:切片是对数组的抽象,它提供了动态长度的、灵活且可变的序列。切片底层引用了数组的一部分或全部,但它本身是一个独立的类型。

  2. 映射:映射是键值对的集合。Go 语言中的映射类型使用 map 关键字定义,例如 map[string]int

  3. 通道:通道用于在 Go 语言的并发程序中传递数据。它们用于实现协程之间的通信。

  4. 接口:接口定义了一组方法的集合,任何实现这些方法的具体类型都被认为实现了该接口。接口本身不存储数据,但可以作为引用类型传递。

  5. 函数:在 Go 语言中,函数也可以被视为值,可以赋值给变量,也可以作为参数传递给其他函数。尽管函数在内存中的表示与常规的值类型略有不同,但在许多上下文中,它们的行为类似于引用类型。

四、golang数据类型系统里的零值

        在 Go 语言中,当声明一个变量但未对其进行赋值时,该变量会被赋予其对应类型的零值。零值是指变量在未被显式赋值时的默认值。下面是 Go 语言中常见类型的零值: 

 

五、Methods and pointer indirection

        前面的铺垫其实我们为了更好地理解“Methods and pointer indirection”。什么是“Methods and pointer indirection”

  • functions with a pointer argument must take a pointer
  • while methods with pointer receivers take either a value or a pointer as the receiver when they are called

为了更好地理解上面这段话,我将问题进行了拓展,即值类型和引用类型在函数传递和方法调用的不同情况下golang编译器的处理方式,分别从参数传递机制,拷贝机制,是否更改原值方面将问题拆分了8种以下情况,并用简单的代码示例来探究解释。

情况1:函数的形参是值类型,实参是值类型的处理情况

package main

import "fmt"

func modifyValue(val int) {
	val = 100
}

func main() {
	x := 10
	modifyValue(x)
	fmt.Println(x) // Output: 10 (原始值未被修改)
}

  • 参数传递机制:当函数形参是值类型,实参也是值类型时,编译器在函数调用时,会复制实参的副本传递给函数,编译器会在栈上分配内存空间,将参数的值复制到栈上的内存位置,然后将栈的内存地址传递给函数
  • 是否修改原始值:不会修改原始值,因为修改只影响参数的副本

情况2:函数的形参是值类型,实参是指针类型的处理情况

package main

import "fmt"

func modifyValue(val int) {
	val = 100
}

func main() {
	x := 10
	modifyValue(&x)
	fmt.Println(x) // cannot use &x (value of type *int) as int value in argument to modifyValue
}

函数的形参是值类型,传递给函数逇也必须是值类型,类型匹配原则 ,类型不匹配编译报错

情况3:函数的形参是指针类型,实参是值类型的处理情况

package main

import "fmt"

func modifyValue(val *int) {
	*val = 100
}

func main() {
	x := 10
	modifyValue(x)
	fmt.Println(x) // cannot use x (variable of type int) as *int value in argument to modifyValue
}

 函数的形参是指针类型,传递给函数的也必须是指针类型,类型匹配原则 ,类型不匹配编译报错

情况4:函数的形参是指针类型,实参是指针类型的处理情况

package main

import "fmt"

func modifyValue(val *int) {
	*val = 100
}

func main() {
	x := 10
	modifyValue(&x)
	fmt.Println(x) // output 100 更改原值
}
  • 参数传递机制:当函数形参是指针类型时,实参也是指针类型时,编译器在函数调用时,会复制实参的地址传递给函数,编译器会在栈上分配空间,将参数的地址复制到栈的内存位置,然后将栈的内存地址传递给函数
  • 是否修改原始值:通过指针可以间接地修改原始值

情况5:方法的接收者是值类型,方法的调用者是值类型的处理情况

package main

import "fmt"

type Myint int

func (m Myint) modify() {
	m = 10
}

func main() {
	x := Myint(5)
	x.modify()

	fmt.Println(x) //output 5
}
  • 参数传递机制:在栈上为调用者分配内存空间,调用方法时,会将调用者的值复制一份,传递给方法的接收者。
  • 是否修改原始值:不会修改原始值,因为修改的只是调用者的副本

情况6:方法的接收者是值类型,方法的调用者是指针类型的处理情况

package main

import "fmt"

type Myint int

func (m Myint) modify() {
	m = 10
}

func main() {
	x := Myint(5)
	(&x).modify()

	fmt.Println(x) //output 5
}
  • 参数传递机制:编译器会将调用者指针解引用为值,然后在解引用后的对象上调用方法,在栈上分配内存空间,并将指针解引用后的值复制到分配的内存位置。编译器将指针解引用,然后解引用后的对象上调用方法
  • 是否修改原始值:不会修改原始值,因为调用者是指针类型,接收者是值类型,调用者后的对象是调用者的副本。

情况7:方法的调用者是指针类型,方法的调用者是值类型的处理情况

package main

import "fmt"

type Myint int

func (m *Myint) modify() {
	*m = 10
}

func main() {
	x := Myint(5)
	x.modify()

	fmt.Println(x) //output 10
}
  • 参数传递机制:调用方法时,编译器会隐式地取调用者的地址,传递给方法的接收者
  • 是否修改原始值:调用地址的副本间接修改原始值

情况8:方法的调用者是指针类型,方法的调用者是指针的处理情况

package main

import "fmt"

type Myint int

func (m *Myint) modify() {
	*m = 10
}

func main() {
	x := Myint(5)
	(&x).modify()

	fmt.Println(x) //output 10
}
  • 参数传递机制:调用方法时,直接将调用者的地址传递给方法的接收者
  • 是否修改原始值:直接修改原始值

总结

        对于函数的形参和实参,参数的类型必须匹配。如果函数的形参是值类型,则传递给它的实参必须是值类型;如果形参是指针类型,则传递给它的实参必须是指针类型。

        而对于方法的接收者和调用者,Go 允许方法的接收者是值类型,这意味着可以直接使用值类型的实例调用该方法;也可以是指针类型,这意味着可以使用指针类型的实例调用该方法。

这种灵活性使得在设计和使用方法时更加方便,可以根据具体的需求和场景选择适合的方法接收者类型。

  • 对于函数(即不附属于任何类型的函数),如果函数的参数是指针类型,那么调用该函数时必须传递一个指针作为参数。这是因为函数是独立存在的,没有与之关联的接收者对象,因此无法通过隐式的方法接收者来自动解引用指针。
  • 对于方法(即附属于某个类型的函数),如果方法的接收者是指针类型,那么在调用该方法时,可以选择传递一个值类型的接收者或者一个指针类型的接收者。如果传递的是值类型的接收者,Go 语言会在内部将其自动解引用为指针类型。这是因为方法是与类型相关联的,可以通过类型的值或指针来调用方法,而不需要显式地进行解引用。

简而言之,函数需要显式地传递指针作为参数,而方法可以接受值类型或指针类型的接收者,并在需要时进行自动解引用。这就是“Methods and pointer indirection”的含义所在

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/537049.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

李沐27_含并行连结的网络GoogLeNet_Inception——自学笔记

Inception块 1.四个路径从不同层面抽取信息,然后在输出通道维合并。 2.有更少的参数个数和计算复杂度(相比于3X3和5X5卷积层) GoogLeNet 1.五个stages,九个inception块 Inception各种后续变种 1.Inception-BN(V2)——使用ba…

【Harbor】harbor.yml详解

目录 前言参数详解hostnameHTTP和HTTPSinternal_tlsharbor_admin_passworddatabasedata_volumestorage_serviceclairtrivyjobservicenotificationchartlog_versionexternal_databaseexternal_redisuaaproxy 前言 网络上对Harbor相关的资料真是少之又少,基本上都是教…

2024mathorcup数学建模A 题思路分析-移动通信网络中 PCI 规划问题

# 1 赛题 A 题 移动通信网络中 PCI 规划问题 物理小区识别码(PCI)规划是移动通信网络中下行链路层上,对各覆盖 小区编号进行合理配置,以避免 PCI 冲突、 PCI 混淆以及 PCI 模 3 干扰等 现象。 PCI 规划对于减少物理层的小区间互相干扰(ICI),增…

STM32程序 关于Semhosting(半主机)和Microlib 以及Printf的关系

一,Keil中Printf导致程序无法运行到Main函数 在Keil中调试STM32程序,编译烧录后,发现程序不能运行,Main函数中点亮LED灯的语句没起作用,说明没有进入Main函数。用Keil调试的时候,虽然设置了Run to main()&…

Docker学习笔记(二):在Linux中部署Docker(Centos7下安装docker、环境配置,以及镜像简单使用)

一、前言 记录时间 [2024-4-6] 前置文章:Docker学习笔记(一):入门篇,Docker概述、基本组成等,对Docker有一个初步的认识 在上文中,笔者进行了Docker概述,介绍其历史、优势、作用&am…

springboot相关报错解决

Caused by: java.lang.ClassNotFoundException: 目录 Caused by: java.lang.ClassNotFoundException: org.springframework.context.event.GenericApplicationListener spring-boot-dependencies:jar:2.1.9.RELEASE was not found org.springframework.context.event.Generi…

Mybatis-plus动态数据源

由于服务没有做微服务部署&#xff0c;需要在后台管理系统访问其他服务的库&#xff0c;所以需要用到动态数据源切换 引入依赖 mybatis-plus动态数据源依赖 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot…

【生产实习-毕设】pyspark学生成绩分析与预测(上)

注意&#xff1a;数据由实习单位老师提供&#xff08;需要自行搜索下载&#xff09;&#xff0c;页面美化为下载模板。 项目介绍&#xff1a;前端页面输入影响成绩的属性&#xff0c;预测出成绩&#xff0c;并作可视化展示——属性对成绩的影响。使用python pyspark 进行数据预…

SpringBoot + Dobbo + nacos

SpringBoot Dobbo nacos 一、nacos https://nacos.io/zh-cn/docs/quick-start.html 1、下载安装包 https://github.com/alibaba/nacos/releases/下载后在主目录下&#xff0c;创建一个logs的文件夹&#xff1a;用来存日志 2、启动nacos 在bin目录下打开cmd运行启动命令&a…

小红的白色字符串

题目描述 小红拿到了一个字符串&#xff0c;她准备将一些字母变成白色&#xff0c;变成白色的字母看上去就和空格一样&#xff0c;这样字符串就变成了一些单词。 现在小红希望&#xff0c;每个单词都满足以下两种情况中的一种&#xff1a; 1.开头第一个大写&#xff0c;其余为…

简述OSI七层模型及每层的功能任务和协议

文章目录 一、OSI七层模型的功能和任务1.物理层2.数据链路层3.网络层4.传输层5.会话层6.表示层7. 应用层 二、OSI七层模型每层的协议 开放系统互连参考模型&#xff08;Open System Interconnect&#xff0c;简称OSI&#xff09;是国际标准化组织(ISO)和国际电报电话咨询委员会…

为什么选择成为程序员?

目录 兴趣和热爱高薪和就业机会持续学习和不断成长挑战和乐趣 兴趣和热爱 许多人选择成为程序员可能是热爱&#xff0c;对计算机&#xff0c;以及编程和科技产生了浓厚的兴趣&#xff0c;并且享受着解决每一个技术问题&#xff0c;构建应用程序和探索新技术所带来的乐趣。 谈到…

vue快速入门(十七)v-model数据双向绑定修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-model.trim 自动去除首尾空格v-model.number 自动转换成数字类型 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" con…

微信小程序(六)定位搜索

一、引言 作者上一章讲了微信小程序的地图实现微信小程序&#xff08;五&#xff09;地图-CSDN博客&#xff0c;但是还有一个功能是和地图紧密结合的&#xff0c;那就是位置搜索定位&#xff0c;这里作者讲讲实现和原理&#xff0c;包括城市筛选。 二、定位搜索实现 1、位置搜…

Mongodb前后端整合篇

一、前端篇 1.1mongoose介绍 Mongoose 是一个对象文档模型库&#xff0c;官网 http://www.mongoosejs.net/ 方便使用代码操作 mongodb 数据库pnpm i mongoose5.13.15 1.2初步使用 import mongoose from mongoose; //设置 strictQuery 为 true mongoose.set(strictQuery, true…

【D3.js Tidy tree绘制树形图,单棵树,左右树,平移,拖拽,树形中的天花板实现,源码实现】

这里写自定义目录标题 D3.js Tidy tree绘制树形图,单棵树,左右树,平移,拖拽,树形中的天花板实现,源码实现D3 简介D3 官网有很多例子,这里说的是Tidy tree[树形图表svg][左侧关系->中间对象<-右侧关系 ] 树形实现 D3.js Tidy tree绘制树形图,单棵树,左右树,平移,拖拽,树形…

网易云信携手 DCloud,共同助力应用开发效率飞升

近日&#xff0c;持续数月的 DCloud 2023 插件开发大赛正式放榜&#xff0c;网易云信音视频呼叫组件获得了本次大赛二等奖。 作为大赛获奖的优秀插件&#xff0c;云信 RTC 呼叫组件已正式在 DCloud 官方插件市场上线&#xff0c;方便企业开发者快速下载和集成&#xff0c;以丰富…

如何快速写一份简历

文章目录 如何快速写一份简历一些写简历的技巧 最近一段时间一直在忙简历相关的事情&#xff0c;起初是有一个其他行业的朋友问我&#xff0c;说这些简历我写了好久真难写&#xff0c;我说你可以借助AI&#xff0c;现在这种工具多了去了&#xff0c;为什么不借助呢&#xff1f;…

最简单的ubuntu安装docker教程

本文参考自docker官方教程&#xff1a;ubuntu上安装docker 一、安装Docker 第一步&#xff1a;添加Docker官方的GPG密钥 直接复制所有代码&#xff0c;作为一行运行即可 sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/k…

Redis性能管理和集群的三种模式(二)

一、Redis集群模式 1.1 redis的定义 redis 集群 是一个提供高性能、高可用、数据分片、故障转移特性的分布式数据解决方案 1.2 redis的功能 数据分片&#xff1a;redis cluster 实现了数据自动分片&#xff0c;每个节点都会保存一份数据故障转移&#xff1a;若个某个节点发生故…