數(shù)組指針(也稱行指針)
定義 int (*p)[n];
()優(yōu)先級高,首先說明p是一個指針,指向一個整型的一維數(shù)組,這個一維數(shù)組的長度是n,也可以說是p的步長。也就是說執(zhí)行p+1時,p要跨過n個整型數(shù)據(jù)的長度。
如要將二維數(shù)組賦給一指針,應(yīng)這樣賦值:
int a[3][4];
int (*p)[4]; //該語句是定義一個數(shù)組指針,指向含4個元素的一維數(shù)組。
p=a; //將該二維數(shù)組的首地址賦給p,也就是a[0]或&a[0][0]
p++; //該語句執(zhí)行過后,也就是p=p+1;p跨過行a[0][]指向了行a[1][]
所以數(shù)組指針也稱指向一維數(shù)組的指針,亦稱行指針。
c中指針可以作數(shù)組使用比如:
int a[] = "hello world"; int *p; p = a; printf("%c", *p++); printf("%c", p[1]); //利用數(shù)組形式輸出
數(shù)組不一定能作指針用比如: int a[] = "hello world"; printf("%c", *a++); //這里不能改變數(shù)組的地址,會報錯
這時會報錯,你不能改變數(shù)組的地址;
但是可以這樣使用:
int a[] = "hello world"; printf("%c", *(p+1)); //沒有改變數(shù)組地址,可行
數(shù)組指針,指的是數(shù)組名的指針,即數(shù)組首元素地址的指針。即是指向數(shù)組的指針。例:int (*p)[10]; p即為指向數(shù)組的指針,又稱數(shù)組指針。
int a[4][5];int (*p)[5]=a;
這里所代表的 *(p+i) 是二維數(shù)組a[i][0]的地址。
指針數(shù)組
定義 int *p[n];
[]優(yōu)先級高,先與p結(jié)合成為一個數(shù)組,再由int*說明這是一個整型指針數(shù)組,它有n個指針類型的數(shù)組元素。這里執(zhí)行p+1時,則p指向下一個數(shù)組元素,這樣賦值是錯誤的:p=a;因?yàn)閜是個不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它們分別是指針變量可以用來存放變量地址。但可以這樣 *p=a; 這里*p表示指針數(shù)組第一個元素的值,a的首地址的值。
如要將二維數(shù)組賦給一指針數(shù)組:
int *p[3];
int a[3][4];
p++; //該語句表示p數(shù)組指向下一個數(shù)組元素。注:此數(shù)組每一個元素都是一個指針
for(i=0;i<3;i++)
p[i]=a[i]
這里int *p[3] 表示一個一維數(shù)組內(nèi)存放著三個指針變量,分別是p[0]、p[1]、p[2]
所以要分別賦值。
這樣兩者的區(qū)別就豁然開朗了,數(shù)組指針只是一個指針變量,似乎是C語言里專門用來指向二維數(shù)組的,它占有內(nèi)存中一個指針的存儲空間。指針數(shù)組是多個指針變量,以數(shù)組形式存在內(nèi)存當(dāng)中,占有多個指針的存儲空間。
還需要說明的一點(diǎn)就是,同時用來指向二維數(shù)組時,其引用和用數(shù)組名引用都是一樣的。
比如要表示數(shù)組中i行j列一個元素:
*(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]
優(yōu)先級:()>[]>*
#include<stdio.h> int main(void) { char **p, i; char *strings[] ={ "one", "two", "three" }; p=strings; //strings是地址的地址,所以要定義**p for(i=0; i<3; i++) printf("%s\n", *(p++)); //這里*(p++)是取出存儲在數(shù)組中的每一個字符串的地址 return 0; }
這里創(chuàng)建了一個指針數(shù)組strings,它的每個數(shù)組元素相當(dāng)于一個指針變量,都可以指向一個整形變量,其值為地址。也就是說strings數(shù)組中每個元素存放的是字符串的地址。
這里可以將指針數(shù)組看成 * (strings[1])之類,后面的那部分 strings[1] 看成指針變量 p.
=========================================================================
初學(xué)者總是分不出指針數(shù)組與數(shù)組指針的區(qū)別。其實(shí)很好理解:
指針數(shù)組:首先它是一個數(shù)組,數(shù)組的元素都是指針,數(shù)組占多少個字節(jié)由數(shù)組本身的大小決定,每一個元素都是一個指針,在32 位系統(tǒng)下任何類型的指針永遠(yuǎn)是占4 個字節(jié)。它是“儲存指針的數(shù)組”的簡稱。
數(shù)組指針:首先它是一個指針,它指向一個數(shù)組。在32 位系統(tǒng)下任何類型的指針永遠(yuǎn)是占4 個字節(jié),至于它指向的數(shù)組占多少字節(jié),不知道,具體要看數(shù)組大小。它是“指向數(shù)組的指針”的簡稱。
下面到底哪個是數(shù)組指針,哪個是指針數(shù)組呢:
A)
int *p1[10];
B)
int (*p2)[10];
每次上課問這個問題,總有弄不清楚的。這里需要明白一個符號之間的優(yōu)先級問題。
“[]”的優(yōu)先級比“*”要高。p1 先與“[]”結(jié)合,構(gòu)成一個數(shù)組的定義,數(shù)組名為p1,int *修飾的是數(shù)組的內(nèi)容,即數(shù)組的每個元素。那現(xiàn)在我們清楚,這是一個數(shù)組,其包含10 個指向int 類型數(shù)據(jù)的指針,即指針數(shù)組。至于p2 就更好理解了,在這里“()”的優(yōu)先級比“[]”高,“*”號和p2 構(gòu)成一個指針的定義,指針變量名為p2,int 修飾的是數(shù)組的內(nèi)容,即數(shù)組的每個元素。數(shù)組在這里并沒有名字,是個匿名數(shù)組。那現(xiàn)在我們清楚p2 是一個指針,它指向一個包含10 個int 類型數(shù)據(jù)的數(shù)組,即數(shù)組指針。我們可以借助下面的圖加深理解:
這里有個有意思的話題值得探討一下:平時我們定義指針不都是在數(shù)據(jù)類型后面加上指針變量名么?這個指針p2 的定義怎么不是按照這個語法來定義的呢?也許我們應(yīng)該這樣來定義p2:
int (*)[10] p2;
int (*)[10]是指針類型,p2 是指針變量。這樣看起來的確不錯,不過就是樣子有些別扭。其實(shí)數(shù)組指針的原型確實(shí)就是這樣子的,只不過為了方便與好看把指針變量p2 前移了而已。你私下完全可以這么理解這點(diǎn)。雖然編譯器不這么想。^_^
既然這樣,那問題就來了。前面我們講過a 和&a 之間的區(qū)別,現(xiàn)在再來看看下面的代碼:
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 都是數(shù)組指針,指向的是整個數(shù)組。&a 是整個數(shù)組的首地址,a是數(shù)組首元素的首地址,其值相同但意義不同。在C 語言里,賦值符號“=”號兩邊的數(shù)據(jù)類型必須是相同的,如果不同需要顯示或隱式的類型轉(zhuǎn)換。p3 這個定義的“=”號兩邊的數(shù)據(jù)類型完全一致,而p4 這個定義的“=”號兩邊的數(shù)據(jù)類型就不一致了。左邊的類型是指向整個數(shù)組的指針,右邊的數(shù)據(jù)類型是指向單個字符的指針。在Visual C++6.0 上給出如下警告:
warning C4047: 'initializing' : 'char (*)[5]' differs in levels of indirection from 'char *'。
還好,這里雖然給出了警告,但由于&a 和a 的值一樣,而變量作為右值時編譯器只是取變量的值,所以運(yùn)行并沒有什么問題。不過我仍然警告你別這么用。
既然現(xiàn)在清楚了p3 和p4 都是指向整個數(shù)組的,那p3+1 和p4+1 的值就很好理解了。
但是如果修改一下代碼,把數(shù)組大小改小點(diǎn),會有什么問題?p3+1 和p4+1 的值又是多少呢?
int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[3] = &a;
char (*p4)[3] = a;
return 0;
}
甚至還可以把代碼再修改,把數(shù)組大小改大點(diǎn):
int main()
{
char a[5]={'A','B','C','D'};
char (*p3)[10] = &a;
char (*p4)[10] = a;
return 0;
}
這個時候又會有什么樣的問題?p3+1 和p4+1 的值又是多少?
上述幾個問題,希望讀者能仔細(xì)考慮考慮,并且上機(jī)測試看看結(jié)果。
測試結(jié)果:
(1).char (*p2)[5]=a;必須使用強(qiáng)制轉(zhuǎn)換,如:char (*p2)[5]=(char (*)[5])a;
(2).把數(shù)組大小改變,都會編譯不通過,提示:
error C2440: 'initializing' : cannot convert from 'char (*)[5]' to 'char (*)[3]'
error C2440: 'initializing' : cannot convert from 'char (*)[5]' to 'char (*)[10]'
(3).把以上程序測試代碼如下:
int main()
{
char a[5]={'a','b','c','d'};
char (*p1)[5]= &a;
char (*p2)[5]=(char (*)[5])a;
printf("a=%d\n",a);
printf("a=%c\n",a[0]);
printf("p1=%c\n",**p1);
printf("p2=%c\n",**p2);
printf("p1+1=%c\n",**(p1+1));
printf("p2+1=%c\n",**(p2+1));
return 0;
}
輸出:
a=1638208
a=a
p1=a
p2=a
p1+1=?
p2+1=?
Press any key to continue
結(jié)論:
根據(jù)指針類型及所指對象,表示指針大小,每次加1,表示增加指針類型大小的字節(jié).----后面還會有解釋說明.
先看下面這個例子:
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
假設(shè)p 的值為0x100000。如下表表達(dá)式的值分別為多少?
p + 0x1 = 0x___ ?
(unsigned long)p + 0x1 = 0x___?
(unsigned int*)p + 0x1 = 0x___?
我相信會有很多人一開始沒看明白這個問題是什么意思。其實(shí)我們再仔細(xì)看看,這個知識點(diǎn)似曾相識。一個指針變量與一個整數(shù)相加減,到底該怎么解析呢?
還記得前面我們的表達(dá)式“a+1”與“&a+1”之間的區(qū)別嗎?其實(shí)這里也一樣。指針變量與一個整數(shù)相加減并不是用指針變量里的地址直接加減這個整數(shù)。這個整數(shù)的單位不是byte 而是元素的個數(shù)。所以:p + 0x1 的值為0x100000+sizof(Test)*0x1。至于此結(jié)構(gòu)體的大小為20byte,前面的章節(jié)已經(jīng)詳細(xì)講解過。所以p +0x1 的值為:0x100014。
(unsigned long)p + 0x1 的值呢?這里涉及到強(qiáng)制轉(zhuǎn)換,將指針變量p 保存的值強(qiáng)制轉(zhuǎn)換成無符號的長整型數(shù)。任何數(shù)值一旦被強(qiáng)制轉(zhuǎn)換,其類型就改變了。所以這個表達(dá)式其實(shí)就是一個無符號的長整型數(shù)加上另一個整數(shù)。所以其值為:0x100001。
(unsigned int*)p + 0x1 的值呢?這里的p 被強(qiáng)制轉(zhuǎn)換成一個指向無符號整型的指針。所以其值為:0x100000+sizof(unsigned int)*0x1,等于0x100004。
上面這個問題似乎還沒啥技術(shù)含量,下面就來個有技術(shù)含量的:在x86 系統(tǒng)下,其值為多少?
intmain()
{
int a[4]={1,2,3,4};
int *ptr1=(int *)(&a+1);//指向a數(shù)組后面的內(nèi)存單元,&a+1表示向后移16個存儲單元
int *ptr2=(int *)((int)a+1);//表示a的存儲單元的地址增加一個字節(jié)
printf("%x,%x",ptr1[-1],*ptr2);//ptr1[-1]其實(shí)指向的是a數(shù)組的最后一個單元,*ptr1則表示a數(shù)組的地址后移一個字節(jié)之后的4個連續(xù)存儲單元所存儲的值
return 0;
}
這是我講課時一個學(xué)生問我的題,他在網(wǎng)上看到的,據(jù)說難倒了n 個人。我看題之后告訴他,這些人肯定不懂匯編,一個懂匯編的人,這種題實(shí)在是小case。下面就來分析分析這個問題:
根據(jù)上面的講解,&a+1 與a+1 的區(qū)別已經(jīng)清楚。
ptr1:將&a+1 的值強(qiáng)制轉(zhuǎn)換成int*類型,賦值給int* 類型的變量ptr,ptr1 肯定指到數(shù)組a 的下一個int 類型數(shù)據(jù)了。ptr1[-1]被解析成*(ptr1-1),即ptr1 往后退4 個byte。所以其值為0x4。
ptr2:按照上面的講解,(int)a+1 的值是元素a[0]的第二個字節(jié)的地址。然后把這個地址強(qiáng)制轉(zhuǎn)換成int*類型的值賦給ptr2,也就是說*ptr2 的值應(yīng)該為元素a[0]的第二個字節(jié)開始的連續(xù)4 個byte 的內(nèi)容。
其內(nèi)存布局如下圖:
好,問題就來了,這連續(xù)4 個byte 里到底存了什么東西呢?也就是說元素a[0],a[1]里面的值到底怎么存儲的。這就涉及到系統(tǒng)的大小端模式了,如果懂匯編的話,這根本就不是問題。既然不知道當(dāng)前系統(tǒng)是什么模式,那就得想辦法測試。大小端模式與測試的方法在第一章講解union 關(guān)鍵字時已經(jīng)詳細(xì)討論過了,請翻到彼處參看,這里就不再詳述。我們可以用下面這個函數(shù)來測試當(dāng)前系統(tǒng)的模式。
int checkSystem()
{
union check
{
int i;
char ch;
} c;
c.i = 1;
return (c.ch ==1);//如果當(dāng)前系統(tǒng)為大端模式這個函數(shù)返回0;如果為小端模式,函數(shù)返回1。
}
如果當(dāng)前系統(tǒng)為大端模式這個函數(shù)返回0;如果為小端模式,函數(shù)返回1。也就是說如果此函數(shù)的返回值為1 的話,*ptr2 的值為0x2000000。如果此函數(shù)的返回值為0 的話,*ptr2 的值為0x100。
如對本文有疑問,請?zhí)峤坏浇涣髡搲?,廣大熱心網(wǎng)友會為你解答??! 點(diǎn)擊進(jìn)入論壇