數組 + 1 的各不相同(或許會有你的盲點)

在討論這個問題前, 我們先看一段代碼。這段代碼,大家都會很容易看懂, 但是真正能夠明白的我不知道有幾。

/*   代碼 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 的位置, 跳轉到之後的第三個位置。



總結:

綜上所訴,筆者最想表達的是,在數組名取地址然後增加偏移量時, 他是按照整個數組再進行跳轉。即跳過整個數組的長度的偏移量倍。







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章