四、数组指针与指针数组--普通仙女为指针怒码万字

前篇:

一二---普通仙女对指针的学习记录(从入门到学废)

三---普通仙女对指针的学习记录(从入门到学废)

咱就是说有图有表格有代码。

目录

1.回顾二维数组

2.指向数组元素的指针

3.数组指针---指向整个数组的指针

4.指针的兼容性

5.二维数组与函数形参

6.指针数组---指向行的指针

7.指向指针的指针(禁止套娃) 

8.指向数组元素的指针、数组指针、指针数组开小会


麻烦手机版的uu们滑动看一下这个表格~

把最后总结的表格放到前面来,害怕你们看不下去,然后错失我的表格
指向数组元素的指针数组指针指针数组
详细名称指向元素本身指向数组

指向行

存放指针的数组

定义int a[2][3];
    int *p;
    p=a[0];或&a[0][0]

int a[4][2];

int (*pz)[2] ;

pz=a;

int a[3][4]; 

int *p[3];
p[0]=a[0];

类型int *int *[2]int *
+1的含义下一个元素

pz+1下一行

*pz+1下一列

p+1该行下一个元素
访问方式a[i][j]=*(p+i*3+j)a[i][j]=*(*(p+i)+j)a[i][j]=*(p[i]+j)

这大概也展示了今天要分享的内容,有一些我以前很ran的点(就是混在一起的意思),配有一些练习。

马不停蹄码了两天,连微积分和线代的课业都往后退了!!!

希望对你的指针学习有帮助~~我们一起进步~~

下面我们先回顾一下二维数组。


1.回顾二维数组

接下来的讲解,以  int rabbit[4][2]={1,2,3,4,5,6,7,8}  为例;

欢迎基础好的你直接看后面总结的表格。

  • 首先,我们知道数组名rabbit是数组首元素的地址。而现在数组首元素指向一个内含两个int的数组。所以rabbit是相当于是这个小数组的地址。(是占用两个int的地址)
  • 而rabbit[0]内含两个int,可以理解为rabbit[0]是一个一维数组名,指向一维数组的第一个元素,也就是指向rabbit[0][0]---一个整数。(是占用一个int的地址)

不过因为第一行和第一行第一个元素的地址相同,即小数组和这个整数都开始于同一个地址,所以 rabbit 和 rabbit[0] 的值相同。

不过明显rabbit一次跨越一行(本例是两个int),rabbit[0]一次只走一个int

而且因为rabbit指向的值是一个地址rabbit[0],而这个地址的值是另一个地址&rabbit[0][0];

所以可以认为rabbit[0]的值是地址,rabbit的值是地址的地址


 接下来让他们两个动起来~

        rabbit+0 =  &rabbit[0]  =  &rabbit[0][0]

        rabbit+i  =  &rabbit[i]   =  &rabbit[i][0]

rabbit移动时,走一行,代表第i行的首地址

        rabbit[i]+0 = &rabbit[i]+0

        rabbit[i]+j = &rabbit[i][j]

rabbit[i]移动时,走一列,代表第i行第j列元素的地址


既然是地址,我们解引用一下看看;

        *(rabbit+i) = *rabbit[i] = rabbit[i][0]

        *(rabbit[i]+j) =  *(*(rabbit+i)+j) = rabbit[i][j]

在这里,因为rabbit是地址的地址,第一次用*后得到的还是一个地址,所以最后相当于我们解引用了两次呢。

二维数组表格(我们简化为a[][])     为了防止大家不喜欢rabbitQAQ
含义数据类型地址(以i=2,j=2)

a+0 

&a[0]

二维数组名

指向一维数组a[0]

0行的首地址

int (*)[2]6487552

a+i 

 &a[i]    

第i行首地址

int (*)[2]6487568

 a[i] 

 *(a+i)

&a[i][0]

第 i 行 第0列地址int *6487568

a[i]+j 

 *(a+i)+j

第 i 行 第 j 个 元素地址int *6487576

*(*(a+i)+j)

a[i][j]

第 i 行 第 j 个 元素int7

注意注意注意:表格的第二三行,虽然它们的地址相等,但他们的意义不全相等
  另外,还有几个规律:加*等于加中括号

*(a+i) == a[i];

*(a+i)+j = a[i]+j;

*(*(a+i)+j) = a[i][j];      ------(  *(a+i) )[j]       -------a[i][j]

练习4.1:

代码在这里在这里~可以自己试试看~下面也附上了我的结果~

#include<stdio.h>
int main()
{
	int a[4][2]={1,2,3,4,5,6,7,8};
	printf("a			%d\n",a);
	printf("&a[0]		%d\n",&a[0]);
	printf("a+2			%d\n",a+2);
	printf("&a[2]		%d\n",&a[2]);
	
	printf("a[2]		%d\n",a[2]);
	printf("*(a+2)		%d\n",*(a+2));
	printf("&a[2][0]	%d\n",&a[2][0]);
	
	printf("a[2]+2		%d\n",a[2]+2);
	printf("*(a+2)+2	%d\n",*(a+2)+2);
	
	printf("*(*(a+2)+2)	%d\n",*(*(a+2)+2));	
	
}

另外,我还画了一个简陋的图想要表达清楚一点,可惜能力有限,如果看不懂请忽略这个图;

(因为rabbit写不下,所以ra就相当于rabbit好不好嘛)这和lazy没有关系

 

2.指向数组元素的指针

定义方法与定义指向变量的指针变量一样,由于二维数组在空间内存放也是有顺序的,所以用p++也可以遍历。

由于p是指向二维数组元素的指针,所以p+1指向下一个元素。(此时就不用管行和列了)

不过我们是把a[0]即&a[0][0]赋给了指针。第i行第j列元素实际上是整个二维数组中的第i*c+j个元素。

a[i][j]=*(p+i*c+j)

练习4.2:

用指针输入、输出数组

#include<stdio.h>
int main()
{
	int a[2][3];
	int *p;
    for(p=a[0];p<a[0]+6;p++)
        scanf("%d",p);
	for(p=a[0];p<a[0]+6;p++)
	{
		if((p-a[0])%3==0)
		printf("\n");
		printf("%3d",*p);
	}
}

//函数
void InputArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol)//数组元素的行数和列数
							
{
    for(int i=0;i<nArrayRow;i++)
      for(int j=0;j<nArrayCol,j++)
       {
		scanf("%d",pArray);//逐个输入数组的每个元素
        pArray++;//每次指针加1,指向下一个元素
        }
}

void OutputArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol)//数组元素的行数和列数
{
    for(int i=0;i<nArrayRow;i++)
      for(int j=0;j<nArrayCol,j++)
    {
		printf("%d ",*pArray);//逐个输出数组元素,然后指针加1,指向下一个元素
        pArray++;
    }
		printf("\n");
}

练习4.3: 

用指向二维数组元素的指针,求二维数组中的最大数和最小数

#include<stdio.h>
void InputArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol);//数组元素的行数和列数
void OutputArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol);//数组元素的行数和列数
void CompareArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol,int *max,int *min);//数组元素的行数和列数

int main()
{
	int a[3][4],max,min;
	InputArray(a[0],3,4);
	OutputArray(a[0],3,4);
	CompareArray(a[0],3,4,&max,&min);
	printf("max=%d,min=%d",max,min);
}
void InputArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol)//数组元素的行数和列数
{
	for(int i=0;i<nArrayRow;i++)
	for(int j=0;j<nArrayCol;j++)
	{
		scanf("%d",pArray);
		pArray++;
	}	
}

void OutputArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol)//数组元素的行数和列数
{
	for(int i=0;i<nArrayRow;i++)
  	for(int j=0;j<nArrayCol;j++)
	{
		printf("%d ",*pArray);//逐个输出数组元素,然后指针加1,指向下一个元素
        pArray++;
	}
		printf("\n");
}

void CompareArray(int *pArray,//指向整型变量的指针,实参将二维数组首地址传递过来
				int nArrayRow, int nArrayCol,int *max,int *min)//数组元素的行数和列数
{
	*min=*pArray;
	*max=*pArray;
	for(int i=1;i<nArrayRow*nArrayCol;i++)
	{
		
		if( *max < *(pArray+i) )
		*max=*(pArray+i);
		if( *min > *(pArray+i) )
		*min=*(pArray+i);	
	}			
}

3.数组指针---指向整个数组的指针

声明一个指向二维数组的指针,这个指针指向内含2个int的数组;

所以:

int rabbit[4][2];

int (*pz)[2] ;

pz=rabbit;

pz被声明为指向一个数组的指针,该数组内含有两个int。括号是必须的,因为[ ]的运算级高于*。

pz是一维数组的首地址了,和rabbit类似,属于行指针,pz指向含2个元素的数组的首地址,p指向rabbit的首地址也就是rabbit[0],*pz指向rabbit[0]的首地址也就是rabbit[0][0],要访问rabbit[0][0],就是*(*p)了。

上面第一部分的表格就同样适用于pz了;可以让pz指向二维数组的某一行。

但是,一维数组指针变量维数  一定要和  二维数组的列数  相同

pz+i偏移i个rabbit数组大小的地址,也就是指向二维数组的第i行。

*pz+i指向当前数组的第i个元素

我们来一段程序试验一下如何使用:

练习4.4:

观察就好啦~

#include<stdio.h>
int main()
{
	int a[4][2]={1,2,3,4,5,6,7,8};
	int (*pz)[2];
	pz=a;
	printf("pz	%p	pz+1	%p\n",pz,pz+1);
	printf("pz[0]	%p	pz[0]+1	%p\n",pz[0],pz[0]+1);
	printf("*pz	%p	*pz+1	%p\n",*pz,*pz+1);

	printf("pz[0][0]	%d\n",pz[0][0]);
	printf("*pz[0]		%d\n",*pz[0]);
	printf("**pz		%d\n",**pz);
	
	printf("pz[2][1]	%d\n",pz[2][1]);
	printf("*(*(pz+2)+1)	%d\n",*(*(pz+2)+1));
}

练习4.5:

用指针输入二维数组

#include<stdio.h>
int main()
{
	int a[2][3];
	int (*p)[3];
	int i,j;
	p=a;
	for(i=0;i<2;i++)
	{
		for(j=0;j<3;j++)
		{
			scanf("%d",*p+j);
			//*p表示第i行首地址,*p+j表示第i行第j列元素的地址 
		 }
		 p++;//使p指向下一行 
	}
	//用于检验输入 
	p=a;
	for(i=0;i<2;i++)
	{
		for(j=0;j<3;j++)
		{
			printf("%d ",*(*p+j));
			//*p表示第i行首地址,*p+j表示第i行第j列元素的地址 
		 }
		 p++;//使p指向下一行 
	}
}

练习4.6:

输出任一列元素的值

#include<stdio.h>
int main()
{
	int a[2][3]={1,2,3,4,5,6};
	int (*p)[3];
	int i,j;
	p=a;
	scanf("%d,%d",&i,&j);
	printf("\na[%d][%d]=%d\n",i,j,*(*(p+i)+j));
}

练习4.7:

输出二维数组的元素,这次我们不用*(*(p+i)+j),因为p可以自增从而指向下一行,所以我们使用p++与*(*p+j),这样子,*p表示第i行首地址,*p+j表示第i行第j列元素的地址;

#include<stdio.h>
int main()
{
	int a[2][3]={1,2,3,4,5,6};
	int (*p)[3];
	int i,j;
	p=a;
	for(i=0;i<2;i++)
	{
		
		for(j=0;j<3;j++)
		{
			printf("a[%d][%d]=%-5d",i,j,*(*p+j));
			//*p表示第i行首地址,*p+j表示第i行第j列元素的地址 
		 }
		 p++;//使p指向下一行 
	}
	
}

练习4.8: 

用指向二维数组元素的指针,求二维数组中的最大数和最小数。

我们和4.3对比着来看,顺便看看指向二维数组元素的指针和数组指针的区别和联系;

  • 在形参部分:

        4.3是int *pArray,4.8是int (*pArray[4])(注意4.8的形参声明一定要指明列数)

  • 在遍历时:

        4.3是pArray+i或者pArray++,4.8是*(pArray+i)+j

  • 在含义上:

        4.3是从第一个无脑地指向下一个元素实现遍历,通过*(pArray+0)访问a[0][0],

  通过*(pArray+3)访问a[1][0],就相当于把二维数组看作一维数组区访问;

        4.8中pArray指向行,是按照行和列遍历。

#include<stdio.h>
#define N 100
void InputArray(int (*pArray)[4],int nArrayRow, int nArrayCol);
void OutputArray(int (*pArray)[4],int nArrayRow, int nArrayCol);
void CompareArray(int (*pArray)[4],int nArrayRow, int nArrayCol,int *max,int *min);

int main()
{
	int a[3][4],max,min;
	int (*p)[4]=a;
	InputArray(p,3,4);
	OutputArray(p,3,4);
	CompareArray(p,3,4,&max,&min);
	printf("max=%d,min=%d",max,min);
}
void InputArray(int (*pArray)[4],int nArrayRow, int nArrayCol)
{
	for(int i=0;i<nArrayRow;i++)
	for(int j=0;j<nArrayCol;j++)
	{
		scanf("%d",*(pArray+i)+j );
		
	}	
}

void OutputArray(int (*pArray)[4],int nArrayRow, int nArrayCol)
{
	for(int i=0;i<nArrayRow;i++)
  	for(int j=0;j<nArrayCol;j++)
	{
		printf("%d ",*(*(pArray+i)+j) );
	}
		printf("\n");
}

void CompareArray(int (*pArray)[4],int nArrayRow, int nArrayCol,int *max,int *min)
{
	*min=**pArray;
	*max=**pArray;
	for(int i=0;i<nArrayRow;i++)
  	for(int j=0;j<nArrayCol;j++)
	{
		
		if( *max < *(*(pArray+i)+j) )
		*max=*(*(pArray+i)+j) ;
		if( *min > *(*(pArray+i)+j)  )
		*min=*(*(pArray+i)+j) ;	
	}			
}

4.指针的兼容性

//参考C primer plus中10.7.2

尽管指针都是地址,指针之间的赋值也很严格,不像数值类型有时候不需要类型转换也可以赋值。

1.使用“&数组名”时,是取数组首地址;数组名或者&数组名[0]都是取得数组首元素地址

2.首地址+1得到的是跳过整个数组的地址,首元素地址+1得到的是下一个元素的地址。

假设有如下声明:

int *pt;        int (*pa)[3];        int ar1[2][3];        int ar2[3][2];        int **p2;

对于以下语句:

pt=&ar1[0][0]=ar1[0];        √都是指向int的指针

pt=ar1;                  ×无效,ar1指向第0行,pt指向int

pa=ar1;                 √都是指向行,即指向内含3个int类型元素数组的指针

pa=ar2;                ×无效,注意维度

p2=&pt;                √都是指向指针的指针,它指向的指针指向int

*p2=ar2[0];           √都是指向int的指针(ar2[0]是指向数组首元素的指针)

p2=ar2;                 ×无效

5、二维数组与函数形参

void functionnnn ( int a[ ][4] )

void functionnnn ( int (*p)[4] )

注意第一行的第一个方括号里是空白,空的方括号表示a是一个指针,其实也可以不为空白,不过编译器会忽略掉。

第二个方括号里是数组的维数,不可为空白!!!!当然也不能为变量,要确切的常量。

编译器会把数组表示法转为指针表示法,要知道a所指向对象的大小,a+1的意思就是“在该地址上加16个字节”,如果没有4,编译器就麻了。

练习4.9:

求和、求行和、求列和。

我们用两种等价的原型演示:

#include<stdio.h>
#define ROWS 3
#define COLS 4
void sum_rows(int ar[][COLS], int rows);
void sum_cols(int (* ar)[COLS], int rows);
int sum2d(int (*ar)[COLS], int rows);
int main()
{
	int junk[ROWS][COLS] = { {2,4,6,8},{3,5,7,9},{12,10,8,6} };
	sum_rows(junk, ROWS);
	sum_cols(junk, ROWS);
	printf("Sum of all elements = %d\n", sum2d(junk, ROWS));
	return 0;
}
void sum_rows(int ar[][COLS], int rows)
{
	int r, c, tot;
	for (r = 0; r < rows; r++)
	{
		tot = 0;//每算一行置零一次,得每一行的和
		for (c = 0; c < COLS; c++)
			tot += ar[r][c];
		printf("row %d:sum=%d\n", r, tot);
	}
}

void sum_cols(int(*ar)[COLS], int rows)
{
	int r, c, tot;
	for (c= 0; c < COLS; c++)
	{
		tot = 0;//每算一列置零一次,得每一列的和
		for (r= 0; r< rows; r++)
			tot += ar[r][c];
		printf("col %d:sum=%d\n", c, tot);
	}
}

int sum2d(int(*ar)[COLS], int rows)
{
	int r, c, tot;
	tot = 0;
	for (r = 0; r < rows; r++)
	{
		for (c = 0; c < COLS; c++)
			tot += ar[r][c];
	}
	return tot;
}

6.指针数组---指向行的指针

指针数组就是一个数组,里面放的都是指针。它是“指向数组的指针”的简称。真是捅了地址窝了。

int *p[4];方括号先与p结合成为一个数组,再由int*说明这是int指针数组,含有4个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素。

数组名是将该数组看作一维数组后的第一个元素的地址。(我个人理解为下一级别,比如二维数组下一级别是行,行的下一级别是元素本身);

比如在a[3][4]中,将a看做一维数组后它的每一个元素实际上是每一行,如果p指向a,就相当于p指向第0行;如果p指向a[0],就相当于p指向第一个元素a[0][0];

但p还是指向int的指针,所以不能直接把数组名给p,那样就是非要一个指向int的人指向一行

定义: int a[3][4];        int *p[3];
         for(int i=0;i<3;i++)   p[i]=a[i];

区别于数组指针(指向数组的指针)int (*p)[4];  括号优先表示p是一个指针,指向一个int一维数组,这个一维数组的长度是n,执行p+1时,p要跨过n个整型数据的长度。

练习4.9:

用指针数组求数组元素的和

p[i]=a[i];这个要放到循环里,每次让他指向不同的行,如果在循环外面只把a[0]给p[0],后面的元素是未定义的。

#include<stdio.h>
int main()
{
	int a[2][3]={1,2,3,4,5,6};
	int *p[2];
	
	int sum=0;
	for(int i=0;i<2;i++)
		for(int j=0;j<3;j++)
		{
			p[i]=a[i];
//这个要放到循环里,每次让他指向不同的行,如果在循环外面只把a[0]给p[0]后面的元素是未定义的。
			printf("%3d",*(p[i]+j) );
			sum+=*(p[i]+j);
		}
		
	printf("\nsum=%d",sum);
 } 

7.指向指针的指针(禁止套娃) 

  1. int a=100; 

  2. int *p=&a; //指针p的地址为&p ,p就是a的地址

  3. int **p1=&p;

这样a、*p、**p1都是100了;

剩下的放到下一节和字符串一起。链接在这里,可以移步,也可以等到下一节再看~

五、指针和字符串--普通仙女为指针怒码万字

8.指向数组元素的指针、数组指针、指针数组开小会

麻烦手机版的uu们滑动看一下这个表格~

总结
指向数组元素的指针数组指针指针数组
详细名称指向元素本身指向数组

指向行

存放指针的数组

定义int a[2][3];
    int *p;
    p=a[0];或&a[0][0]

int a[4][2];

int (*pz)[2] ;

pz=a;

int a[3][4]; 

int *p[3];
p[0]=a[0];

类型int *int *[2]int *
+1的含义下一个元素

pz+1下一行

*pz+1下一列

p+1该行下一个元素
访问方式a[i][j]=*(p+i*3+j)a[i][j]=*(*(p+i)+j)a[i][j]=*(p[i]+j)

呼呼呼~终于写完了zzzzzz

分享就结束啦,感谢阅读!!!!阿里嘎多!!!

十二月快乐(*^_^*)

2021.12.1

SThree楠钰子.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序和三三总有一个能跑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值