【原创】浅谈指针(九)二维数组和多级指针相关

x33g5p2x  于2021-12-26 转载在 其他  
字(2.7k)|赞(0)|评价(0)|浏览(172)

本文仅在博客园发布,其他网站均为盗取,请自觉支持正版: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);
}

注意,二级指针和二维数组,指向数组的指针是三个完全不同的东西。
完。

相关文章