区别二者,字面上只看 后两个字即可:
数组指针 :它是一个指针,但是数据类型为数组,或者说指向数组
指针数组 :它是一个数组,该数组的元素都为地址值
一.数组指针
1.语法
var 变量名 *[数组大小] 数组类型:
var arrPtr *[size] Type
因为数组指针是一个指针,所以在定义时,先写 *, 表示定义一个指针,后面接数据类型
2.实例
一步一步创建,容易理解。
创建一个数组指针,并赋值:
var arrPtr * [4]int // 创建一个指针 arrPtr,指向一个数组
var arr = [4]int{1,2,3,4} // 创建一个数组并初始化
arrPtr = &arr // 将数组 arr的地址赋值给arrPtr
fmt.Println("将 arr 的内存地址赋值给数组指针 arrPtr, arrPtr=",ptr)
输出为:
将 arr 的内存地址赋值给数组指针 arrPtr, arrPtr= &[1 2 3 4]
注意,输出的结果不是地址(不是16进制的数),在Golang中,直接输出的是
&[元素1 元素2 …]
我们可以输出一下二者的地址:
fmt.Printf("arr 数组的地址为:%p\n", &arr)
fmt.Printf("arrPtr 存储的地址为:%p\n", arrPtr)
输出为:
arr 数组的地址为:0xc00000c3c0
arrPtr 存储的地址为:0xc00000c3c0
可以看到,它俩的输出时一样的,因为将数组 arr的地址赋值给了arrPtr,而arrPtr是一个指针,存储的是内存地址。
当然arrPtr也有自己的内存地址,我们可以看一下:
fmt.Printf("arrPtr 指针自己的地址为:%p\n", &arrPtr)
输出为:
arrPtr 指针自己的地址为:0xc000006028
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
注意:仅对访问下标时,寻址运算符允许不写!
二.指针数组
它是一个数组,该数组的元素都为地址值
1.语法
var 变量名 [数组大小] * 数组类型:
var ptrArr [size] *Type
因为指针数组是一个数组,所以在定义时,先写 [size], 表示定义一个数组,后面再接指针 * 和 数组的数据类型
2.实例
1.创建一个数组指针, 数组的类型为int,大小为4,并赋值:
var ptrArr [4]*int
a, b, c, d := 1, 2, 3, 4
arr2 := [4]int{a, b, c, d} // 拷贝四个变量的值为函数组元素
fmt.Println("数组 arr2 :", arr2)
ptrArr = [4]*int{&a, &b, &c, &d} // 存的都是内存地址
fmt.Println("指针数组 ptrArr :", 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 都是值类型,各自改变,互不影响
以上就是在学习Golang数组指针和指针数组时的一些笔记和感悟。
总结一下,数组指针和指针数组这两个概念的区分,记住两点即可:
- 后两字是啥,则它就表示啥
- 定义时,最先写的 代码 就已经表示了其是数组还是指针