文章目录
前言
一、指针数组
1、概念:
2、指针数组有什么用呢?
二、数组指针
1、数组指针的定义
2、数组名与 &数组名 的区别
3、数组指针如何初始化?
4、数组指针的用法
三、根据代码区分 指针数组 和 数组指针
四、数组参数、指针参数
1、一维数组传参
2、二维数组传参
3、一级指针传参
4、二级指针传参
总结
前言
路漫漫其修远兮,吾将上下而求索。
一、指针数组
指针数组是一个存放指针的数组。这里就简单讲述,想要了解地更清楚可以戳此链接:http://t.csdnimg.cn/7R5S7
1、概念:
在前面我们学过字符数组、整型数组等,字符数组即数组中的元素是char 类型,char ch [10]; 则表示数组ch 中有10个为char 类型的元素; 整型数组即数组中的元素是 int 类型,int arr [10] ; 则表示数组arr 中有10个为int 类型的元素;(假设有10个元素)显然指针数组就应该其数组有10个元素为指针,即 int* p1[10] ; 则说明数组 p1中有10个为 Int * 类型的元素;
在 int * p1[10] ; 中p1 先与 [10] 结合,说明 p1 为数组,而其前面的int* 代表着数组p1 中的元素为 int * 类型;同理数组的元素也可以是 char * 、short * 、 long * 、 float * 、 double * 等类型。
2、指针数组有什么用呢?
举例:利用指针数组模拟实现二维数组
注:此处并不为二维数组,因为二维数组在内存中是连续存放的;而这里的模拟的二维数组并不是连续存放在一块空间中;
代码如下:
#include<stdio.h>
int main()
{
int arr1[] = { 1,1,1,1,1 };
int arr2[] = { 2,2,2,2,2 };
int arr3[] = { 3,3,3,3,3 };
int* parr[3] = { arr1,arr2,arr3 };
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 5; j++)
{
printf("%d ", *(*(parr + i) + j)); //利用地址
//printf("%d ", parr[i][j]);//利用下标进行访问
}
printf("\n"); //打印完一行就换行
}
return 0;
}
代码运行结果如下:
分析:arr1 、arr2 、 arr3 是整型数组;因为数组名代表着数组的首元素地址,所以数组parr 中的元素为数组名,本质就是 int* 类型的元素;所以指针数组parr 的类型为 int* ; 数组名可以看作数组名,也可以当作数组首元素的地址;故而想要访问指针数组中存放的数组名中的元素便有两种方式,一是通过数组的下标进行访问;二是利用地址的方式进行访问;
一:
利用下标访问数组parr 中的元素 --> parr [i] ; parr数组中的元素就是数组名,即 parr[0] 相当于 arr1, parr[1] 相当于 arr2 , parr [2] 相当于 arr3 ; 那么访问arr1 arr2 arr3 中的元素也可以利用下标的形式,即得到 parr [i][j] ;
二:
利用地址的方式来访问 parr 中的元素--> 数组名parr 代表着数组parr中首元素的地址,即指针,可以利用指针+1 来跳过一个元素再解引 --> *( parr + i ),所以得到了数组parr 中的元素;然而parr 中的数组名又可以看作其首元素地址 --> * (*( parr + i ) + j) ;
二、数组指针
1、数组指针的定义
之前我们学过整型指针: int * p1,* 代表着变量 p1 为指针变量,指针p1 指向对象的类型为 int ; 字符指针 : char * p2, * 代表着变量 p2 为指针变量,指针 p2 指向对象的类型为char ; 浮点型指针: float * p3 ,* 代表着变量 p3 为指针变量,指针变量p3 指向对象的类型为 float ;等,那么数组指针呢?应该就是指向数组的一个指针变量;
在学习数组的时候,曾说过数组的类型,详情可以戳此链接:http://t.csdnimg.cn/DTudO
注:例如: int arr[10] ; --> 整型数组arr的类型为 int [10] ; 即去掉数组名剩下的为数组的类型;
如果将以上例子中的数组arr 的地址存放起来 --> int (*p) [10] = &arr;
* 与变量 p 结合代表着变量p 为指针变量,int [10] 为数组arr 的类型,代表着数组有10 个为int 类型的元素;
注:这里的 [ ] 的优先级高于 * ,所以要用() 将 *p 括起来,以保证 p 先与 * 结合。
2、数组名与 &数组名 的区别
eg . int arr [10];
显然,数组名arr 代表着其首元素的地址;
数组名不是为首元素地址吗?为什么此处 sizeof(arr) 的结果为40 byte? 因为数组名不能只能理解为其首元素地址;
数组名通常表示的都是其首元素的地址;
但是有两个例外:
1、sizeof(数组名) ,这里的数组名表示整个数组,所以计算的是整个数组的大小
2、&数组名,这里的数组名表示的依然是整个数组,所以 &数组名 取出的是整个数组的地址;
分析:&arr[0] 、arr 针对的是数组中的首元素地址,即此地址的类型为一个整型,也就是 int* ,那么这些地址 +1 便会跳过 4 byte;而&arr 取出来整个数组的地址,即此地址的类型为40byte ,那么&arr +1 便会跳过 40byte ;
观察代码的运行结果也可得知,0136FBD8 --> 0136FBDC 之间差距了 4byte; 而 0136FBD8--> 0136FC00 --> 十六进制差了:28 --> 十进制:2*16+8*1 = 40,单位为字节;
3、数组指针如何初始化?
注:想要存放数组的地址,指针变量类型中的[ ]里必须要标明数组的元素个数
站在数组arr 的角度来看,它有10个元素,但站在指针变量 p 的角度来看,它认为自己指向的数组里面没有元素,即为0--> 指针变量p 的类型为 int (*)[0] ;
所以初始化数组指针时,一定要标明数组元素的个数;
4、数组指针的用法
数组指针不适合用于一维数组中,至少是用到二维、三维数组之中;
例二:(常见用法)
代码如下:
#include<stdio.h>
void print(int(*p)[4], int r, int c)
{
int i = 0;
int j = 0;
for (i = 0; i < r; i++)
{
for (j = 0; j < c; j++)
{
printf("%d ", *(*(p + i) + j)); //利用地址进行访问
//printf("%d ", p[i][j]); 利用下标进行访问
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { {1,2,3,4},{1,2},{1,2,3}};
print(arr, 3, 4);//二维数组的首元素是其第一行的元素,即一维数组
return 0;
}
代码运行结果如下:
注:二维数组数组名代表着其首元素的地址,而二维数组的首元素是其一行元素,即一维数组,所以在函数形参部分要用数组指针来接收 (当然,实参传的是二维数组,也可以用二维数组来接收);既然形参是一维数组,即一维数组名,而一维数组名又为其首元素地址;于是乎此处便又有两种方式来访问二维数组arr 中的所有元素;一是利用数组的下标进行访问,二是利用地址进行访问;
利用数组下标进行访问: p[i][j] ; 形参 int(*p)[4] 接受了实参 arr ,arr 为二维数组,其数组名代表着其第一排元素的地址即一维数组的地址,而变量 p 为指向一维数组的指针变量,即 可以将p看作二维数组名arr ;那么利用数组的下标进行访问即可得到 p[i][j] ;
利用地址进行访问:*(*(p + i) + j) ;形参中变量 p为一个指向一维数组的指针变量;指针变量的类型为 int ( * )[4] ,所以 p+1 ,便会跳过此一维数组而访问到下一个一维数组(即二维数组的下一个元素);又可以将一维数组名当作其首元素的地址,*( p + i ) 为一维数组的数组名,*( p + i) + j 代表着访问到各个元素的地址,想要得到元素就要对地址进行解引用操作,即 *(*(p + i) + j ) ;
三、根据代码区分 指针数组 和 数组指针
int arr[5] ;
int * parr1[10] ;
int (*parr2)[10] ;
int (*parr3[10]) [5];
分析:
1、arr 与 [ ] 先结合,代表着 arr 为数组,即数组arr 有10个int 类型的元素;所以arr 是整型数组;
2、 由于 [ ] 的优先级高于 * ,故而 parr1 先于 [ ] 结合,代表着parr1 为数组,即数组 parr1 有10个为 int * 类型的元素;所以 parr1 是整型指针数组;
3、( ) 将* 与 parr2 括起来,代表着parr2 先与 * 结合,代表着变量 parr2 为指针变量,指针变量 parr2 指向了有10个为 int* 元素的数组,即此数组的类型为 int (*) [10] ;所以 parr2 为数组指针;
4、由于[ ] 的优先级高于* ,所以parr3 先与 [ ] 结合,代表着parr3 为数组,即有10个为 int (*)[5] 类型的数组,所以数组 parr3 为存放数组指针的数组;所以parr3 为数组指针数组;(可以理解为二维数组)
四、数组参数、指针参数
1、一维数组传参
一维数组的数组名有两种理解方式,一是理解为数组名代表着整个数组,想要访问数组中的元素的话就用下标访问即可;二是,将一维数组名当作其首元素的地址,那么想要访问此一维数组中的元素就要利用地址进行访问;
所以形参接收形式上可以划分为两种,一是用一维数组来接受,二是利用其元素类型的指针来接收;
注:形参用一维数组来接收实参传过来的一维数组时,形参部分可以不用写数组元素的个数,但是写了也不会报错,因为编译器根本就不会用这个值;
例3:
int arr1[10] = {0} ;
若是将数组arr1传参,那么形参部分便可以写作:
int arr[ ] //形参部分用一维数组来接收,省略了数组的元素个数
int arr[10] //形参部分用一维数组来接收,并未省略了数组的元素个数
int * p //形参部分用传过来数组元素类型的指针来接收
同理,例4:
int* arr2[10] = { 0 } ;
若是将数组arr2传参,那么形参部分便可以写作:
int* arr [ ] //形参部分用一维数组来接收,省略了数组的元素个数
int* arr [10] //形参部分用一维数组来接收,并未省略了数组的元素个数
int** p //形参部分用传过来数组元素类型的指针来接收;数组arr2 中的元素类型为int* ;
此处 int** p第二个 * 代表了变量p 是指针变量,即指针变量p 指向的对象类型为 int* ;
2、二维数组传参
同理,二维数组的数组名也有两种理解,一是理解为数组名代表着整个数组,想要访问数组中的元素的话就用下标访问即可;二是,将一维数组名当作其首元素的地址,只不过二维数组的首元素是其第一行的元素,那么想要访问此二维数组中的元素就要利用地址进行访问;
所以二维数组的形参接收形式上可以划分为两种,一是用二维数组来接受,二是利用此二维数组首元素(其第一行元素)的类型的指针来接收;
注:
1、形参用二维数组来接收实参传过来的二维数组时,形参部分可以不用写数组行的信息,但是写了也不会报错,因为编译器根本就不会用这个值;但是不可省略列的信息;
2、二维数组的首元素为其第一行的元素
例4:
int arr3 [3][5] = { 0 } ;
若是将数组arr3传参,那么形参部分便可以写作:
int arr[ ][5] //形参部分用二维数组来接收,省略了数组行的信息
int arr[3][5] //形参部分用二维数组来接收,没有省略了数组行的信息
int (*p) [5] //形参部分用此二维数组的首元素类型的指针来接收;
//二维数组首元素的类型为int [5] , * 代表着变量 p 为指针变量,指针变量p 的类型为 int (*) [5] ,指针变量指向的对象的类型为 int [5];
3、一级指针传参
若是实参传一级指针,那么实参是怎么样的类型,形参就用什么类型的指针接收即可;
那么有哪些数据传参需要用一级指针变量来接受呢:
1、一级指针变量
2、对变量进行& 操作
3、数组的数组名(其数组的元素不为指针,数组名为其首元素地址;不论几维数组)
4、二级指针传参
若是实参传二级指针,那么实参是怎么样的类型,形参就用什么类型的指针接收即可;
那么有哪些数据传参需要用二级指针变量来接受呢:
1、二级指针变量
2、对一级指针变量进行& 操作
3、指针数组的数组名(指针数组的元素为指针;不论几维数组)
总结
1、指针数组是一个存放指针的数组。
2、想要访问指针数组中存放的数组名中的元素便有两种方式,一是通过数组的下标进行访问;二是利用地址的方式进行访问;
3、数组名通常表示的都是其首元素的地址;
但是有两个例外:
1、sizeof(数组名) ,这里的数组名表示整个数组,所以计算的是整个数组的大小
2、&数组名,这里的数组名表示的依然是整个数组,所以 &数组名 取出的是整个数组的地址;
4、数组指针的初始化:想要存放数组的地址,指针变量类型中的[ ]里必须要标明数组的元素个数
5、[ ] 的优先级高于 *
6、那么有哪些数据传参需要用一级指针变量来接受呢:a、一级指针变量 b、对变量进行& 操作 c、数组的数组名(其数组的元素不为指针,数组名为其首元素地址;不论几维数组)
7、那么有哪些数据传参需要用二级指针变量来接受呢:a、二级指针变量 b、对一级指针变量进行& 操作 c、指针数组的数组名(指针数组的元素为指针;不论几维数组)