一文了解 Go 中的指针和结构体

news2025/7/13 11:31:42

一文了解 Go 中的指针和结构体

  • 前言
  • 指针
    • 指针的定义
    • 获取和修改指针所指向变量的值
  • 结构体
    • 结构体定义
    • 结构体的创建方式
  • 小结

耐心和持久胜过激烈和狂热。

前言

前面的两篇文章对 Go 语言的基础语法和基本数据类型以及几个复合数据类型进行介绍,本文将对 Go 里面的指针和结构体进行介绍,也为后续文章做铺垫。

指针

Go 语言中,指针可以简单理解是一个地址,指针类型是依托于某一个类型而存在的,例如 Go 里面的基本数据类型 intfloat64string 等,它们所对应的指针类型为 *int*float64*string等。

指针的定义

  • 语法格式:var 指针变量名 *数据类型 = &变量
    & 为取地址符号,通过 & 符号获取某个变量的地址,然后赋值给指针变量。

    import (
    	"fmt"
    )
    
    func main() {
    	var num int = 666
    	var numPtr *int = &num
    	fmt.Println(numPtr)  // num 变量的地址值 0xc00001c098
    	fmt.Println(&numPtr) // 指针变量本身的地址值 0xc00000a028
    }
    

    npmPtr 指针变量指向变量 num0xc00001c098num 变量的地址,0xc00000a028 为指针变量本身的地址值。

    在这里插入图片描述

  • 使用 new(T) 函数创建指针变量

    import (
    	"fmt"
    )
    
    func main() {
    	numPtr := new(int)
    	fmt.Println(numPtr)  // 0xc000122058
    	fmt.Println(*numPtr) // 0
    }
    

    new(T) 函数为每个类型分配一片内存,内存单元保存的是对应类型的零值,函数返回一个指针类型。

  • 错误的类型地址赋值

    func main() {
    	var num float64 = 666
    	var numPtr *int = &num // cannot use &num (value of type *float64) as type *int in variable declaration
    }
    

    当指针所依托的类型与变量的类型不一致时,Go 编译器会报错,类型不匹配。

获取和修改指针所指向变量的值

  • 通过指针获取所指向变量的值
    func main() {
    	var num int = 666
    	var numPtr *int = &num
    
    	// 获取 num 的值
    	fmt.Println(*numPtr) // 666
    }
    
    对指针使用 * 操作符可以获取所指向变量的值。
  • 通过指针修改所指向变量的值
    import (
    	"fmt"
    )
    
    func main() {
    	var num int = 666
    	var numPtr *int = &num
    
    	// 修改 num 的值
    	*numPtr = 555
    	fmt.Println(num) // 555
    }
    
    同时也可以对指针使用 * 操作符修改所指向变量的值。

结构体

通过上一篇文章,我们了解了数组和切片的特点,它们可以存储一组相同类型的数据,而结构体,它可以由 0 个或多个字段组成,每个字段的数据类型可以一样,也可以不一样。结构体可以表示一个对象,例如:人,人包含的一些字段有姓名、性别和年龄等。

结构体定义

语法格式:

type XXX struct {
	/*
	结构体字段定义区域
	*/
}

XXX 为结构体的名字,下面以人为对象,引入结构体

// Person定义一个人的结构体
type Person struct {
	// 姓名
	Name string
	// 年龄
	Age int
	// 性别
	Sex string
	// 身份证号
	idNumber string
}

上述代码定义了人的结构体 Person,包含四个字段,字段的命名规则和变量是一样的,前三个字段首字母大写,可以被包外访问,第四个字段首字母小写,表示只能在包内访问。
除了上述的定义方式以外,结构体里还可以内嵌结构体

// Person 定义一个人的结构体
type Person struct {
	// 姓名
	Name string
	// 年龄
	Age int
	// 性别
	Sex string
	// 身份证号
	idNumber string
}

// Phone 手机结构体
type Phone struct {
	// 品牌
	Brand string
	// 拥有者
	Owner Person
}

上述代码定义了 Person 结构体和 Phone 结构体,Phone 结构体拥有两个字段,分别为 Brand 品牌和 Owner 拥有者,Owner 属性的类型,指定为我们所自定义的 Person 结构体。以这种方式定义的结构体字段,我们可以称为嵌入字段(Embedded Field)。也可以将这种字段称为匿名字段。

结构体的创建方式

  • 1、声明一个结构体变量
    // Person 定义一个人的结构体
    type Person struct {
    	// 姓名
    	Name string
    	// 年龄
    	Age int
    	// 性别
    	Sex string
    	// 身份证号
    	idNumber string
    }
    
    func main() {
    	var person Person
    	fmt.Println(person.Name)     // ""
    	fmt.Println(person.Age)      // 0
    	fmt.Println(person.Sex)      // ""
    	fmt.Println(person.idNumber) // ""
    
    	// 修改结构体字段的值
    	person.Name = "chenmingyong"
    	fmt.Println(person.Name) // "chenmingyong"
    }
    
    
    • 通过声明一个结构体变量,隐式创建一个结构体变量,这个结构体变量各个字段值默认为零值,也就是对应类型的默认值。
    • 结构体属性的值,可以通过 变量.字段名 的方式获取,同时也可以通过此方式对字段值进行修改。
  • 2、使用复合字面值进行显式初始化结构体对象
    import "fmt"
    
    // Person 定义一个人的结构体
    type Person struct {
    	// 姓名
    	Name string
    	// 年龄
    	Age int
    	// 性别
    	Sex string
    	// 身份证号
    	idNumber string
    }
    
    func main() {
    	person := Person{
    		"chenmingyong",
    		18,
    		"男",
    		"xxxxxxxxx",
    	}
    
    	fmt.Println(person) // {chenmingyong 18 男 xxxxxxxxx}
    }
    
    • 上述代码根据字段的顺序进行赋值,实际上,Go 语言并不推荐我们用这种方式进行赋值,因为可能会带来一些问题,例如结构体的某个中间的字段被删除或者字段的顺序改变了,那么我们需要维护对应代码,调整赋值的顺序。
    • 上述创建结构体的方式有弊端,因此我们需要换一种方法,如下第三种方法。
  • 3、 使用 field:value 形式的复合字面值进行显式初始化结构体对象
    import "fmt"
    
    // Person 定义一个人的结构体
    type Person struct {
    	// 姓名
    	Name string
    	// 年龄
    	Age int
    	// 性别
    	Sex string
    	// 身份证号
    	idNumber string
    }
    
    func main() {
    	person := Person{
    	    Sex:      "男",
    	    Age:      18,
    	    Name:     "chenmingyong",
    	    idNumber: "xxxxxxxxx",
        }
    
    	fmt.Println(person) // {chenmingyong 18 男 xxxxxxxxx}
    }
    
    通过以上的方式,我们就不被字段的顺序所约束了。
  • 4、通过 new(T) 函数创建结构体指针
    // Person 定义一个人的结构体
    type Person struct {
    	// 姓名
    	Name string
    	// 年龄
    	Age int
    	// 性别
    	Sex string
    	// 身份证号
    	idNumber string
    }
    
    func main() {
    	person := new(Person)
    	(*person).Name = "chenmignyong"
    	fmt.Println((*person).Name) // chenmignyong
    	// 简化赋值,底层自动转换成 (*person).Age = 18
    	person.Age = 18
    	fmt.Println(person.Age) // 18
    }
    
    前面提到过,访问指针所指向变量的值,需要使用 * 操作符,但在结构体这里有点特殊,如果不加 * 的话,底层会将 person.Age = 18 转成 (*person).Age = 18

小结

本文对指针和结构体进行了介绍,也指出使用指针和结构体时需要注意的一些地方。因为本文是基于了解的层面去写的文章,一些深入的知识并没有提到,然后也没有提到结构体的方法,是因为打算留到后面和函数一起去介绍。

如果本文对你有帮助,欢迎点赞收藏加关注,如果本文有错误的地方,欢迎指出!

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

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

相关文章

机器学习-(手推)线性回归-最小二乘法(矩阵表达)、几何意义

一、最小二乘法(矩阵表达)误差平均分散每个样本 如下数学推到过程(手推!!!): 数据介绍: D{(x1,y1),(x2,y2),......(xn,yn), Xi(P维列向量&…

行列向量的维数和个数的关系【三秩相等作为桥梁】

前置知识 1.列向量组维数增加时,向量组的极大无关组增加(或不变)。 2. 三秩相等 向量组证明 直观证明 这两个列向量显然是相关的。 这两个列向量当a和b取k和2k的时候相关(k为任意常数),当不是k和2k的时…

【2-Docker安装部署ElasticSearch和Kibanan详细步骤】

一.知识回顾 【0.ElasticSearch专栏在这里哟,想要学习的可自行进入专栏学习】 【1-ElasticSearch的基本介绍与用途、ElasticSearch中一些基本的概念、倒排索引的基本概念】 二.Docker安装部署ElasticSearch 2.1 docker pull 从镜像仓库中拉拉取ElasticSearch的镜像…

【零基础入门SpringMVC】第三期——请求域添加数据与视图

一、域对象共享数据 SpringMVC 中有哪些域对象? Request请求域,代表一次请求,从浏览器开启到关闭Session请求域,代表一次会话,从服务器开启到关闭【一次getSession获得了cookie,这个会话没关闭,…

Romantics三大浪漫(编译原理+操作系统+计算机图形学)

Romantics三大浪漫 一、编译原理1.1 研究翻译的科学1.2 编译器和解释器1.3 编译的流程(JIT为例)1.4 词法分析器1.5 多有限状态机提取Token- 实现词法分析器lexer1.6 实现流的peek和putBack操作一、编译原理 本章目标: 提升编程能力 区别于面向研究人员、学者的编译原理教学&a…

CSS学习笔记(三)

her~~llo,我是你们的好朋友Lyle,是名梦想成为计算机大佬的男人! 博客是为了记录自我的学习历程,加强记忆方便复习,如有不足之处还望多多包涵!非常欢迎大家的批评指正。 目录 一、CSS 的三大特性 1.1 层叠…

mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)

mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存)MyBatis的缓存机制MyBatis的一级缓存MyBatis的二级缓存二级缓存的相关配置MyBatis缓存查询的顺序整合第三方缓存EHCacheEHCache配置文件说明:MyBatis的缓存机制 MyBatis作为持久化框架&…

社区故事|SmartX 用户社区技术发烧友独家专访

小伙伴们,SmartX 用户社区已经陪伴我们走过近两年的时光,这期间有一千多位小伙伴加入我们,共同讨论问题、分享经验。今天,SmartX 用户社区的一线记者小乐为我们带来了独家采访,揭秘社区中两位技术发烧友的幕后故事&…

葡萄糖-聚乙二醇-转铁蛋白|Transferrin-PEG-Glucose|转铁蛋白-PEG-葡萄糖

转铁蛋白又名运铁蛋白 transferrin,TRF,siderophilin)还可以提供PEG接枝修饰葡萄糖,葡萄糖-聚乙二醇-转铁蛋白,Transferrin-PEG-Glucose,转铁蛋白-PEG-葡萄糖 中文名称:葡萄糖-转铁蛋白 英文名称:Glucose…

Java学习——Servlet服务器请求响应程序

Servlet服务器程序 1. Servlet的概念 Servlet(Server Applet):运行在Web服务器端(Tomcat)的小程序。 Servlet的主要作用:接收客户端浏览器的请求,还可以为客户端浏览器做出响应。 学习Servl…

戴尔笔记本重装系统按f几进入

有不少使用戴尔笔记本电脑的用户对于u盘重装系统中的按f几进入u盘启动的操作不熟悉,导致自己无法独立完成戴尔笔记本重装系统的步骤怎么办?其实相关的方法不难,下面小编就教下大家戴尔笔记本重装系统按f几进入u盘启动项安装。 工具/原料&…

【培训】MMEdu离线版的使用:实现石头剪刀布图像分类的检测

一、MMEdu离线版的使用 1.双击XEdu v1.0.exe解压缩到某个盘,会是一个文件夹XEdu 2.进入XEdu,双击运行“点我初始化.bat”,等待至运行结束命令提示符窗口自动关闭 3.双击运行“jupyter编辑器.bat”,将会打开一个网页版jupyter&…

第五站:操作符(第二幕)

在前面的文章中我们详细讲解了操作符的一些内容, 今天我们来继续了解操作符剩余的内容 操作符第一幕的传送门在这:第五站:操作符(第一幕) 目录 七、关系操作符 八、逻辑操作符 1.基础知识 2.几道经典的题目 九、条…

视频压缩软件哪个好?万兴优转:好用的视频无损压缩软件

如今,无论是学生党,上班族还是专业的视频制作者,都会遇到视频文件体量太大,需要对视频文件进行压缩的时候,但是又会担心视频压缩以后,画质受损,清晰度不如从前,所以一个好用的视频压…

机器学习笔记之高斯网络(三)高斯马尔可夫随机场

机器学习笔记之高斯网络——高斯马尔可夫随机场引言回顾:马尔可夫随机场——团、势函数高斯马尔可夫随机场点势函数关联的项边势函数相关的项关于多元高斯分布学习任务的核心思想关于条件独立性的总结引言 上一节介绍了高斯贝叶斯网络(Gaussian Bayesian Network,G…

c语言tips-带参main函数

0.写在最前 最近因为工作需要开始重新学c语言,越学越发现c语言深不可测,当初用python轻轻松松处理的一些数据,但是c语言写起来却异常的复杂,这个板块就记录一下我的c语言复习之路 1. main函数的两种表现形式 main函数是c/cpp语言的…

python破解wifi教程

目录 一、安装pywifi 二、构建wifi密码字典 三、python实现wifi破解 四、结果展示 4.1 选择要破解的wifi名称 4.2 获取字典 4.3 成功破解 一、安装pywifi pywifi是在python中一个用于操作无线接口的模块,可以跨平台使用,Windows和Linux都支持 …

Linux下Jenkins服务搭建及配置

一、简介 Jenkins是一个开源软件项目,是基于Java开发的一种持续集成工具,用于监控持续重复的工作,旨在提供一个开放易用的软件平台,为软件的持续集成提供了一种便捷的方式。 二、Jenkins自动化部署实现原理 Jenkins的自动化部署实…

现代密码学导论-14-基于伪随机发生器的EAV安全

目录 3.3.3 基于伪随机发生器的EAV安全 用伪随机发生器进行加密的图示 CONSTRUCTION 3.17 一种基于伪随机发生器的私钥加密方案 THEOREM 3.16 基于伪随机发生器的私钥加密方案的EAV安全 THEOREM 3.16 的证明 最后来理一下 3.3.3 基于伪随机发生器的EAV安全 用伪随机发生…

pandas是什么以及pandas的三种数据结构Series、DataFrame、MultiIndex的创建方式函数代码

一、pandas概述 pandas是2008年WesMcKinney开发出的专门用于数据挖掘的开源python库,以Numpy为基础,借助numpy模块在计算方面性能高的优势,基于matplotlib,能够简便的画图,具有独特的数据结构 pandas特点 图表的可读…