在討論這個問題前, 我們先看一段代碼。這段代碼,大家都會很容易看懂, 但是真正能夠明白的我不知道有幾。
/* 代碼 1-1 */
#include <stdio.h>
int main()
{
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
return 0;
}
輸出結果爲: 2, 5
討論:
當我第一眼看到這個代碼的時候, 我的第一直覺答案是2, 1. 但是當運行結果出來以後, 我蒙了。爲什麼會是2, 5 呢。後來我就測試了輸出他的地址(原來最終問題,通過查看內存纔是王道),我就發現了問題的所在。主要的原因就在於 &a + 1 這裏。這裏給出我查看內存運行情況的代碼:
/* 代碼1-2 */
#include <stdio.h>
int main()
{
int c[6] = {1, 2, 3, 4, 5, 6, };
printf("c = %p, c+1 = %p, &c+1 = %p\n", c, c+1, &c+1);
/* 數組名c 的地址, 數組名c + 1 的地址, 數組名地址 + 1 的地址*/
return 0;
}
運行結果: c = 0022FF18, c+1 = 0022FF1C, &c+1 = 0022FF30
通過這個輸出結果, 我們不難看出,c+1 的地址是在數組地址(c 的地址)的基礎上加了1 個int。但是,&c + 1 的地址是在數組地址(c 的地址)的基礎上加了6 個int 。而這6 個int 就是整個數組的長度。這不是偶然,這是必然。就是說,在數組地址的基礎上加 1 就相當於再加了一個數組(跳過一個數組的長度)。
這樣我們就不難解釋上面代碼 1-1,爲什麼結果是2, 5 。
int *ptr=(int *)(&a+1);
這裏的ptr 地址實際上就是跳到整個數組的最末端,然後新開始的一個數組(即元素5 後面的一個位置)。所以,當輸出*(ptr-1) 就是5 。
討論了一維數組的情況, 下面我們接着看看二維數組。
按理說,把一維數組弄明白了二維數組就很簡單,這裏因爲筆者在後面寫二維數組的時候也遇到了點麻煩,所以就多多囉嗦一下。
下面我們看一段測試代碼:
/* 代碼 2 - 1 */
#include <stdio.h>
int main()
{
int a[][3] = {{1, 2, 3, }, {4, 5, 6, }};
// 數組名的不同 +1
printf("結果1\n");
printf("** a = %d\n", **a);
printf("a = %p\n", a);
printf("&a = %p\n", &a);
printf("a+1 = %p\n", a+1);
printf("&a+1 = %p\n", &a+1);
printf("\n");
// 數組起始單元的不同+1
printf("結果2\n");
printf("* a[0] = %d\n", *a[0]);
printf("&a[0] = %p\n", &a);
printf("a[0]+1 = %p\n", a[0]+1);
printf("&a[0]+1 = %p\n", a[0]+1);
printf("\n");
// 數組中間單元不同+1
printf("結果3\n");
printf("a[0][1] = %d\n", a[0][1]);
printf("&a[0][1] = %p\n", &a[0][1]);
printf("&a[0][1]+1 = %p\n", &a[0][1]+1);
return 0;
}
輸出結果爲:
結果1
** a = 1
a = 0022FF18 // 數組名
&a = 0022FF18 // 數組名地址
a+1 = 0022FF24 // 數組名加1 , 結果爲跳轉到下一行(即第二行,這裏跳過3 個int)開頭。
&a+1 = 0022FF30 // 數組名地址加1, 按照上文介紹,跳轉到下一個數組的位置(即跳過整個數 組,如果是加2 , 那麼就跳過2 個數組空間的位置。),這裏就跨過了6 個int 的空間。
結果2
* a[0] = 1
&a[0] = 0022FF18 // 數組中第一行行名地址
a[0]+1 = 0022FF1C // 數組中第一行行名加1, 結果爲跳轉到同行的下一個元素位置(跳過1 個int)
&a[0]+1 = 0022FF24 // 數組中第一行行名地址加1, 結果爲跳轉一行的地址,到下一行的行頭位置 (即跳轉了3 個int 位置)。
結果3
a[0][1] = 2
&a[0][1] = 0022FF1C // 數組中位置爲[0][1] 的地址
&a[0][1]+1 = 0022FF20 // 數組中位置爲[0][1] 的地址加1 ,結果跳轉了1 個int 的位置。跳轉到同行的下一 個元素位置。是本行的行末位置,那麼就跳轉到下一行的開頭位置。
這段代碼有點多, 都是printf(); 這個不重要,重要的是輸出的內容。分析就在答案的後面註釋。
從這裏的結果, 我們可以看出 結果3 中的 &a[0][1]+1 = 0022FF20 與 結果1, 結果2中的 &a+1 = 0022FF30 , &a[0]+1 = 0022FF24 運算方式是不同的。這是因爲,結果1和結果2 中是對數組名地址加1, 而結果3 不是 對數組名地址加1, 他只是數組中間的一個元素地址。如果要進一步深究,我們可以再把指針帶進來考慮就可以更加清晰明白。
指針的操作,就相當於上面 結果3 的操作結果一樣。他的操作都是按部就班,不會存在因爲加1 而跳轉一個數組的長度的情況。下面我們看看測試代碼:
/* 代碼 3 -1 */
#include <stdio.h>
int main()
{
int a[][3] = {{1, 2, 3, }, {4, 5, 6, }};
int *p1 = *a; // 取a 的地址
int *p2 = a[0]; // 取a[0] 的地址
int *p3 = &a[0][1]; // 去a[0][1] 的地址
printf("p1 = %p \n", p1);
printf("p1+1 = %p \n", p1+1);
printf("p2 = %p \n", p2);
printf("p2+1 = %p \n", p2+1);
printf("p3 = %p \n", p3);
printf("p3+1 = %p \n", p3+1);
return 0;
}
輸出結果:
p1 = 0022FF0C
p1+1 = 0022FF10
p2 = 0022FF0C
p2+1 = 0022FF10
p3 = 0022FF10
p3+1 = 0022FF14
從這個結果可以看出指針的跳轉是按位置跳轉的。一次只能跳轉偏移個數個位置。
比如: int *p = a; p + 3; 就表示跳轉從當前p 的位置, 跳轉到之後的第三個位置。
總結:
綜上所訴,筆者最想表達的是,在數組名取地址然後增加偏移量時, 他是按照整個數組再進行跳轉。即跳過整個數組的長度的偏移量倍。