天天看點

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

本文僅在部落格園釋出,其他網站均為盜取,請自覺支援正版: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,如果使用記憶體圖示的畫法,應該是如下的:

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

那麼,有人會問,這樣和指向數組首個元素的指針有什麼差別呢?當然,差別很大。

我們假設指向數組首個元素的指針為q,那麼,當執行

p++;q++;

時候,

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

數組會變成上面的樣子,也就是說,q增加的是sizeof(int),而p增加的是sizeof(a),也就是3個int的位子。

1.3.二維數組的尋址方式

在我們人類的了解中,數組a[2][3]是這樣排列的:

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

但是在機器的了解中,實際上是這樣排列的:

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

我們做一個實驗:

#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)
           

我們看到下面的圖:

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

此時,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位的調試模式,

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

我們得出的結論是:二維數組作為函數的參數傳遞,傳遞的是指向數組的指針。

那麼也就是說,這個函數和下面的函數同義。

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]大小未知,導緻必須手動指定數組的寬度

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

否則由于寬度不同,導緻無法解釋數組

如果把聲明寫成(*a)[4],二維數組的解讀也會完全不同。

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

會被解釋做這樣。

如果指定了數組的寬度,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就是二級指針。

類型對照一覽表:

【原創】淺談指針(九)二維數組和多級指針相關 https://www.cnblogs.com/jisuanjizhishizatan/p/15732439.html

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);
}
           

注意,二級指針和二維數組,指向數組的指針是三個完全不同的東西。

完。