【原創】淺談指標(九)二維陣列和多級指標相關

計算機知識雜談發表於2021-12-26

本文僅在部落格園釋出,其他網站均為盜取,請自覺支援正版: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);
}

注意,二級指標和二維陣列,指向陣列的指標是三個完全不同的東西。
完。

相關文章