【Golang开发入门】一篇文章弄懂:值类型、指针类型

news2024/9/21 19:32:23

在这里插入图片描述

  • 博主简介:努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:数据结构、Go,Java等相关知识。
  • 博主主页: @是瑶瑶子啦
  • 所属专栏: Go语言核心编程
  • 近期目标:写好专栏的每一篇文章

在这里插入图片描述

目录

  • 一、前言:
  • 二、不同语言的值类型、指针类型比较
    • Go语言中值类型、指针类型:

一、前言:

昨天写的【Golang项目实战】手把手教你写一个备忘录程序|附源码——建议收藏

我写第二个版本代码的时候遇到如下问题:

在这里插入图片描述
这段代码报错的原因是memos是一个map类型的变量,而map类型的值不能直接被赋值。在修改map类型变量的值时,需要使用键来访问对应的值,并对值进行修改。

假设memos是一个map[int]*Memo类型的变量,其中键是备忘录的编号,值是指向备忘录的指针。在这种情况下,如果要修改某个备忘录的标题,可以使用如下的代码:

memos[id].Title = newMemoTitle

这段代码将会访问memos中编号为id的备忘录,并将其标题修改为newMemoTitle。注意,这里使用了指针类型的值来表示备忘录,因此可以直接访问备忘录的字段。

如果memos不是指针类型的变量,而是map[int]Memo类型的变量,那么在修改备忘录的标题时需要先将备忘录取出来,然后再修改标题。例如:

memo := memos[id]
memo.Title = newMemoTitle
memos[id] = memo

这段代码将会访问memos中编号为id的备忘录,并将其取出来赋值给一个新的变量memo。然后修改memo的标题,并将其重新赋值给memos中的编号为id的备忘录。

承接上文,为什么memos是map[int]Memo类型变量需要先将备忘录取出来,而不能memos[id].Head=newMemoHead这样直接赋值

memos是一个map[int]Memo类型的变量,其中键是备忘录的编号,值是备忘录的结构体,而结构体是值类型(非指针类型),在Go语言中,值类型的变量在赋值时会进行一次值拷贝,即将变量的值复制一份到新的内存地址中。因此,如果直接对memos[id]进行赋值,实际上是在对备忘录的副本进行操作,而不是对原备忘录进行操作,这样就无法实现真正的修改。

为了实现真正的修改,需要先将备忘录从memos中取出来,修改其字段的值,然后再将其放回到memos中。这样,就可以保证对原备忘录进行操作,而不是对备忘录的副本进行操作。因此,在map[int]Memo类型的变量中,修改备忘录的字段需要使用如下的代码:

memo := memos[id]
memo.Head = newMemoHead
memos[id] = memo

这段代码将会访问memos中编号为id的备忘录,并将其取出来赋值给一个新的变量memo。然后修改memoHead字段,并将其重新赋值给memos中的编号为id的备忘录。这样,就可以实现对原备忘录进行修改。

💡Summary

上面说了这么多呢,其实简单来说,Go语言的结构体是值类型通过map的键map[id],获得到的是原始结构体的值拷贝(一个副本)(如果是指针类型,那么获取的是结构体的指针!!!)值类型的结构体,就是获取到了一个重新开了内存,但存值和原来结构体一样的新结构体。你对它进行操作,根本不会影响原始备忘录。

(感觉熟悉Java的朋友和我一样可能还是不太清楚,可以看下面补充,结构体可以对应到Java中的类,类变量都是指针类型,所以像遇到这种值类型的结构体,我们才会如此不解)

二、不同语言的值类型、指针类型比较

值类型和指针类型在很多编程语言中都有类似的概念和特性,但具体实现和表达方式可能有所不同。

c++:
结构体可以定义为值类型指针类型,而且可以通过引用或指针来访问结构体的字段。在C++中,值类型的结构体在传递和复制时会进行 值拷贝,而指针类型的结构体则只是复制指向结构体的指针。这和Go语言中值类型和指针类型的行为类似。

💬"值拷贝"?
值类型的结构体来说,它们的值包含了所有的成员字段,当一个值类型的结构体变量被赋值给另一个变量时,或者作为函数参数传递时,都会进行值拷贝。这意味着在内存中会创建一个新的结构体对象,并将原始结构体对象的值复制到新对象中,两个结构体对象之间互不影响。因此,如果我们修改其中一个值类型的结构体变量的属性值,不会影响到其他的变量。

Java:
在Java中,所有的对象都是指针类型,因此对象的操作都是基于指针进行的。在Java中,可以使用关键字new来创建对象,这会分配一块内存用于存储对象的数据,并返回一个指向该内存区域的指针。在Java中,不能直接访问对象的内存地址,只能使用对象的引用来访问对象的字段和方法。

Python:

在Python中,对象的内存管理是由解释器自动进行的,因此不需要手动进行内存分配和释放。Python中的对象都是指针类型,而且对象的引用计数机制可以自动进行垃圾回收。在Python中,可以使用赋值语句将一个对象的引用复制给另一个变量,这样两个变量都指向同一个对象。但是,如果对其中一个变量进行修改,就会创建一个新的对象,而另一个变量仍然指向原来的对象。

总的来说,值类型和指针类型是编程语言中一种基础概念和特性,不同的语言可能会有不同的实现和表达方式,但其本质是相似的。

Go语言中值类型、指针类型:

在Go语言中,值类型和引用类型都有各自的适用场景和运用方式。以下是一些常见的示例:

  1. 值类型的运用示例(1)
// 定义一个点(Point)结构体
type Point struct {
    X, Y int
}

// 定义一个函数,用于计算两个点之间的距离
func distance(p1, p2 Point) float64 {
    dx := p2.X - p1.X
    dy := p2.Y - p1.Y
    return math.Sqrt(float64(dx*dx + dy*dy))
}

// 创建两个点并计算它们之间的距离
p1 := Point{1, 2}
p2 := Point{4, 6}
d := distance(p1, p2)
fmt.Println(d)

上述代码中,我们定义了一个Point结构体来表示二维平面上的一个点,该结构体是一个值类型。我们还定义了一个distance函数来计算两个点之间的距离。在计算距离时,我们将两个点作为参数传递给distance函数,由于Point是一个值类型,因此在传递参数时会进行值拷贝。这样,我们就可以在不改变原始数据的情况下计算两个点之间的距离。

  1. 值类型的运用示例(2)
type Memo struct {
    Head string
    // other fields
}

var memos map[string]Memo

func main() {
    memos = make(map[string]Memo)

    memo1 := Memo{Head: "Memo 1"}
    memos["1"] = memo1

    memo2 := memos["1"]
    memo2.Head = "Memo 2"

    fmt.Println(memos["1"].Head) // "Memo 1"
}

在上面的代码中,我们首先声明了一个map类型的变量memos,其中键值对的值类型为Memo。然后,我们创建了一个Memo结构体变量memo1,并将其添加到memos中。接着,我们通过memos[“1”]获取到了memo1的拷贝,并将其赋值给了memo2。然后,我们修改了memo2的Head属性,并打印了memos[“1”].Head的值。由于memos[“1”]中存储的是memo1的拷贝,因此memo2的修改并不会影响到memos[“1”],所以最终打印出来的结果是"Memo 1"。

相比之下,如果使用map[string]*Memo这样的类型来存储结构体指针,那么每次在map中存储一个值时,只会存储指向原始结构体的指针。这样,当修改map中的某个元素时,会直接影响原始的结构体变量。例如:

type Memo struct {
    Head string
    // other fields
}

var memos map[string]*Memo

func main() {
    memos = make(map[string]*Memo)

    memo1 := Memo{Head: "Memo 1"}
    memos["1"] = &memo1

    memo2 := memos["1"]
    memo2.Head = "Memo 2"

    fmt.Println(memos["1"].Head) // "Memo 2"
}

在上面的代码中,我们首先声明了一个map类型的变量memos,其中键值对的值类型为*Memo。然后,我们创建了一个Memo结构体变量memo1,并将其地址添加到memos中。接着,我们通过memos[“1”]获取到了指向memo1的指针,并将其赋值给了memo2。然后,我们修改了memo2的Head属性,并打印了memos[“1”].Head的值。由于memos[“1”]中存储的是memo1的指针,因此memo2的修改直接影响到了memos[“1”],所以最终打印出来的结果是"Memo 2"。

因此,如果希望在map类型的变量中存储结构体,并且希望修改map中的元素会影响到原始的结构体变量,应该使用map[string]*Memo这样的类型来存储结构体指针。

  1. 引用类型的运用示例
// 定义一个切片(Slice)变量
nums := []int{1, 2, 3, 4, 5}

// 定义一个函数,用于对切片进行排序
func sort(slice []int) {
    sort.Ints(slice)
}

// 对切片排序并输出结果
sort(nums)
fmt.Println(nums)

上述代码中,我们定义了一个nums变量来表示一个整数切片,该变量是一个引用类型。我们还定义了一个sort函数来对整数切片进行排序。在对切片排序时,我们将切片作为参数传递给sort函数,由于切片是一个引用类型,因此在传递参数时只是复制切片的指针。这样,我们就可以在不改变原始数据的情况下对切片进行排序,并输出排序后的结果。

总的来说,值类型和引用类型在Go语言中都有其各自的优势和适用场景。需要结合具体的应用场景和需求来选择合适的类型,并注意在使用值类型和引用类型时需要注意其特性和行为。


欢迎在评论区交流和留下你的想法和建议

如果对你有用,还请:💭评论+👍🏻点赞+⭐收藏+➕关注

在这里插入图片描述

  • Java岛冒险记【从小白到大佬之路】
  • LeetCode每日一题–进击大厂
  • 算法
  • C/C++
  • Go语言核心编程
  • 数据结构

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据分析之道-Pandas(二)】DataFrame

文章目录 专栏导读1、DataFrame简介2、DataFrame创建2.1字典创建DataFrame2.2列表创建DataFrame2.3ndarrays 创建DataFrame2.4CSV文件创建DataFrame 3、DataFrame基本操作3.1添加列3.2删除列 专栏导读 ✍ 作者简介:i阿极,CSDN Python领域新星创作者&…

什么是 TDSQL-C MySQL版 ---- 数据库开发者视角

我们从设计演化的角度来讲什么是 TDSQL-C MySQL 版本。 首先,我们有了一个纯净版 MySQL。它是一个单机数据库。存算分离:然后,我们把 MySQL 的存储引擎拿掉,换成云存储。这就成了存算分离。这时,在用户看来它还是一个…

测牛学堂:2023软件测试入门系列(软件测试方法之流程分析法)

流程分析法 流程就是用户为了完成一定的业务目的,需要进行的一系列的操作。 流程分析法又叫场景分析法,是编写测试用例最常用的方法之一。 流程分析法不校验单个功能点的正确性,只关心流程能不能走通。 流程分类的几个概念 场景&#xff1…

Arduno ESP8266接入中移OneNet动态显示实时数据

Arduno ESP8266接入中移OneNet动态显示实时上传数据 ✨本案例基于HTTP协议. 🌼网页查看动态数据: 📓Onenet产品创建 从全部产品服务点击进入-多协议接入 2.创建基于HTTP协议的产品 添加产品属性 创建产品成功后,可以开始添加具体的设备了。

本周一至周三总结

周一 学习如何进行竞品分析 对软件杯项目进行了竞品分析,测试了十余个强相关网站,为团队写好了竞品分析报告 分别对主要目标,竞品优劣点,竞品选择原因,产品创新点等进行了分析和阐述 周二 下午晚上刷了五道题 题解…

不是什么高深玩意,Arrays.asList、ArrayList.subList需要注意的坑

前言 集合是日常工作中几乎每天都在用的玩意,也是八股文中被翻烂的东西,诸如List、Map,确实很重要也很实用,但是不注意细节就比较容易踩坑。比较常见的就是今天要整理的Arrays.asList和ArrayList.subList。不是什么高深的东西&…

第一章设计模式前置知识

文章目录 软件设计模式的概念学习设计模式的必要性设计模式分类UML图类的表述方式类的表示方式 类与类之间的表示方式关联关系聚合关系组合关系依赖关系继承关系实现关系 软件设计原则开闭原则实例 里氏代换原则反例优化 依赖倒转原则组装电脑改进反例 接口隔离原则安全门案例 …

1分钟学会Midjourney十种绘图风格关键词

Midjourney最新V5版的卡通模型中最流行的就是皮克斯,今天介绍十种绘图风格。我们统一用如下描述词来绘制,每次只是风格不一样,对比看看。 首先我们先画一个皮克斯风格(Pixar),打开ai绘图软件,点击左上角的图像绘制&a…

centos主机测磁盘读写速度极限

下面将使用dd命令在CentOS主机上测试磁盘的极限速度 先测试磁盘的极限写入速度 dd if/dev/zero of/tmp/testfile bs1M count3072 convfdatasync,notrunc statusprogress 该命令将在/tmp目录下创建一个名为testfile的文件,并向其中写入3GB的数据 if/dev/zero&#x…

【算法】【算法杂谈】折纸问题(完全二叉树解法)

目录 前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本 思考感悟写在最后 前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介…

Camunda8流程引擎私有化安装部署快速入门

Camunda8是基于标准云原生架构设计开发的,所以官方优先推荐基于Kubernetes和docker的方式安装部署Camunda8,考虑到大部分开发者不一定具备Kubernetes环境和云原生相关知识,所以本文介绍如何在一台windows机器下手动安装运行Camunda8的方式&am…

Linux文件操作基础及基本I/O函数使用

文章目录 前言一、基础命令简单讲解二、文件描述符三、open、read、write函数讲解1.open函数2.read函数3.write函数 四、使用open、read、write操作标准输入输出总结 前言 Linux是一个基于文件系统的操作系统,文件操作是其中一项最基本的任务之一。通过文件操作&am…

什么!appium安装不上???快来试试这种方法吧!——appium的手动安装步骤教程

前言 相信你不少软件测试行业小伙伴应该在用npm安装appuim或者是cpm安装appuim途中也碰到下面一些报错吧,接下来小陈教你改为手动安装appium吧。 一、手动下载appium安装包 appuim手动安装包下载链接: appium / Appium.app / Downloads — Bitbucket &a…

《Netty》从零开始学netty源码(四十四)之PoolChunk释放内存

free 当PoolChunk需要释放内存空间时可调用free方法,具体的源码过程如下: 在这个过程中最重要的是第三步的collapseRuns方法,当释放了空间以后要更新runsAvail和runAvailsMap的信息,如果handle对应的内存空间的上边界以及下边界是…

什么是AMS?什么是WMS?

AMS和WMS AMS ActivityManagerService是Android系统中一个特别重要的系统服务,也是上层APP打交道最多的系统服务之一(几乎所有的APP应用都需要与AMS打交道)。 AMS与操作系统中的进程管理和调度模块很像,在Android系统中非常重要…

从“恰当”的项目管理工具中,了解自己的缺点

项目管理工具是为了帮助管理者,但管理者需要了解自己在特定情况下的“缺点”,才能从“恰当”的工具中获得“恰当”的帮助。如果你不知道在某个特定项目中自己(作为项目经理)的缺点,也不知道自己需要利用哪些好用的项目…

【Python_Opencv图像处理框架】边缘检测、轮廓检测、图像金字塔

写在前面 本篇文章是opencv学习的第四篇文章,主要讲解了边缘及轮廓检测的主要操作,并对两种图像金字塔简单的介绍了一下,作为初学者,我尽己所能,但仍会存在疏漏的地方,希望各位看官不吝指正😍 …

Kafka3.0.0版本——生产者数据有序与乱序

目录 一、生产经验数据有序二、生产经验数据乱序2.1、kafka1.x版本之前保证数据单分区有序的条件2.2、kafka1.x版本及以后保证数据单分区有序的条件2.3、kafka1.x版本及以后开启幂等性数据单分区有序的原因 一、生产经验数据有序 单分区内,数据有序。如下图partion…

React之购物车+动态获取参数+Hooks+Redux

1、redux-logger中间件 1️⃣:安装redux-logger依赖包 yarn add redux-logger 2️⃣:在store的配置文件index中配置 import {legacy_createStore as applyMiddleware,applyMiddleware} from reduximport counterReducer from ./counterReducerimport {c…

【论文写作】-我的第一篇论文形成记(投稿过程、课题来源、python与数学学习、实验设计、论文思路建立与写作、回复审稿人)

我的第一篇论文形成记 投稿过程背景记录课题来源-本科毕业设计python及数学学习实验设计调参阶段实验阶段 论文思路建立论文写作回复审稿人总结 投稿过程 2022年12月28日 投AIChE 2023年01月05日 AlChE编辑认为方向不太符合期刊定位,建议投其他期刊 2023年01月06日…