前篇:
咱就是说有图有表格有代码。
目录
麻烦手机版的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]; |
类型 | 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是地址的地址,第一次用*后得到的还是一个地址,所以最后相当于我们解引用了两次呢。
含义 | 数据类型 | 地址(以i=2,j=2) | |
---|---|---|---|
a 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 个 元素 | int | 7 |
注意注意注意:表格的第二三行,虽然它们的地址相等,但他们的意义不全相等
另外,还有几个规律:加*等于加中括号
*(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.指向指针的指针(禁止套娃)
-
int a=100;
-
int *p=&a; //指针p的地址为&p ,p就是a的地址
-
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]; |
类型 | 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楠钰子.