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

1.介绍

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

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

2.数组指针

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

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

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

Go 复制代码
var arrPtr *[size] Type

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

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

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

运行结果:

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

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

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

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

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

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

输出为:

Go 复制代码
arrPtr 指针自己的地址为:0xc000006028

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

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

Go 复制代码
 x := 10
 p := &x  // 将指针 p 指向变量 x 的地址
 ​
 a := 10
 b := &a // 将指针 b 指向变量 a 的地址

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

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

输出结果:

Go 复制代码
 type of b:*int
 type of c:int
 value of c:10

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

3.通过指针访问数组

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

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

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

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

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

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

输出:

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

4.指针数组

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

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

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

Go 复制代码
var ptrArr [size] *Type

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

**实例:

  1. 创建一个数组指针, 数组的类型为 int,大小为 4,并赋值:**
Go 复制代码
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)
}

输出:

Go 复制代码
数组 arr2 : [1 2 3 4]
指针数组 ptrArr : [0xc0000140f0 0xc0000140f8 0xc000014100 0xc000014108]

2. 操作数据,查看变化

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

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

输出:

Go 复制代码
arr2 的值为: [100 2 3 4]
a 的值为; 1
ptrArr 的值为; 1

先看 a 的值为 1 解释:

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

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

ptrArr 的值为 1 解释:

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

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

我们可以查看一下 ptrArr0 的值和 a 的地址是否一致:

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

输出:

Go 复制代码
ptrArr[0] 的值: 0xc0000140f0
a 的内存地址为:0xc0000140f0

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

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

输出:

Go 复制代码
a 的值为: 1000
arr2 的值为: [100 2 3 4]

a 的值为: 1000 解析:

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

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

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

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

相关推荐
子兮曰6 分钟前
前端工具链的「Rust 化」:一场没有赢家的军备竞赛?
前端·后端·rust
爱勇宝1 小时前
从 Ctrl+CV 到 Enter:程序员正在失去什么
前端·后端·程序员
码事漫谈2 小时前
EdgeOne Makers + WorkBuddy:零基础也能快速搭建可上线的 AI 智能体(附图文教程)
后端
像我这样帅的人丶你还2 小时前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩2 小时前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
烤代码的吐司君2 小时前
Redis 数据结构 ZSet, BIT, HyperLogLog,Geo 空间数据
redis·后端
苏三说技术3 小时前
为什么越来越多的人使用FastAPI?
后端
JavaGuide3 小时前
比 iTerm2 更适合 Claude Code/Codex 的终端,我换成 Ghostty 了
人工智能·后端
DyLatte3 小时前
AI 时代,最危险的不是被替代,而是努力不沉淀
前端·后端·程序员
神奇小汤圆4 小时前
架构师必备:CPU使用率不均匀排查
后端