剛開始學c語言的時候,老是會認爲,一級指針能夠用來訪問一維數組,那麼二維數組就要用二級指針來訪問啦。。。。html
實際上二級指針和二維數組真的沒什麼關係,並且,切記千萬不要用二級指針訪問二維數組。。。。。小程序
下面是幾個有關的小程序,加深印象。。。。。。。。數組
實驗環境:主機CPU酷睿i5,vs2012函數
程序1:spa
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 **設計
可見,二級指針和二維數組名,根本就不是一類東西。3d
程序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; }
輸出結果:code
爲啥能夠輸出結果呢:純屬巧合!!!!!htm
什麼巧合呢:在個人機器上指針的長度和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的圖
但願對你有幫助,有紕漏的地方還請指正,謝謝。。。。