***************************************************
更多精彩,歡迎進入:http://shop115376623.taobao.com
***************************************************
相信不少的C語言初學者都知道,數組名相當于指針,指向數組的首地址,而函數名相當于函數指針,指向函數的入口地址。
現在有這樣一個問題,如果對數組名取地址,那得到的會是什么呢?很多人立刻會想到:給指針取地址,就是指針的指針,
即二級指針嘛!當然這樣的結論是錯誤的,不然這篇筆記也就沒有意義了。
下面我們來逐步分析,下面是一段驗證這個問題的代碼
- #include<stdio.h>???
- int?main() ??
- { ??
- ????int?a[10]; ??
- ??
- ????printf("a:/t%p/n",?a);????????? ??
- ????printf("&a:/t%p/n",?&a);??????? ??
- ????printf("a+1:/t%p/n",?a+1);????? ??
- ????printf("&a+1:/t%p/n",?&a+1);?? ??
- ??
- ????return?0; ??
- }??
?
大家可以編譯運行一下,我的輸出的結果是:
- /*???
- a:??????????0012FF20??
- &a:?????????0012FF20??
- a+1:????????0012FF24??
- &a+1:?????? 0012FF48??
- */??
a和&a指向的是同一塊地址,但他們+1后的效果不同,a+1是一個元素的內存大小(增加4),而&a+1增加的是整個數組的內存
大小(增加40)。既a和&a的指向和&a[0]是相同的,但性質不同!
讀到這里,有很多朋友已經明白其中的機制了,如果還是有些模糊,請繼續往下看
- int?main() ??
- { ??
- ????int?a[10]; ??
- ????printf("%d/n",sizeof(a)); ??
- ????return?0; ??
- }??
這段代碼會輸出整個數組的內存大小,而不是首元素的大小,由此我們是否聯系到,sizeof(a)這里的a和
&a有些相同之處呢?!? 是的,沒錯,&a取都得是整個數組的地址!既數組名取地址等價于對數組取地址。
好了,讓我們總結一下,如果你還是不太理解,不用擔心,下面的概念很清晰
其實a和 &a結果都是數組的首地址,但他們的類型是不一樣。
a表示&a[0],也即對數組首元素取地址,a+1表示首地址+sizeof(元素類型)。
&a雖然值為數組首元素地址,但類型為:類型 (*)[數組元素個數],所以&a+1大小為:首地址+sizeof(a)。
還有這篇文章最初提到的指針的指針的那個錯誤結論,其實即使不懂上述內容,也應該判斷出結論是錯誤的,大家應該在了解數組名
即是數組的首地址的同時,也要知道,數組名僅僅是“相當”于指針,而并非真的是指針,數組名是只是個常量(一個值為數組首元素
地址的常量),所以不能進行++或者--運算。而常量更是無法取地址的,而之所以有&a,其實這里的a的意義早已經不是當初那個數組
名了,它此時代表了整個數組。
申明:本文系原創,轉載時請注明出處,本人保留追究責任的權利。 原文地址:http://hi.baidu.com/surfmygod/blog/item/53d44914cdb8a5d6a7ef3f13.html 本文適用于機器為32位,編譯器為VC6.0。 先來看下面一個例子:main() { int a[5]={0x11121314,0x21222324,0x31323334,0x41424344,0x51525354}; int *ptr1=(int *)(&a+1); int *ptr2=(int *)(a+1); printf("%x,%x",ptr1[-1],*ptr2); getch(); } 打印輸出的結果是多少?第4行&a是不是寫錯了?數組名怎么還取地址呢? 你可以試著在軟件中編譯運行一下,結果是51525354,21222324,沒錯,就是它了。你可以試著寫以下程序 再在計算機中編譯運行下: main() { int a[5]={0x11121314,0x21222324,0x31323334,0x41424344,0x51525354}; printf("%p\n%p\n",a,&a); printf("%d\n%d\n",sizeof(a),sizeof(&a)); printf("%p\n%p",&a+1,a+1); getch(); } 你可以看到前兩行a和&a打印的結果是一樣的(比如0012FF6C),后兩行的結果是都是20,而&a+1的結果是0012FF6C, 即a的起始地址后移20字節。看到這里對于a和&a的區別是不是有些眉目了?不錯,a和&a的值雖然一樣,但是前者表示 的是數組a的首地址,進階為數組元素長度,而后者也是表示數組a的首地址(或許有些人不同意我這個觀點,對地址 取地址應該是指向指針的指針呀。不過你注意了沒有,a和&a值是一樣的,試想如果這里表示的是指向指針的指針, 那么a的地址和指向a的指針的地址可能一樣么? 兩個指針都指向數組首地址,怎么會是指向指針的指針?還有一點需要強調,對一個指針進行sizeof運算,32位系統 下應該得到4,而對數組名進行sizeof運算得到的結果卻是20,這說明了什么?很顯然,數組名雖然能夠拿出來代表 一個地址,但是它和指針還是有區別的,對它進行sizeof運算也不會得到指針的大小。不信?你可以試試sizeof(&a[0]) 和sizeof(a),看看結果是不是相同), 但是是將整個數組作為一個對象來看待的,進階為整個數組長度。也就是說,a和&a雖然值相同,但是a是將所有數組 元素看成一個個不同的個體來對待的,而&a是將整個數組作為一個整體來對待的。這里要強調的是,a+1和&a[1]其實 是一樣的,都是代表a數組向后偏移一個元素的地址,你可以再用sizeof關鍵字打印一下兩個的值。但是a和&a[0]的 意義相同么?若打印地址的話,兩者確實是一樣的,但是你也用sizeof打印下兩者,一樣么?再試試a+0呢?很顯然, a和a+0是有區別的,但是a+0和&a[0]是含義相同的。所以,你可以說a+i和&a[i]含義相同,但是絕不能說a和&a[0] 或a+0含義相同。所以&a+1后移20位為&a+sizeof(&a),而a+1卻是a+sizeof(a[i])。 (《C語言深度解剖》認為在32為系統下sizeof(&a)的值仍為4,我覺得是不對的。他覺得是VC6.0錯了,其實不止VC, 其他很多編譯器得到的結果也不會是4,這說明a和&a在C中本來就不是作為指針來用的,只是具有指針的部分性質而已。)
ptr1是將&a+1強轉為int類型指針,此時打印ptr1[-1]即將ptr指針前移sizeof(int)個字節,故打印結果是5。 這樣一來后面的也明了了,a+1的值為在數組a的首地址上后移sizeof(int)個字節,然后強轉為int類型指針 (其實強轉并沒有改變什么,原來類型即為整型指針,可以去掉強轉),最后輸出結果為21222324。
這里要考慮數據在計算機中的存儲模式:大端模式和小端模式。解釋一下: 大端模式(Big_endian):字數據的高字節存儲在低地址中,而字數據的低字節則存放在高地址中。 小端模式(Little_endian):字數據的高字節存儲在高地址中,而字數據的低字節則存放在低地址中。 在大端模式下,a在計算機中存儲如下(從低地址到高地址,一個十六進制數代表內存的一個字節,下同): 0x11 0x12 0x13 0x14 0x21 0x22 0x23 0x24 0x31 0x32 0x33 0x34 0x41 0x42 0x43 0x44 0x51 0x52 0x53 0x54 在小端模式下,a在計算機中存儲如下: 0x14 0x13 0x12 0x11 0x24 0x23 0x22 0x21 0x34 0x33 0x32 0x31 0x44 0x43 0x42 0x41 0x54 0x53 0x52 0x51 (int)a表示將a的首地址強轉為整型數據(若原來是0012FF6C,轉換后仍為0012FF6C,但是這時已經不是表示地址而是一個 十六進制整型數了),這時+1代表整型數(int)a加1(變為0012FF6D),再把結果強轉為整形指針,故指向的數據地址為0012FF6D, 即上述存儲去的第二個字節開始處。此時輸出*ptr2,即輸出a數組存儲區中第二到第五字節組成的一個整型數,若為大端模式則輸出 12131421,若為小端模式則輸出24111213。 補充說明下,在vc6.0上,上面sizeof(&a)才是20,而在GCC ,以及之后的vc版本(如vs2005及之后版本)則是將&a完全視作一個指針,sizeof(&a)為4 (32位機器) int *p1[3], (*p2)[2], ** p, a[3][2];a) p1=a;是否語法正確? b) p2=a;呢? ? 為什么?請詳述原因。 c) p=a; 是否正確???可不可以認為二維數組名是一個指向指針的指針變量? 其他網址:?http://topic.csdn.net/u/20100106/15/e5fa8fa6-555e-4729-a435-d1f8fdac5612.html 示例程序: ?#if 1 #include <stdio.h> #include <stdlib.h> int main() { int a[] = {1,2,3,4,5}; int *p1 = (int *)(a+1); int *p2 = (int *)(&a+1);? int *p3 = (int *)((int)a + 1); int *p4 = a;? printf("a is %p \n",a); printf("&a is %p \n",&a); printf("sizeof(a) = %d\n",sizeof(a)); printf("sizeof(&a) = %d\n",sizeof(&a)); printf("sizeof(p4) = %d \n",sizeof(p4));? printf("p1 = %p \n",p1); printf("p2 = %p \n",p2); printf("*p1 = %d\n",*p1); printf("p2[-1] = %d\n",p2[-1]); printf("*p3 = %d \n",*p3); printf("*(p2-1) = %d \n",*(p2-1)); ?? ? ? ? ? ? system("PAUSE"); ? ? return 0; } #endif |