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 mainimport "fmt"func modifyValue(val int) {val = 100
}func main() {x := 10modifyValue(x)fmt.Println(x) // Output: 10 (原始值未被修改)
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

package mainimport "fmt"type Myint intfunc (m *Myint) modify() {*m = 10
}func main() {x := Myint(5)x.modify()fmt.Println(x) //output 10
}
  • 参数传递机制:调用方法时,编译器会隐式地取调用者的地址,传递给方法的接收者
  • 是否修改原始值:调用地址的副本间接修改原始值

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

package mainimport "fmt"type Myint intfunc (m *Myint) modify() {*m = 10
}func main() {x := Myint(5)(&x).modify()fmt.Println(x) //output 10
}
  • 参数传递机制:调用方法时,直接将调用者的地址传递给方法的接收者
  • 是否修改原始值:直接修改原始值

总结

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

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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

7-20 打印九九口诀表

题目链接&#xff1a;7-20 打印九九口诀表 一. 题目 1. 题目 2. 输入输出格式 3. 输入输出样例 4. 限制 二、代码 1. 代码实现 #include <stdio.h>int main(void) {unsigned int n;if (scanf("%d", &n) ! 1) {return -1;}for (int i 1; i < n; i) …

线程池详解

线程池 什么是线程池&#xff1a;线程池就是管理一系列线程的资源池。 当有任务要处理时&#xff0c;直接从线程池中获取线程来处理&#xff0c;处理完之后线程并不会立即被销毁&#xff0c;而是等待下一个任务。 为什么要用线程池 / 线程池的好处&#xff1a; **降低资源消…

FPGA开源项目分享——基于 DE1-SOC 的 String Art 实现

导语 今天继续康奈尔大学FPGA课程ECE 5760的典型案例分享——基于DE1-SOC的String Art实现。 &#xff08;更多其他案例请参考网站&#xff1a; Final Projects ECE 5760&#xff09; 1. 项目概述 项目网址 ECE 5760 Final Project 项目说明 String Art起源于19世纪的数学…

Taro打包生成不同目录

使用taro init创建taro项目时&#xff0c;taro默认打包目录是&#xff1a; /config/index.js outputRoot:dist默认的目录&#xff0c;编译不同平台代码时就会覆盖掉&#xff0c;为了达到多端同步调试的目的&#xff0c;这时需要修改默认生成目录了&#xff0c;通过查看官方文…

【MySQL数据库 | 第二十六篇】InnoDB基本数据存储单元以及存在问题

前言&#xff1a; InnoDB作为MySQL中最常用的存储引擎之一&#xff0c;承载着许多数据库系统的关键任务&#xff0c;如事务管理、并发控制和数据完整性保障。 然而&#xff0c;就像任何技术一样&#xff0c;InnoDB也并非完美无缺。在深入了解其工作原理和特性的同时&#xff…

ExoPlayer停止更新,建议升级到AndroidX Media3

1. 大家常用的ExoPlayer地址&#xff1a;GitHub - google/ExoPlayer: An extensible media player for Android ExoPlayer是谷歌官方提供的媒体播放库&#xff0c;大家在开发项目中经常使用ExoPlayer播放音视频&#xff0c;谷歌官方已经明确表示该库在2024-04-03停止更新&…

鸿蒙Native输出so动态库,并提供给第三方导入使用

前言&#xff1a; DevEco Studio版本&#xff1a;4.0.0.600 API:9 最近在学习鸿蒙的Native输出so动态库&#xff0c;下面就给大家分享下我的学习心得及在实现过程中遇到的问题。 实现需求&#xff1a;通过so库输出文本内容 “你好&#xff0c;鸿蒙&#xff01;” 参考资料…

CSS 使用 SVG 绘制动态皮筋与小球交互动画

CSS 使用 SVG 绘制动态皮筋与小球交互动画 效果展示 CSS 知识点 使用 animation 控制 SVG 的 path 属性执行动画使用 CSS 设置 SVG 部分属性 整体页面布局 <div class"elasic"><!-- 小球 --><div class"ball"></div><!-- 皮…

Nodejs 第六十二章(短链接)

短链接介绍 短链接是一种缩短长网址的方法&#xff0c;将原始的长网址转换为更短的形式。它通常由一系列的字母、数字和特殊字符组成&#xff0c;比起原始的长网址&#xff0c;短链接更加简洁、易于记忆和分享。 短链接的主要用途之一是在社交媒体平台进行链接分享。由于这些…

人工智能:更多有用的 Python 库

​ 目录 推荐 JupyterLab 入门 复杂的矩阵运算 其它人工智能和机器学习的 Python 库 推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 在进入主题之前&#xff0c;我们先讨论几个人…

利用Sentinel解决雪崩问题(二)隔离和降级

前言&#xff1a; 虽然限流可以尽量避免因高并发而引起的服务故障&#xff0c;但服务还会因为其它原因而故障。而要将这些故障控制在一定范围避免雪崩&#xff0c;就要靠线程隔离(舱壁模式)和熔断降级手段了&#xff0c;不管是线程隔离还是熔断降级&#xff0c;都是对客户端(调…

太赫兹探测器是太赫兹技术应用核心器件之一 我国研究成果不断增多

太赫兹探测器是太赫兹技术应用核心器件之一 我国研究成果不断增多 太赫兹探测器&#xff0c;是太赫兹&#xff08;THz&#xff09;应用的基础&#xff0c;是太赫兹成像、太赫兹通信等系统的核心器件&#xff0c;其性能直接决定太赫兹系统的优劣&#xff0c;地位极为重要&#x…

2024年同城网总流量全新生态,个人工作室落地式游戏玩法,单账户月入3万

我要为大家解读的是本地生活新项目&#xff0c;这个业务模式中&#xff0c;即使是低收入的玩法&#xff0c;一个月赚取万儿八千也是完全可行的。而高收入的玩法&#xff0c;一单的收入甚至能超过一万。目前&#xff0c;你的圈子里已经有一些同行业的人开始以个人工作室的形式去…

【机器学习】数学基础详解

线性代数&#xff1a;构建数据的骨架 数学对象 标量&#xff08;Scalar&#xff09; 标量是最基本的数学对象&#xff0c;代表了单个的数值&#xff0c;无论是整数还是实数。在机器学习中&#xff0c;标量可以用来表示一个模型的单个参数&#xff0c;如偏差&#xff08;bias&…

Collection与数据结构 Stack与Queue(二):队列与Queue

1. 队列 1.1 概念 只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾&#xff08;Tail/Rear&#xff09; 出队列&#xff1a;进行删除操作…

免费https详细教程

简单叙述一下https的定义和实现https的一些基本作用&#xff0c;然后会给到申请SSL证书的方式以及安装部署流程&#xff0c;最终实现网站的https访问。 随着互联网的快速发展&#xff0c;网络安全问题日益凸显。在互联网上传输敏感信息、进行在线交易和共享个人数据时&#xf…

09 Php学习:数组和排序

数组概念 在PHP中&#xff0c;数组是一种复合数据类型&#xff0c;用于存储多个值。以下是关于PHP数组的详细解释&#xff1a; 索引数组&#xff1a;索引数组是最基本的数组类型&#xff0c;其中每个元素都有一个唯一的数字索引&#xff0c;从0开始递增。 关联数组&#xff…

Qt for MCUs 2.7正式发布

本文翻译自&#xff1a;Qt for MCUs 2.7 released 原文作者&#xff1a;Qt Group高级产品经理Yoann Lopes 翻译&#xff1a;Macsen Wang Qt for MCUs的新版本已发布&#xff0c;为Qt Quick Ultralite引擎带来了新功能&#xff0c;增加了更多MCU平台的支持&#xff0c;并且我们…

概率论基础——拉格朗日乘数法

概率论基础——拉格朗日乘数法 概率论是机器学习和优化领域的重要基础之一&#xff0c;而拉格朗日乘数法与KKT条件是解决优化问题中约束条件的重要工具。本文将简单介绍拉格朗日乘数法的基本概念、应用以及如何用Python实现算法。 1. 基本概念 拉格朗日乘数法是一种用来求解…

Golang 开发实战day08 - Multiple Return values

&#x1f3c6;个人专栏 &#x1f93a; leetcode &#x1f9d7; Leetcode Prime &#x1f3c7; Golang20天教程 &#x1f6b4;‍♂️ Java问题收集园地 &#x1f334; 成长感悟 欢迎大家观看&#xff0c;不执着于追求顶峰&#xff0c;只享受探索过程 Golang 教程08 - Multiple R…