剛開始學c語言的時候,總是會認為,一級指針可以用來訪問一維數組,那么二維數組就要用二級指針來訪問啦。。。。
實際上二級指針和二維數組真的沒什么關系,而且,切記千萬不要用二級指針訪問二維數組。。。。。
下面是幾個有關的小程序,加深印象。。。。。。。。
實驗環境:主機CPU酷睿i5,vs2012
程序1:
int _tmain(int argc, _TCHAR* argv[]) { int **p= NULL; int a[2][3] = {1,2,3,4}; p = a; return 0; }
結果:編譯錯誤,錯誤提示:無法從“int [2][3]”轉換為“int **
可見,二級指針和二維數組名,根本就不是一類東西。
程序2:
int _tmain(int argc, _TCHAR* argv[]) { int **p= NULL; int a[2][3] = {1,2,3,4}; p = (int **)a; printf("指針的長度為:%d\n",sizeof(p)); printf("值為:%d\n",(int)(*p)); p++; printf("值為:%d\n",(int)(*p)); getchar(); return 0; }
輸出結果:
為啥可以輸出結果呢:純屬巧合!!!!!
什么巧合呢:在我的機器上指針的長度和int類型的長度都剛好為4個字節。
把程序放在虛擬機中跑一下,虛擬機的系統是CentOS7,64位的。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][2]={1,2,3,4}; int **p =(int **)a; printf("值為:%d\n",(int)(*p)); p++; printf("值為:%d\n",(int)(*p)); char *pointer; printf("指針的長度為:%zd\n",sizeof(pointer)); return 0; }
結果:編譯錯誤,提示:
原因是,在我的虛擬機中指針占8個字節嗎,而int占四個字節。
但是long是占8個字節,所以改成long也可以實現主機中的效果。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { long a[2][2]={1,2,3,4}; long **p =(long **)a; printf("值為:%ld\n",(long)(*p)); p++; printf("值為:%ld\n",(long)(*p)); char *pointer; printf("指針的長度為:%zd\n",sizeof(pointer)); return 0; }
結果為:
原因和第一個程序一樣,純屬巧合:在我的虛擬機中指針和long都占8個字節。
在寫這個程序的時候還有個小插曲(小錯誤),也設計了兩個知識點,順便分享一下,寫上面程序的時候。
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][2]={1,2,3,4}; int **p =(int **)a; printf("值為:%d\n",(long)(*p)); p++; printf("值為:%d\n",(long)(*p)); char *pointer; printf("指針的長度為:%zd\n",sizeof(pointer)); return 0; }
輸出結果為:
在輸出long型數據的時候我不小心用了%d,而不是%ld。
看一下用%ld的效果:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][2]={1,2,3,4}; int **p =(int **)a; printf("值為:%ld\n",(long)(*p)); p++; printf("值為:%ld\n",(long)(*p)); char *pointer; printf("指針的長度為:%zd\n",sizeof(pointer)); return 0; }
輸出結果為:
為什么會這樣? 還是要從內存角度看:
在我的虛擬機中,指針占8個字節,而p是個二級指針,*p就是個int類型的指針,占8個字節,所以其實*p占據了a[0]和a[1]。
至於為什么結果是8589934593.這還要涉及一個字節序的問題(這里就不解釋了)
我的cpu是英特爾的,一般x86系列的cpu都是采用低字節序。
所以a[0]和a[1]兩個單元在內存中類似於:
高地址------------------------------------------------------------------------------------低地址
00000000 00000000 00000000 00000010 00000000 00000000 00000000 00000001
把這段內存解釋成long類型的數據就是8589934593
------------------------------------------------------------------------------------------------------------------------------
還要注意的是我上面,那兩個巧合之下能運行的程序,都是用*p訪問的,如果用**p會報錯,因為**p就是*p指向的內存空間的值,而*p指向的內存,也就是8589934593地址空間,是未知的。
總之記住,二級指針和二維數據沒啥關系
問題來了,既然不能用二級指針訪問二維數據,那怎樣用指針訪問二維數組呢?
在我之前的隨筆 由typedef和函數指針引起的危機 中,提到了,要聲明某種類型的指針變量,只需先聲明處該類型的變量,然后加上*即可(注意優先級)
那么怎樣用指針訪問二維數組呢?
先來回憶一下怎樣用指針訪問一維數組。在訪問一位數組時,我們實際上聲明的是一個和數組元素類型相同的指針變量,指向了數組第一個元素的地址,然后在用這個指針訪問數組。
比如訪問int b[3]= {1,2,3}.
b中的元素為int類型,所以我們要聲明一個int類型的指針變量 如int *p ,然后將p指向b的第一個元素的地址,即p = &b[0],數組的首地址和第一個元素的地址是一樣的,所以也可以用p=b;
這里需要強調的是,指針類型和數組元素的類型一致,而不是數組類型一致,比如b的類型是有三個int類型元素的數組,而b中元素是int類型,b是數組類型,而元素是int類型,是不一樣的。上面之所以能夠用p=b,不是說p是數組類型的指針,只是數組的首地址,和數組中第一個元素的地址一樣罷了。p是個int類型的指針而不是數組類型的指針,這一點一定要記住。。。。
回到二維數組來,拿int a[2][3]= {1,2,3,4,5,6}來說, 數組a的元素為 一個含有三個int類型數據的數據,即a的元素是一個一維數組,這個數組含3個元素。。。。
那么怎樣聲明指向a數組中元素的指針變量呢? 在 由typedef和函數指針引起的危機 提到,方法及時先聲明一個該類型的變量,然后加上*即可,
a中元素為含有3個int數據的數組,定義這樣一個普通變量為: int p[3],然后在變量名前面加上*即可,但是要注意運算符的優先級,因為*的優先級比[]低,所以要加括號,即int (*p)[3]。
這樣再將p指向a的第一個元素的地址:p = &a[0],然后就可以用p來訪問數組a啦!
給出程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][3] = {1,2,3,4,5,6}; int (*p)[3] = &a[0]; printf("值為:%d\n",(*p)[0]); printf("值為:%d\n",(*p)[1]); printf("值為:%d\n",(*p)[2]); p++; printf("值為:%d\n",(*p)[0]); printf("值為:%d\n",(*p)[1]); printf("值為:%d\n",(*p)[2]); getchar(); return 0; }
結果為:
再來一個程序,加深一下理解
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { int a[2][3][4] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24}; int (*p)[3][4] = &a[0]; int i = 0; for(;i < 3; i++) { int j = 0; for(; j < 4;j++) printf("值為:%d\n",(*p)[i][j]); } p++; i = 0; for(;i < 3; i++) { int j = 0; for(; j < 4;j++) printf("值為:%d\n",(*p)[i][j]); } return 0; }
運行結果:
最后,貼上一張,來自csdn的圖
希望對你有幫助,有紕漏的地方還請指正,謝謝。。。。