变量和一维数组的指针比较容易理解,但二维数组与函数的指针较难理解,后期容易混了,所以梳理一下二维数组和函数指针的一些要点。
一、回顾变量和一位数组的指针
(一)变量的指针
内存的每个内存单元大小为一字节(8比特位),每个内存单元都有自己的独有的地址。x86系统由32根地址总线(每个地址线可以看作一个比特位),在x86系统下,地址长度为32个比特位(即4个int大小);同理在x64系统下,由64根地址总线,也就是内存单元的地址长度为64个比特位(即8个int大小)。
指针就是内存的地址,变量的指针就是这个变量存到的内存单元所对应的内存地址(4或者8个int大小)。
指针变量是用来存储地址信息的一种变量,可以类比理解,整型变量是用来存放int类型数据的变量,字符型变量是用来存放char类型的数据。因此,指针变量是用来存放指针的变量。
如上图, 指针变量有独特的表示方法,就是在定义指针变量时,用'*'来表示这个变量是个指针变量。如果有一个变量n,在它的前面加上'*',那就代表这个变量n是个指针变量,里面存储的数据类型是地址(4个或8个int长度的数据)。取地址运算符是'&',a是整型变量,&a意为取整型变量a的地址,*n=&a,意为将变量a的地址放到指针变量里。
(二)一维数组的指针
一维数组的地址就是数组名arr,如下图:
arr指向的地址是数组首元素的地址,也就是指针指向了第一个元素'0'所在的地址,查看0的地址: 会发现arr[0]的地址和整个数组arr的地址是一样的,但是他们的类型是不一样的。&arr[0]的地址类型是int*,意为这个地址是个int类型变量的地址,&arr的地址类型是int[5]*,意思是这个地址是包含5个int类型变量的数组的地址。
int[5]是什么意思?代表arr这个数组的类型(不是地址类型),是int[5],与数组地址类型int[5]*要区分开。
都是同样一个地址,int*和int[5]*的区别在于,指针移动时的移动跨度,即移动1个单位,指针挪动多少个字节,如下图:
会发现第一个元素移动了4个字节,正好一个整型int的距离,而数组地址移动的距离更大,移动了0x0000014(16进制)个字节,就是20个字节,5个整型int的距离。用一个图来说:
由此可见,指针移动一个单位的距离,它的移动跨度是有指针的类型决定的。
二、二维数组的指针
二维数组的指针理解起来更复杂一些。
有一个二维数组arr[3][3],它的数组类型为int[3][3]。
继续查看&arr[0](等同于数组名arr)的类型是int[3]*,也就是&arr[0]是一个指向包含3个int类型元素的指针;&arr地址类型intarr[3][3]*,这是一个指向3*3的二维数组的指针。虽然他们指向的地址都是0x010ff910,但是它们的类型不一样。
再拿出&arr[0][0]看一下:
地址也是0x010ff910,类型是int*,代表这个指针指向一个int类型的变量。
接下来,让这3个指针都移动一个单位:
由上图可知,arr[0][0],移动了4个字节,也就是移动了一个int类型的长度;arr[0]移动了12个字节(上图是16进制,算出来是地址增加了12),也就是移动了3个字节,正好一个一维数组的长度;arr移动了36个字节的长度(9个整型类型的长度),表示指针移动到了整个二维数组后的那个内存单元地址。
指针移动如下图:
三、二维数组指针迷惑行为
有一个数组int a = [3][4] = {0};运用关系操作放sizeof计算一些数据。
(1)计算a[0][0]占用多少字节
(2)计算a[0]占用多少字节
(3)计算a[0]+1占用多少字节
(4)计算*(a[0]+1)占用的字节
(5)计算a+1占用的字节
(6)计算*(a+1)占用字节大小
(7)计算&a[0]+1占用的字节
(8)计算*(&a[0]+1)占用字节大小
(9)计算*a占用的字节
(10)计算a[3]占用的字节大小