本文僅在部落格園釋出,其他網站均為盜取,請自覺支援正版: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);
}
注意,二級指標和二維陣列,指向陣列的指標是三個完全不同的東西。
完。