【原创】浅谈指针(九)二维数组和多级指针相关
本文仅在博客园发布,其他网站均为盗取,请自觉支持正版:https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html
前言
最近在学校里面看见有人写的代码出错了,如下:
void dfs(int graph[][],int used[][],int x,int y);
这样的代码必然会出错(从语法的角度),有感而发,写下这点内容。
最近也是好久不写干货了,这次来多写点干货。
1.指针做函数参数
1.1.一维数组(复习)
#include<bits/stdc++.h>
using namespace std;
void f(int a[]){
cout<<sizeof(a)<<endl;
}
int main(){
int a[]={1,2,3};
cout<<sizeof(a)<<endl;
f(a);
}
在64位机器输出12 8,在32位机器输出12 4.
通过这个实验,我们得出的结论是:*当一维数组当做函数的参数传递时,它会被当做指向数组第一个元素的指针。
1.2.指向数组的指针
上一节中我们的试验,只是指向数组的第一个元素的指针,并非指向数组的指针。
真正的指向数组的指针是这个东西:
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[]={1,2,3};
int (*p)[3]=&a;
cout<<sizeof(*p)<<endl;
}
输出是12,也就是3个sizeof(int)。
可以看到,*p就是这个数组a,如果使用内存图示的画法,应该是如下的:
那么,有人会问,这样和指向数组首个元素的指针有什么区别呢?当然,区别很大。
我们假设指向数组首个元素的指针为q,那么,当执行p++;q++;
时候,
数组会变成上面的样子,也就是说,q增加的是sizeof(int),而p增加的是sizeof(a),也就是3个int的位子。
1.3.二维数组的寻址方式
在我们人类的理解中,数组a[2][3]是这样排列的:
但是在机器的理解中,实际上是这样排列的:
我们做一个实验:
#include<bits/stdc++.h>
using namespace std;
int main(){
int a[2][3]={1,2,3,4,5,6};
for(int i=0;i<2;i++){
for(int j=0;j<3;j++){
printf("%p ",&a[i][j]);
}
printf("\n");
}
}
结果如下:
00000000006ffe00 00000000006ffe04 00000000006ffe08
00000000006ffe0c 00000000006ffe10 00000000006ffe14
没错,是按照线性排列的。
而我们通常所熟知的a[i][j],它的寻址方式,实际是如下的:
*(*(a+i)+j)
我们看到下面的图:
此时,a[0]是指向数组的指针,指向的是数组{1,2,3},a[1]指向的则是数组{4,5,6}。
那么,a[0]+1,就是指向元素2的指针,再进行解引用*,得到的就是数值2.
即:*(a[0]+1)=a[0][1]
推广开来就是上面的二维数组的展开式:a[i][j]=*(*(a+i)+j)
1.4.二维数组做函数参数
仿照1.1节中的实验,我们在做一个类似的。
#include<bits/stdc++.h>
using namespace std;
int f(int a[2][3]){
cout<<sizeof(a)<<endl;
}
int main(){
int a[2][3]={1,2,3,4,5,6};
cout<<sizeof(a)<<endl;
f(a);
}
输出:24 8(64位)或24 4(32位)
由此,我们发现,二维数组做函数参数时,同样是传递了指针。
这里值得注意的是,同一机型,我们一般要同时测试32位和64位的输出结果,才能更好判断。如果某个输出值,在32位和64位下有两倍的关系,一般就是用到了指针。
某些调试环境(例如DEV C++),可以选择32位和64位的调试模式,
我们得出的结论是:二维数组作为函数的参数传递,传递的是指向数组的指针。
那么也就是说,这个函数和下面的函数同义。
int f(int (*a)[3]){
for(int i=0;i<2;i++){
for(int j=0;j<3;j++){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}
回到章节开头的问题,为什么函数参数的声明中不能写used[][]?
答案非常简单,a[i][j]等同于*(*(a+i)+j)
,其中,
根据指针运算的原则,*(a+i)
,实际加上的不是i而是i*sizeof(a[0])
这样一来,因为在函数参数中,a[0]大小未知,导致必须手动指定数组的宽度
否则由于宽度不同,导致无法解释数组
如果把声明写成(*a)[4],二维数组的解读也会完全不同。
会被解释做这样。
如果指定了数组的宽度,a[0]的大小也随之确定,也能顺利的寻址。
2.二级指针
2.1.概念
#include<bits/stdc++.h>
using namespace std;
int main(){
int a=10;
int *p=&a;
int **pp=&p;
printf("%d %d %d",a,*p,**pp);
}
输出:10 10 10
其中,带有两个星号的**pp就是二级指针。
类型对照一览表:
2.2.应用
在函数参数中,如果想要修改数值,必须使用指针(或引用)。
那么,在函数参数中,如果想要修改指针自己的值,就需要指针的指针,也就是多级指针。
#include<bits/stdc++.h>
using namespace std;
void f1(char *s){
s="abcd";
}
void f2(char **s){
*s="abcd";
}
int main(){
char *s="first";
f1(s);puts(s);
f2(&s);puts(s);
}
注意,二级指针和二维数组,指向数组的指针是三个完全不同的东西。
完。