Go语言基本语法(三)指针

什么是指针

在Go语言中,"指针是一种存储变量内存地址的数据类型",意味着指针本身是一个特殊的变量,它的值不是数据本身,而是另一个变量在计算机内存中的位置(地址)。形象地说,就像存放了一个数据项在内存中的“门牌号”。

"它允许程序直接操纵内存中的数据",这句话意味着通过指针,程序能够绕过变量本身,直接到达变量在内存中的存储位置,并对那里的数据进行读取或修改。这种能力非常重要,因为:

1. **效率**:当数据结构很大时(如大型数组或结构体),直接操作其内存地址可以避免复制整个数据结构,从而节省时间和空间。
2. **灵活性**:在函数调用时,传递数据的指针而非数据本身的副本,可以使函数有能力修改调用者的数据,这在很多场景下是必要的,比如更新共享状态或配置。
3. **控制权**:指针提供了底层的内存访问能力,这对于系统编程、性能优化和一些高级数据结构的实现至关重要。

例如,如果你有一个很大的数组,想要修改其中的一个元素,直接通过指针定位到那个元素的内存位置并修改它,比起先复制整个数组或结构到函数内部再修改,显然更高效。此外,通过指针,你还可以创建动态数据结构,如链表、树等,因为每个节点可以指向下一个节点的位置。

相比之下,C/C++的指针以其高度灵活性闻名,允许自由的偏移和运算,这为系统级编程和大数据操作提供了强大工具,也是其高性能的来源。然而,这种灵活性也带来了风险,如内存泄漏、指针悬挂、缓冲区溢出等问题,这些安全漏洞常常成为黑客攻击的入口,也是操作系统频繁更新修复的原因之一。

指针的概念

指针地址和指针类型

在Go语言中,理解和操作指针时,"指针地址"和"指针类型"是两个核心概念:

指针地址

指针地址指的是一个变量在内存中的实际存储位置。在Go语言中,你可以使用`&`操作符来获取一个变量的地址。这个地址是一个无符号整数,但它通常以十六进制形式显示,代表了变量在内存中的确切位置。例如:

var num int = 10
var ptr *int = &num

在这个例子中,`&num`就是获取变量`num`的内存地址,并将其赋值给指针变量`ptr`,`ptr`的类型就是指向`int`类型的指针,即`*int`。

指针类型

指针类型定义了指针所指向的数据类型。每个指针都有一个明确的类型,它决定了该指针可以指向哪种类型的变量。在Go语言中,指针类型的声明语法是在数据类型前加上星号`*`。例如,`*int`表示一个指向整型变量的指针,`*string`表示一个指向字符串变量的指针。

指针类型的重要性在于,它确保了类型安全,意味着你不能错误地将一个类型的指针赋值给另一个不匹配类型的指针变量,除非通过类型断言或类型转换(在类型兼容的情况下)。

指针的使用

package main
import ("fmt"
)
func main() {var money int = 156var str string = "ppp"fmt.Printf("%p %p", &money, &str)
}

运行结果:

指针取值

当使用`&`操作符对普通变量进行取地址操作并得到变量的指针后,可以对指针使用`*`操作符,也就是指针取值,代码如下:

package main
import ("fmt"
)
func main() {// 准备一个字符串类型var day01 = "Hello World"// 对字符串取地址, tem类型为*stringtem := &day01// 打印tem的类型fmt.Printf("tem type: %T\n", tem)// 打印ptr的指针地址fmt.Printf("address: %p\n", tem)// 对指针进行取值操作value := *tem// 取值后的类型fmt.Printf("value type: %T\n", value)// 指针取值后就是指向变量的值fmt.Printf("value: %s\n", value)
}

运行结果:


取地址操作符&和取值操作符*是一对互补操作符,&取出地址,*根据地址取出地址指向的值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:

  • 对变量进行取地址操作使用&操作符,可以获得这个变量的指针变量。
  • 指针变量的值是指针地址。
  • 对指针变量进行取值操作使用*操作符,可以获得指针变量指向的原变量的值。

使用指针修改值

通过指针不仅可以取值,也可以修改值:

示例1:

package mainimport "fmt"// 交换函数
func swap(a, b *int) {// 取a指针的值, 赋给临时变量tt := *a// 取b指针的值, 赋给a指针指向的变量,*a = *b// 将a指针的值赋给b指针指向的变量*b = t
}func main() {// 准备两个变量, 赋值1和2x, y := 1, 2// 交换变量值swap(&x, &y)// 输出变量值fmt.Println(x, y)
}

运行结果:

*操作符作为右值时,意义是取指针的值,作为左值时,也就是放在赋值操作符的左边时,表示 a 指针指向的变量。其实归纳起来,*操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。

如果在 swap() 函数中交换操作的是指针值,会发生什么情况?可以参考下面代码:

package mainimport "fmt"func swap(a, b *int) {b, a = a, b
}func main() {x, y := 1, 2swap(&x, &y)fmt.Println(x, y)
}

运行结果:

结果表明,交换是不成功的。上面代码中的 swap() 函数交换的是 a 和 b 的地址,在交换完毕后,a 和 b 的变量值确实被交换。但和 a、b 关联的两个变量并没有实际关联。这就像写有两座房子的卡片放在桌上一字摊开,交换两座房子的卡片后并不会对两座房子有任何影响。

示例2:

package mainimport "fmt"func main() {// 声明一个整型变量var num int = 10// 声明一个指向整型的指针var ptr = &num//将50赋给ptr指针指向的变量*ptr = 50fmt.Println(num) //输出50fmt.Println(*ptr) //输出50
}

指针的指针

指针的指针,顾名思义,就是一个指针变量,它的值是另一个指针的地址。在Go语言中,正如你可以声明指向任何基本类型或复合类型的指针一样,你也可以声明指向指针的指针。这种多级指针可以用来表示更加复杂的内存关系,或者在某些情况下,为了通过函数传递指针并修改指针本身(而不仅仅是指针指向的值)时使用。

声明与初始化

假设你有一个整型指针*int,那么一个指向这个整型指针的指针就会是**int。声明和初始化一个指针的指针的方式如下:

package main
import "fmt"func main() {// 声明一个整型变量var num int = 10// 声明一个指向整型的指针var ptr *int = &num// 声明一个指向指针的指针(即指针的指针)var ptrToPtr **int = &ptr// 修改通过指针的指针访问的值**ptrToPtr = 20fmt.Println(num)       // 输出: 20fmt.Println(*ptr)      // 输出: 20fmt.Println(*ptrToPtr) // 输出: 地址,显示ptr的地址
}

使用场景

指针的指针在实际编程中的使用相对较少,但在某些特定场景下非常有用,例如:

  • 当你需要通过函数修改一个指针变量本身(比如让指针指向不同的内存地址)时。
  • 在配置或设置结构体的指针成员时,特别是这些成员也是指针类型。
  • 在某些高级的数据结构或底层系统编程中,用于复杂的数据操作和内存管理。

new() 函数

Go语言还提供了另外一种方法来创建指针变量,格式如下:

new(类型)

一般这样写:

str := new(string)
*str = "Go语言教程"
fmt.Println(*str)

new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

参考文章:(6 封私信 / 42 条消息) Go 语言怎么定义和使用指针? - 知乎 (zhihu.com)

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

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

相关文章

解决iview(view ui)中tabs组件中使用图片预览组件ImagePreview,图片不显示问题

同学们可以私信我加入学习群! 正文开始 前言一、问题描述二、原因分析三、解决方案总结 前言 最近在写个人项目的web端和浏览器插件,其中一个功能是base64和图片的转换。因为分成四个小功能,所以使用的iview的tabs来展示不同功能&#xff0c…

Liunx磁盘管理(上)

Liunx磁盘管理(中)-CSDN博客 目录 一.硬盘类型 机械硬盘(HDD) 固态硬盘(SSD) 二.插拔方式 1. 热插拔(Hot Swapping) 2. 冷插拔(Cold Swapping) 3. 模块…

回溯Backtracking Algorithm

目录 1) 入门例子 2) 全排列-Leetcode 46 3) 全排列II-Leetcode 47 4) 组合-Leetcode 77 5) 组合总和-Leetcode 39 6) 组合总和 II-Leetcode 40 7) 组合总和 III-Leetcode 216 8) N 皇后 Leetcode 51 9) 解数独-Leetcode37 10) 黄金矿工-Leetcode1219 其它题目 1) 入…

Git推送本地项目到gitee远程仓库

Git 是一个功能强大的分布式版本控制系统,它允许多人协作开发项目,同时有效管理代码的历史版本。开发者可以克隆一个公共仓库到本地,进行更改后将更新推送回服务器,或从服务器拉取他人更改,实现代码的同步和版本控制。…

[二叉树] 二叉树的前中后三序遍历#知二求一

标题:[二叉树] 二叉树的前中后三序遍历#知二求一 水墨不写bug (图片来源于网络) 正文开始: 其实这一类题就是考察对二叉树的结构理解,此类题目的二叉树一般通过数组传入,我们只需根据二叉树的就够特点对数…

Docker: 如何不新建容器 修改运行容器的端口

目录 一、修改容器的映射端口 二、解决方案 三、方案 一、修改容器的映射端口 项目需求修改容器的映射端口 二、解决方案 停止需要修改的容器 修改hostconfig.json文件 重启docker 服务 启动修改容器 三、方案 目前正在运行的容器 宿主机的3000 端口 映射 容器…

程序员出路在哪?技术变迁与时代背景的双重挑战

在这个充满不确定性的时代,焦虑似乎成了每个人心中不可避免的情绪,准备好,我要开始贩卖焦虑了 。 最近,裁员的消息真的太多了,下面是我最近看到的裁员消息: 2024 年 3 月份,字节内部公开信曝光…

pip是的配置

1 疑惑 当你安装了python后打开cmd命令行输入pip发现运行不起来 疑惑了吧不是说python有内置的吗,怎么运行不起来,很简单没有配置环境变量所以运行不了 2 如何打开环境变量配置 打开电脑的设置 找到关于点开高级系统设置 点开环境变量 点开后有系统变…

LLM大语言模型原理、发展历程、训练方法、应用场景和未来趋势

LLM,全称Large Language Model,即大型语言模型。LLM是一种强大的人工智能算法,它通过训练大量文本数据,学习语言的语法、语义和上下文信息,从而能够对自然语言文本进行建模。这种模型在自然语言处理(NLP&am…

windows11家庭版开启Hyper-v

前提:如果在控制面板中-->程序和功能-->启用和关闭windows功能-->没有Hyper-v 1.什么是Hyper-v? Hyper-v分为两个部分:底层的虚拟机平台、上层的虚拟机管理软件 2.Hyper-v安装 2.1新建hyper.cmd文件,写入下面的内容&…

变电站自动化控制系统应用案例分析

变电站自动化控制系统介绍 变电站自动化控制系统用于大中型企业变电站项目,这类企业变压器多,日耗电量大。把多个变压器集中到一个电器平台上,集中管理分析,优化厂区用电管理,从而达到集中控制、集中分析、集中管理的…

设计模式之建造者模式BuilderPattern(七)

一、建造者模式 建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 二、代码实例 1、OrderItem类 Data:这是Lombok中提供的Ge…