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