[C語言] - 指標及多維陣列 - Ivan's Blog

文章推薦指數: 80 %
投票人數:10人

指標及一維陣列在不少的書籍,我們都可以看到可以把陣列看成指標,這不完全正確,但在實作上也不能說完全不正確本質上陣列名稱代表的是一個位址, ... 指標及一維陣列在不少的書籍,我們都可以看到可以把陣列看成指標,這不完全正確,但在實作上也不能說完全不正確 本質上陣列名稱代表的是一個位址,而指標代表的是位址的位址,因此在宣告上是不相等的,例如你在某一個地方宣告了inta[10],而在其他地方想extern進來,但如果使用的是extern*a,那會compileerror,因為位址和位址的位址並不一樣再來如果a[10]真的和*a是一樣的,那sizeof(a)就會應該是4 那為什麼我們在實際使用上結果會是一樣的呢呢?下面這個例子都會印出20 inta[]={10,20,30}; printf("%d",a[1]); printf("%d",*(a+1)); 那是因為陣列在compiler階段都會被轉成指標,當看到a[1]的時候就從a的位址加上1個integer的size,如果看到*(a+1)那行為會一樣差別只在如果compiler知道a是陣列那會忽略’*’和’&’也就是不會作dereference,這樣也可以解釋為什麼下面這情況compiler認為是合法的 intb[3]={10,20,30}; b[99]=40; 那在什麼狀況下會完全相等呢?只有在傳陣列參數到函式的時候,以下三種情況compiler編出來會一模一樣,事實上在傳陣列到函式裡面時,真正傳的是指標並不是整份陣列複製過去 func(int*verctor){...}; func(intverctor[]){...}; func(intverctor[10]){...}; 試著想一下以下這段程式碼會印出什麼 #include charga[2]={'a','b'}; voidfunc_in_vec(charca[2]) { printf("sizeofca:%ld\n",sizeof(ca)); printf("&ca:%p\n",&ca); printf("&(ca[0]):%p\n",&(ca[0])); printf("&(ca[1]):%p\n",&(ca[1])); printf("\n"); } voidfunc_in_ptr(char*pa) { printf("sizeofpa:%ld\n",sizeof(pa)); printf("&pa:%p\n",&pa); printf("&(pa[0]):%p\n",&(pa[0])); printf("&(pa[1]):%p\n",&(pa[1])); printf("\n"); } intmain(void) { printf("sizeofga:%ld\n",sizeof(ga)); printf("&ga:%p\n",&ga); printf("ga:%p\n",ga); printf("&(ga[0]):%p\n",&(ga[0])); printf("&(ga[1]):%p\n",&(ga[1])); printf("\n"); func_in_vec(ga); func_in_ptr(ga); return0; } 結果如下(有可能在不同平台會有不同結果,這邊是在x64平台的結果,不過概念是一樣的) sizeofga:2-------------(1) &ga:0x601048------------(2) ga:0x601048-------------(3) &(ga[0]):0x601048-------(4) &(ga[1]):0x601049-------(5) sizeofca:8-------------(6) &ca:0x7fffd7b1c718------(7) &(ca[0]):0x601048-------(8) &(ca[1]):0x601049-------(9) sizeofpa:8-------------(10) &pa:0x7fffd7b1c718------(11) &(pa[0]):0x601048-------(12) &(pa[1]):0x601049-------(13) 由(1)可看出ga的型別為2byte的陣列,代表不直接相等指標(2)(3)可看出&ga=ga,代表雖然compiler會將陣列轉成指標,但會忽略’*’和’&’,(6)(8)可看出若經由funtion傳入參數後皆轉為指標,因此取sizeof為8(64位元平台) 指標及二維陣列(矩陣)在現實生活上我們有二維陣列的概念,用來作運算或儲存各種資料,但是在記憶體裡是如何儲存這些資料?記憶體只是一大塊連續的位址空間,如何實現二維陣列的概念? 事實上在記憶體裡面二維陣列就是陣列的陣列,而三維陣列則是陣列的陣列的陣列,依此類推 舉個例子來說,一個[3][2]的陣列代表的是有一個大小3的陣列裡面有大小2的陣列,共6個,參考下圖即可清楚看出,裡面的數字代表的就是程式取值時需給的二維陣列位址,橘色代表的是第一層陣列,藍色代表第二層陣列(陣列的陣列) 試著想一下以下這段程式碼會印出什麼 intmain(void) { inta[3][2]={{10,20},{30,40},{50,60}}; int*b=a[0]; printf("a[0][0]=%d\n",a[0][0]); printf("a[0][1]=%d\n",a[0][1]); printf("a[1][1]=%d\n",a[1][1]); printf("a[2][1]=%d\n",a[2][1]); printf("*(b+0)=%d\n",*(b+0)); printf("*(b+1)=%d\n",*(b+1)); printf("*(b+3)=%d\n",*(b+3)); printf("*(b+5)=%d\n",*(b+5)); return0; } 結果如下 a[0][0]=10---(1) a[0][1]=20---(2) a[1][1]=40---(3) a[2][1]=60---(4) *(b+0)=10---(5) *(b+1)=20---(6) *(b+3)=40---(7) *(b+5)=60---(8) 從下圖可看出此段程式將b指向第一層陣列a[0]的位址,搭配下圖和以上的結果可清楚看出(1)=(5)、(2)=(6)、(3)=(7)、(4)=(8),也可看出記憶體擺放的位址和二維陣列的關係 指標及三維陣列了解指標與陣列關係之後,我們可以來看更為抽象的三維以上的陣列,事實上如果能轉換成記憶體實際擺放的位址來看,也就不會覺得抽象了,假如有一個a[2][3][4]的三維陣列,那在記憶體擺放的情形如下,橘色代表第一維陣列,藍色代表第二維陣列,綠色代表第三維陣列,因此此陣列為一個大小為2的陣列裡面有大小為3的陣列裡面有大小為4的陣列,共24個 試著想一下以下這段程式碼會印出什麼 intmain(void) { inta[2][3][4]={0}; int*b=a[0][0]; inti,j,k; for(i=0;i<2;i++) for(j=0;j<3;j++) for(k=0;k<4;k++) a[i][j][k]=i+j+k; printf("a[0][0][0]=%d\n",a[0][0][0]); printf("a[0][0][2]=%d\n",a[0][0][2]); printf("a[0][1][1]=%d\n",a[0][1][1]); printf("a[0][2][3]=%d\n",a[0][2][3]); printf("a[1][0][0]=%d\n",a[1][0][0]); printf("a[1][2][2]=%d\n",a[1][2][2]); printf("*(b+0)=%d\n",*(b+0)); printf("*(b+2)=%d\n",*(b+2)); printf("*(b+5)=%d\n",*(b+5)); printf("*(b+11)=%d\n",*(b+11)); printf("*(b+12)=%d\n",*(b+12)); printf("*(b+22)=%d\n",*(b+22)); return0; } 結果如下 a[0][0][0]=0 a[0][0][2]=2 a[0][1][1]=2 a[0][2][3]=5 a[1][0][0]=1 a[1][2][2]=5 *(b+0)=0 *(b+2)=2 *(b+5)=2 *(b+11)=5 *(b+12)=1 *(b+22)=5 從下圖可看出三維陣列和記憶體的關係,將指標b指到三維陣列的頭,四維以上的陣列和二維三維陣列的原理一樣,可以此類推 接下來我們可以看一下不同type的指標和陣列的關係,首先先看int的指標,試著想一下以下這段程式碼會印出什麼 intmain(void) { inta[2][3][4]={0}; int*b=a[0][0]; int*c=a[0][2]; int*d=a[1][1]; inti,j,k; intcnt=0; for(i=0;i<2;i++) for(j=0;j<3;j++) for(k=0;k<4;k++){ a[i][j][k]=cnt; cnt++; } printf("b=%p,b+1=%p\n",b,b+1); printf("*(b+0)=%d,*(b+1)=%d\n",*(b+0),*(b+1)); printf("a[0][0][0]=%d,a[0][0][1]=%d\n\n",a[0][0][0],a[0][0][1]); printf("*(c+0)=%d,*(c+6)=%d\n",*(c+0),*(c+6)); printf("a[0][2][0]=%d,a[1][0][2]=%d\n\n",a[0][2][0],a[1][0][2]); printf("*(d+0)=%d,*(d+7)=%d\n",*(d+0),*(d+7)); printf("a[1][1][0]=%d,a[1][2][3]=%d\n\n",a[1][1][0],a[1][2][3]); return0; } 結果如下 b=0x7fffedac8880,b+1=0x7fffedac8884 *(b+0)=0,*(b+1)=1 a[0][0][0]=0,a[0][0][1]=1 *(c+0)=8,*(c+6)=14 a[0][2][0]=8,a[1][0][2]=14 *(d+0)=16,*(d+7)=23 a[1][1][0]=16,a[1][2][3]=23 b因為指到的是inttype,所以b和b+1的差距為4byte b,c,d皆為指到int的指標,因此可指到此三維陣列的第三維陣列,大小為4(藍色框住的綠色部份) b指到a[0][0]的大小為4的陣列,因此*(b+0)=a[0][0][0],*(b+1)會加一個intsize(4byte)之後取值所以*(b+1)=a[0][0][1] c指到a[0][2]的大小為4的陣列,因此*(c+0)=a[0][2][0],*(c+6)會加六個intsize(4byte)之後取值所以*(c+6)=a[1][0][2] d指到a[1][1]的大小為4的陣列,因此*(d+0)=a[1][1][0],*(d+7)會加七個intsize(4byte)之後取值所以*(d+7)=a[1][2][3] 再來看int[]的指標,試著想一下以下這段程式碼會印出什麼 intmain(void) { inta[2][3][4]={0}; int(*b)[4]=a[0]; int(*c)[4]=a[1]; inti,j,k; intcnt=0; for(i=0;i<2;i++) for(j=0;j<3;j++) for(k=0;k<4;k++){ a[i][j][k]=cnt; cnt++; } printf("b=%p,b+1=%p\n",b,b+1); printf("(*(b+0))[0]=%d,(*(b+1))[0]=%d\n",(*(b+0))[0],(*(b+1))[0]); printf("a[0][0][0]=%d,a[0][0][1]=%d\n\n",a[0][0][0],a[0][1][0]); printf("(*(b+2))[2]=%d,(*(b+4))[3]=%d\n",(*(b+2))[2],(*(b+4))[3]); printf("a[0][2][2]=%d,a[1][1][3]=%d\n\n",a[0][2][2],a[1][1][3]); printf("(*(c+1))[1]=%d,(*(c+2))[2]=%d\n",(*(c+1))[1],(*(c+2))[2]); printf("a[1][1][1]=%d,a[1][2][2]=%d\n\n",a[1][1][1],a[1][2][2]); return0; } 結果如下 b=0x7fff2c1a96c0,b+1=0x7fff2c1a96d0 (*(b+0))[0]=0,(*(b+1))[0]=4 a[0][0][0]=0,a[0][1][0]=4 (*(b+2))[2]=10,(*(b+4))[3]=19 a[0][2][2]=10,a[1][1][3]=19 (*(c+1))[1]=17,(*(c+2))[2]=22 a[1][1][1]=17,a[1][2][2]=22 b因為是指到int[4]的指標因此b和b+1的差距為16byte(4byte(int)*4(size)) b,c,d皆為指到int[4]的指標,因此可指到此三維陣列的第二維陣列,大小為3(橘色框住的藍色部份) b指到a[0]的大小為3的陣列,因此(*(b+0))[0]=a[0][0][0],*(b+1)會加四個intsize(16byte)之後取值所以(*(b+1))[0]=a[0][1][0],*(b+2)會加八個intsize(32byte)之後取值所以(*(b+2))[2]=a[0][2][2],*(b+4)會加十六個intsize(64byte)之後取值所以(*(b+4))[3]=a[1][1][3] c指到a[1]的大小為4的陣列,因此(*(c+1))[1]=a[1][1][1],*(c+2)會加八個intsize(32byte)之後取值所以(*(c+2))[2]=a[1][2][2] 再來看int[][]的指標,試著想一下以下這段程式碼會印出什麼 intmain(void) { inta[2][3][4]={0}; int(*b)[3][4]=a; inti,j,k; intcnt=0; for(i=0;i<2;i++) for(j=0;j<3;j++) for(k=0;k<4;k++){ a[i][j][k]=cnt; cnt++; } printf("b=%p,b+1=%p\n",b,b+1); printf("(*(b+0))[0][0]=%d,(*(b+0))[0][2]=%d\n",(*(b+0))[0][0],(*(b+0))[0][2]); printf("a[0][0][0]=%d,a[0][0][2]=%d\n\n",a[0][0][0],a[0][0][2]); printf("(*(b+0))[2][1]=%d,(*(b+0))[2][3]=%d\n",(*(b+0))[2][1],(*(b+0))[2][3]); printf("a[0][2][1]=%d,a[0][2][3]=%d\n\n",a[0][2][1],a[0][2][3]); printf("(*(b+1))[0][1]=%d,(*(b+1))[2][3]=%d\n",(*(b+1))[0][1],(*(b+1))[2][3]); printf("a[1][0][1]=%d,a[1][2][3]=%d\n\n",a[1][0][1],a[1][2][3]); return0; } 結果如下 b=0x7fff1604c440,b+1=0x7fff1604c470 (*(b+0))[0][0]=0,(*(b+0))[0][2]=2 a[0][0][0]=0,a[0][0][2]=2 (*(b+0))[2][1]=9,(*(b+0))[2][3]=11 a[0][2][1]=9,a[0][2][3]=11 (*(b+1))[0][1]=13,(*(b+1))[2][3]=23 a[1][0][1]=13,a[1][2][3]=23 b因為是指到int[3][4]的指標因此b和b+1的差距為48byte(4byte(int)(34)(size)) b,c,d皆為指到int[3][4]的指標,因此可指到此三維陣列的第一維陣列,大小為3*4(橘色部份) b指到a的大小為34的陣列,因此(\(b+0))[0][0]=a[0][0][0],(*(b+0))[0][2]=a[0][0][2],(*(b+0))[2][1]=a[0][2][1],(*(b+0))[2][3]=a[0][2][3],*(b+1)會加十二個intsize(48byte)之後取值所以(*(b+1))[0][1]=a[1][0][1],(*(b+1))[2][3]=a[1][2][3], 文章目錄 本站概覽 IvanLin Ivna’sBlog 19 文章 4 標籤 1.指標及一維陣列2.指標及二維陣列(矩陣)3.指標及三維陣列



請為這篇文章評分?