Bootstrap

C语言 #指针数组 #数组指针 #数组参数、指针参数

文章目录

前言

一、指针数组

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、指针数组的数组名(指针数组的元素为指针;不论几维数组)

;