初學者老是分不出指針數組與數組指針的區別。其實很好理解:
指針數組:首先它是一個數組,數組的元素都是指針,數組佔多少個字節由數組自己決定。它是「儲存指針的數組」的簡稱。
數組指針:首先它是一個指針,它指向一個數組。在32 位系統下永遠是佔4 個字節,至於它指向的數組佔多少字節,不知道。它是「指向數組的指針」的簡稱。
下面到底哪一個是數組指針,哪一個是指針數組呢:
A)
int *p1[10];
B)
int (*p2)[10];
每次上課問這個問題,總有弄不清楚的。這裏須要明白一個符號之間的優先級問題。
「[]」的優先級比「*」要高。p1 先與「[]」結合,構成一個數組的定義,數組名爲p1,int *修飾的是數組的內容,即數組的每一個元素。那如今咱們清楚,這是一個數組,其包含10 個指向int 類型數據的指針,即指針數組。至於p2 就更好理解了,在這裏「()」的優先級比「[]」高,「*」號和p2 構成一個指針的定義,指針變量名爲p2,int 修飾的是數組的內容,即數組的每一個元素。數組在這裏並無名字,是個匿名數組。那如今咱們清楚p2 是一個指針,它指向一個包含10 個int 類型數據的數組,即數組指針。咱們能夠藉助下面的圖加深理解:數組
這裏有個有意思的話題值得探討一下:平時咱們定義指針不都是在數據類型後面加上指針變量名麼?這個指針p2 的定義怎麼不是按照這個語法來定義的呢?也許咱們應該這樣來定義p2:
int (*)[10] p2;
int (*)[10]是指針類型,p2 是指針變量。這樣看起來的確不錯,不過就是樣子有些彆扭。其實數組指針的原型確實就是這樣子的,只不過爲了方便與好看把指針變量p2 前移了而已。你私下徹底能夠這麼理解這點。雖然編譯器不這麼想。^_^
函數
既然這樣,那問題就來了。前面咱們講過a 和&a 之間的區別,如今再來看看下面的代碼:
int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[5] = &a;
char (*p4)[5] = a;
return 0;
}
上面對p3 和p4 的使用,哪一個正確呢?p3+1 的值會是什麼?p4+1 的值又會是什麼?毫無疑問,p3 和p4 都是數組指針,指向的是整個數組。&a 是整個數組的首地址,a是數組首元素的首地址,其值相同但意義不一樣。在C 語言裏,賦值符號「=」號兩邊的數據類型必須是相同的,若是不一樣須要顯示或隱式的類型轉換。p3 這個定義的「=」號兩邊的數據類型徹底一致,而p4 這個定義的「=」號兩邊的數據類型就不一致了。左邊的類型是指向整個數組的指針,右邊的數據類型是指向單個字符的指針。在Visual C++6.0 上給出以下警告:
warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *'。
還好,這裏雖然給出了警告,但因爲&a 和a 的值同樣,而變量做爲右值時編譯器只是取變量的值,因此運行並無什麼問題。不過我仍然警告你別這麼用。
既然如今清楚了p3 和p4 都是指向整個數組的,那p3+1 和p4+1 的值就很好理解了。
可是若是修改一下代碼,會有什麼問題?p3+1 和p4+1 的值又是多少呢?
int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[3] = &a;
char (*p4)[3] = a;
return 0;
}
甚至還能夠把代碼再修改:
int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[10] = &a;
char (*p4)[10] = a;
return 0;
}
這個時候又會有什麼樣的問題?p3+1 和p4+1 的值又是多少?
上述幾個問題,但願讀者能仔細考慮考慮。
佈局
先看下面這個例子:
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
假設p 的值爲0x100000。以下表表達式的值分別爲多少?
p + 0x1 = 0x___ ?
(unsigned long)p + 0x1 = 0x___?
(unsigned int*)p + 0x1 = 0x___?
我相信會有不少人一開始沒看明白這個問題是什麼意思。其實咱們再仔細看看,這個知識點似曾相識。一個指針變量與一個整數相加減,到底該怎麼解析呢?
還記得前面咱們的表達式「a+1」與「&a+1」之間的區別嗎?其實這裏也同樣。指針變量與一個整數相加減並非用指針變量裏的地址直接加減這個整數。這個整數的單位不是byte 而是元素的個數。因此:p + 0x1 的值爲0x100000+sizof(Test)*0x1。至於此結構體的大小爲20byte,前面的章節已經詳細講解過。因此p +0x1 的值爲:0x100014。
(unsigned long)p + 0x1 的值呢?這裏涉及到強制轉換,將指針變量p 保存的值強制轉換成無符號的長整型數。任何數值一旦被強制轉換,其類型就改變了。因此這個表達式其實就是一個無符號的長整型數加上另外一個整數。因此其值爲:0x100001。
(unsigned int*)p + 0x1 的值呢?這裏的p 被強制轉換成一個指向無符號整型的指針。因此其值爲:0x100000+sizof(unsigned int)*0x1,等於0x100004。
上面這個問題彷佛還沒啥技術含量,下面就來個有技術含量的:在x86 系統下,其值爲多少?
intmain()
{
int a[4]={1,2,3,4};
int *ptr1=(int *)(&a+1);
int *ptr2=(int *)((int)a+1);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}
這是我講課時一個學生問個人題,他在網上看到的,聽說難倒了n 我的。我看題以後告訴他,這些人確定不懂彙編,一個懂彙編的人,這種題實在是小case。下面就來分析分析這個問題:
根據上面的講解,&a+1 與a+1 的區別已經清楚。
ptr1:將&a+1 的值強制轉換成int*類型,賦值給int* 類型的變量ptr,ptr1 確定指到數組a 的下一個int 類型數據了。ptr1[-1]被解析成*(ptr1-1),即ptr1 日後退4 個byte。因此其值爲0x4。
ptr2:按照上面的講解,(int)a+1 的值是元素a[0]的第二個字節的地址。而後把這個地址強制轉換成int*類型的值賦給ptr2,也就是說*ptr2 的值應該爲元素a[0]的第二個字節開始的連續4 個byte 的內容。
其內存佈局以下圖:測試
好,問題就來了,這連續4 個byte 裏到底存了什麼東西呢?也就是說元素a[0],a[1]裏面的值到底怎麼存儲的。這就涉及到系統的大小端模式了,若是懂彙編的話,這根本就不是問題。既然不知道當前系統是什麼模式,那就得想辦法測試。大小端模式與測試的方法在第一章講解union 關鍵字時已經詳細討論過了,請翻到彼處參看,這裏就再也不詳述。咱們能夠用下面這個函數來測試當前系統的模式。
int checkSystem( )
{
union check
{
int i;
char ch;
} c;
c.i = 1;
return (c.ch ==1);
}
若是當前系統爲大端模式這個函數返回0;若是爲小端模式,函數返回1。也就是說若是此函數的返回值爲1 的話,*ptr2 的值爲0x2000000。若是此函數的返回值爲0 的話,*ptr2 的值爲0x100。spa