go语言中的数组指针和指针数组的区别详解

news2024/11/13 13:20:39

1.介绍

大家知道C语言之所以强大,就是因为c语言支持指针,而且权限特别大,c语言可以对计算机中任何内存的指针进行操作,这样自然而然也会带来一些不安全的因素,所以在golang中,「取消了对指针的一些偏移,翻转等算术运算」(+、-、++、--)所以使用起来更安全。

  • 数组指针 :它是一个指针,但是数据类型为数组,或者说指向数组
  • 指针数组 :它是一个数组,该数组的元素都为地址值

2.数组指针

 在Go语言中,数组是一个固定长度的相同类型元素的序列。数组在声明时必须指定其长度,并且一旦创建,其长度就不能改变。

数组指针是指指向数组的指针。在Go语言中,你可以使用`&`操作符来获取数组的地址,从而创建一个指向数组的指针。数组指针的类型是`*[长度]类型`。

var 变量名 *[数组大小] 数组类型:

var arrPtr *[size] Type

初始化指针可以通过 new 函数来分配内存并返回指针的地址:

p := new(int)  // 分配一个 int 类型的内存,并将指针 p 指向该内存

 因为数组指针是一个指针,所以在定义时,先写 *, 表示定义一个指针,后面接数据类型

运行结果: 

 注意,输出的结果不是地址(不是 16 进制的数),在 Golang 中,直接输出的是&[元素 1 元素 2 ……]

我们可以输出一下二者的地址:

fmt.Printf("arr 数组的地址为:%p\n", &arr)   
fmt.Printf("arrPtr 存储的地址为:%p\n", arrPtr)

可以看到,它俩的输出时一样的,因为将数组 arr 的地址赋值给了 arrPtr,而 arrPtr 是一个指针,存储的是内存地址。

当然 arrPtr 也有自己的内存地址,我们可以看一下:

fmt.Printf("arrPtr 指针自己的地址为:%p\n", &arrPtr)

输出为:

arrPtr 指针自己的地址为:0xc000006028

2.1获取指针的地址和解使用

通过 & 操作符可以获取变量的地址,例如:

 x := 10
 p := &x  // 将指针 p 指向变量 x 的地址
 ​
 a := 10
 b := &a // 将指针 b 指向变量 a 的地址

 使用 * 操作符可以解引用指针,获取指针指向的值 :

fmt.Println(*p)  // 输出指针 p 指向的值,即变量 x 的值

 输出结果:

 type of b:*int
 type of c:int
 value of c:10

 取地址操作符 & 和取值操作符 * 是一对互补操作符,& 取出地址,* 根据地址取出地址指向的值。

3.通过指针访问数组

访问数组的元素可以通过下标来访问,比如:arr [0] 即可访问数组 arr 的第一个元素。

但是我们学习了指针数组,所以尝试使用指针数组来访问元素内容,在 Golang 中,取地址的操作为 * (寻址运算符) 。

因此,我们先取存储的地址 *arrPtr,访问到数组 ,然后再下标取值 *arrPt[0],代码如下:

*arrPtr[0]
  • 实际上,这段代码编译就会报错,因为在 Golang 中 * 寻址运算符和 [] 中括号运算符的优先级是不同的!
  • [] 中括号是初等运算符
  • 寻址运算符是单目运算符
  • 初等运算符的优先级是大于单目运算符的,因此先参与计算的是 arrPtr[0],arrPtr[0] 其实就是数组的第一个元素,就是数字 1。
  • 数字 1 必然是 int 类型,而不是一个地址,因此针对数字 1 使用 * 寻址运算符自然也就发生了错误。

解决问题的办法很简单,就是添加一个小括号,更改运算优先级即可:  

(*arrPtr)[0]
  • 不过因为 * 在 Golang 中,建立了 arrPtr := &arr 这种类似地址关系后,* 允许不写。
  • 所以,访问时候可以直接写成 arrPtr[0]。事实上在工作开发过程中,这种写法反而更加常见。实战代码:
fmt.Println("(*arrPtr)[0] 通过指针访问数组的第一个元素:", (*arrPtr)[0])
fmt.Println("arrPtr[0] 通过指针访问数组的第一个元素:", arrPtr[0])

 输出:

(*arrPtr)[0] 通过指针访问数组的第一个元素: 1
arrPtr[0] 通过指针访问数组的第一个元素: 1

4.指针数组

在Go语言中,指针数组是指一个数组,其元素时指针,指针元素通常用于存储多个指针,这些指针可以指向不同类型的数据结构,如结构体,数组,基本数据类型等等。

它是一个数组,该数组的元素都为地址值

var 变量名 [数组大小] * 数组类型:

var ptrArr [size] *Type

因为指针数组是一个数组,所以在定义时,先写 [size], 表示定义一个数组,后面再接指针 * 和 数组的数据类型

实例:
1. 创建一个数组指针, 数组的类型为 int,大小为 4,并赋值:

package main
import "fmt"

func main() {
 var ptrArr [4]*int 
 a,b,c,d := 1,2,3,4
  
 arr2 :=[4]int{a,b,c,d}//拷贝四个变量的值为函数组元素
 fmt.Println(arr2)

 ptrArr =[4]*int{&a,&b,&c,&d}//拷贝四个变量的地址值给函数组元素
 fmt.Println(ptrArr)
}

输出:

数组 arr2 : [1 2 3 4]
指针数组 ptrArr : [0xc0000140f0 0xc0000140f8 0xc000014100 0xc000014108]

2. 操作数据,查看变化

(1).arr2 的第一个元素改变,a 会不会变化,ptrArr 会不会变化?

arr2[0] = 100     
fmt.Println("arr2 的值为:", arr2)                      
fmt.Println("a 的值为;", a)               
fmt.Println("ptrArr 的值为;", *ptrArr[0])

输出:

arr2 的值为: [100 2 3 4]
a 的值为; 1
ptrArr 的值为; 1

先看 a 的值为 1 解释:

在 Golang 中,int,float,bool,string,array,struct 都属于值类型,数据拷贝时都是值拷贝,拷贝副本过来。

因此,尽管 arr2[0] = 100 执行了,只是修改了 arr2 的值,原来 a 的值不会受任何影响。因此,a 的值仍为 1

ptrArr 的值为 1 解释:

ptrArr 是指针数组, 该数组存储都是 指针,也就是 a,b,c,d 四个变量的内存地址。

其中,*ptrArr[0] 存储的是 a 的内存地址;a 没变, *ptrArr[0] 也不会变。所以输出仍为 1

我们可以查看一下 ptrArr[0] 的值和 a 的地址是否一致:

fmt.Println("ptrArr[0] 的值:", ptrArr[0])
fmt.Printf("a 的内存地址为:%p\n", &a)  // %p 占位符表示地址值

输出:

ptrArr[0] 的值: 0xc0000140f0
a 的内存地址为:0xc0000140f0

(2). 指针数组变化,a,b,c,d 会不会改变? 数组 arr2 会不会改变?

*ptrArr[0] = 1000  // 指针数组的第一个元素地址指向的值发生改变
fmt.Println("a 的值为:", a)       
fmt.Println("arr2 的值为:", arr2)

输出:

a 的值为: 1000
arr2 的值为: [100 2 3 4]

a 的值为: 1000 解析:

首先要明白一点:*ptrArr[0] = 1000 这段代码不会编译报错,因为 ptrArr 是指针数组,按照运算符的执行顺序,先 ptrArr[0] 获取 a 的地址,然后再 *a,这样获取的就是 a 的值

其实,解析的已经差不多了,ptrArr[0] 本来就是 a 的内存地址,所以 *ptrArr[0] = 1000 执行后,就改变了 a 的值

arr2 的值为: [100 2 3 4] 解析:

arr2 拷贝了 a,b,c,d 值的副本,a 的改变和 arr2 没有关系的,各不会受影响。a 和 arr2 都是值类型,各自改变,互不影响

 

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

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

相关文章

清理Go/Rust编译时产生的缓存

Go Mac 1T的磁盘频频空间高级,发现是/Users/yourname/Library/Caches/go-build 目录占用了大量空间。 此目录保存来自 Go 构建系统的缓存构建工件。 如果目录太大,请运行go clean -cache。 运行go clean -fuzzcache以删除模糊缓存。 当时直接手工清理了…

C++——多线程编程(从入门到放弃)

进程:运行中的程序 线程:进程中的进程 线程的最大数量取决于CPU的核心数 一、将两个函数添加到不同线程中 demo:两个函数test01()和test02(),实现将用户输入的参数进行打印输出1000次 将这两个函数均放到独立的线程t1和t2中&…

STM32 的 CAN 通讯全攻略

目录 一、CAN 通讯概述 二、 CAN 通讯原理 1.ISO11898 标准下的物理层特征 2.CAN 协议的帧类型 3. 总线仲裁介绍 4.位时序 5.STM32 CAN 控制器简介 6.标识符筛选器 三、软件设计 1.发送流程 1.1初始化 CAN 控制器 1.2准备发送数据 1.3 将数据填充到发送缓冲区 1.4…

初始c++的继承

概念: 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构&#xff0c…

Java高级Day43-类加载

117.类加载 静态和动态加载 反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强 动态加载:运行时加载需要的类,如果运行时不用该类…

集群聊天服务器项目【C++】(五)网络模块和业务模块

经过前面介绍相关的库和工具,比如Json、CMake、muduo等,我们可以开始编写本项目的代码了。 1.项目目录创建 一般一个项目由以下结构组成: bin文件夹存放:可执行程序build文件夹存放:编译过程中的临时文件include文…

消失的数去哪里了

大家好,我是大圣,最近消失了很长一段时间了,之前答应粉丝要更新的文章也没有按时更新。其实我这段时间去闭关修炼去了,现在满血归来啦,之前答应粉丝的文章都会陆续发出来的。 消失的 Count 去哪了 今天给大家分享一个…

BolckingQueue

队列 队列的特点先进先出(FIFO)。 如图: 进入队列的顺序是1,2,3,那么出队列的顺序只能是1,2,3,不可能是其他顺序,这是由队列的特点保证的。 保存数据的基本…

彻底理解浅拷贝和深拷贝

目录 浅拷贝实现 深拷贝实现自己手写 浅拷贝 浅拷贝是指创建一个新对象,这个对象具有原对象属性的精确副本 基本数据类型(如字符串、数字等),在浅拷贝过程中它们是通过值传递的,而不是引用传递,修改值并不…

基于yolov8的茶叶病害检测系统python源码+onnx模型+评估指标曲线+精美GUI界面

【算法介绍】 基于YOLOv8的茶叶病害检测系统,是利用深度学习技术,特别是YOLOv8这一先进的目标检测算法,来精准识别和监测茶叶生长过程中出现的各种病害。该系统通过无人机、地面机器人或固定摄像头等设备,定期采集茶园的高分辨率…

力扣刷题(6)

两数之和 II - 输入有序数组 两数之和 II - 输入有序数组-力扣 思路: 因为该数组是非递减顺序排列,因此可以设两个左右下标当左右下标的数相加大于target时,则表示右下标的数字过大,因此将右下标 - -当左右下标的数相加小于targ…

??Ansible——ad-hoc

文章目录 一、ad-hoc介绍二、ad-hoc的使用1、语法2、ad-hoc常用模块1)shell模块2)command模块3)script模块4)file模块5)copy模块6)yum模块7)yum-repository模块8)service模块9&#…

优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序

遗传算法(Genetic Algorithm, GA)是一种启发式搜索算法,用于寻找复杂优化问题的近似解。它模拟了自然选择和遗传学中的进化过程,主要用于解决那些传统算法难以处理的问题。 遗传算法的基本步骤: 初始化种群&#xff0…

【GO语言】Go语言详解与应用场景分析,与Java的对比及优缺点

Go is an open source programming language that makes it easy to build simple, reliable, and efficient software. Go是一种开源编程语言,可以轻松构建简单、可靠和高效的软件。 文章目录 一、引言二、Go语言详解1. 简史2. 特点3. 核心库 三、应用场景四、与Ja…

comfyui中,sam detector与yoloworld图像分割算法测试以及影响

🍖背景 图像处理中,经常会用到图像分割,在默认的comfyui图像加载中就有一个sam detector的功能,yoloworld是前一段时间公开的一个更强大的图像分割算法,那么这两个差别大吗?在实际应用中有什么区别吗&…

普推知产:明知商标驳回也要去申请注册!

有个去年加的网友让普推知产商标老杨看在32类申请如何,去年是把33类的申请复审下来,这个网友想的名称都是存在已存在的商标名称,直接都是申请不下来的,需要申请和再加驳回复审。 去年那个在33类的名称,当时查过只有一个…

函数(下)

static 代码1的test函数中的局部变量i是每次进⼊test函数先创建变量(⽣命周期开始)并赋值为0,然后 ,再打印,出函数的时候变量⽣命周期将要结束(释放内存)。 代码2中,我们从输出结果…

论文阅读-Demystifying Misconceptions in Social Bots Research

论文链接: https://arxiv.org/pdf/2303.17251 目录 摘要: Introduction Methodological issues Information leakage Cherry-picking(采摘樱桃) Straw-man methodology (稻草人) Data biases Conceptual issu…

Spring高手之路23——AOP触发机制与代理逻辑的执行

文章目录 1. 从整体视角学习Bean是如何被AOP代理的2. AOP代理的触发机制2.1 postProcessAfterInitialization方法源码分析2.2 wrapIfNecessary方法源码分析2.3 时序图演示触发机制 3. AOP代理逻辑的执行3.1 AOP代理如何使用拦截器3.2 proceed方法源码分析3.3 时序图 1. 从整体视…

【Linux】线程锁条件变量信号量生产消费者模型线程池

文章目录 线程概念线程控制接口和线程id线程优缺点线程互斥和条件变量锁和条件变量相关接口POSIX 信号量生产消费者模型阻塞队列实现生产消费者模型环形队列实现生产消费者模型简易懒汉线程池自旋锁和读写锁(了解) 线程概念 在操作系统的的视角下&#x…