網(wǎng)站關(guān)鍵字標(biāo)簽最新seo操作
一、細(xì)說(shuō)指針
指針是一個(gè)特殊的變量,它里面存儲(chǔ)的數(shù)值被解釋成為內(nèi)存里的一個(gè)地址。要搞清一個(gè)指針需要搞清指針的四方面的內(nèi)容:
- 指針的類(lèi)型
- 指針?biāo)赶虻念?lèi)型
- 指針的值或者叫指針?biāo)赶虻膬?nèi)存區(qū)、
- 指針本身所占據(jù)的內(nèi)存區(qū)。
1、指針的類(lèi)型
從語(yǔ)法的角度看,你只要把指針聲明語(yǔ)句里的指針名字去掉,剩下的部分就是這個(gè)指針的類(lèi)型。
(1)int *ptr; // 指針的類(lèi)型是 int*
(2)char *ptr; // 指針的類(lèi)型是 char*
(3)int **ptr; // 指針的類(lèi)型是 int**
(4)int (*ptr)[3];// 指針的類(lèi)型是 int(*)[3]
(5)int *(*ptr)[4];// 指針的類(lèi)型是 int*(*)[4]
2、指針指向的類(lèi)型
當(dāng)你通過(guò)指針來(lái)訪問(wèn)指針?biāo)赶虻膬?nèi)存區(qū)時(shí),指針?biāo)赶虻念?lèi)型決定了編譯器將把那片內(nèi)存區(qū)里的內(nèi)容當(dāng)做什么來(lái)看待。
從語(yǔ)法上看,你只須把指針聲明語(yǔ)句中的指針名字和名字左邊的指針聲明符 *
去掉,剩下的就是指針?biāo)赶虻念?lèi)型。
(1)int *ptr; // 指針?biāo)赶虻念?lèi)型是 int
(2)char *ptr; // 指針?biāo)赶虻牡念?lèi)型是 char
(3)int **ptr; // 指針?biāo)赶虻牡念?lèi)型是 int*
(4)int (*ptr)[3]; // 指針?biāo)赶虻牡念?lèi)型是 int()[3]
(5)int *(*ptr)[4]; // 指針?biāo)赶虻牡念?lèi)型是 int*()[4]
3、指針的值(指針?biāo)赶虻膬?nèi)存區(qū)或地址)
指針的值是指針本身存儲(chǔ)的數(shù)值,這個(gè)值將被編譯器當(dāng)作一個(gè)地址,而不是一個(gè)一般的數(shù)值。在 32 位程序里,所有類(lèi)型的指針的值都是一個(gè)32 位整數(shù),因?yàn)?32 位程序里內(nèi)存地址全都是 32 位長(zhǎng)。
指針?biāo)赶虻膬?nèi)存區(qū)就是從指針的值所代表的那個(gè)內(nèi)存地址開(kāi)始,長(zhǎng)度為 sizeof(指針?biāo)赶虻念?lèi)型)
的一片內(nèi)存區(qū)。
4、指針本身所占據(jù)的內(nèi)存區(qū)
指針本身占了多大的內(nèi)存?你只要用函數(shù)sizeof(指針的類(lèi)型)測(cè)一下就知道了。在32 位平臺(tái)里,指針本身占據(jù)了4 個(gè)字節(jié)的長(zhǎng)度。指針本身占據(jù)的內(nèi)存這個(gè)概念在判斷一個(gè)指針表達(dá)式(后面會(huì)解釋)是否是左值時(shí)很有用。
二、指針的算術(shù)運(yùn)算
指針的算術(shù)運(yùn)算和通常的數(shù)值的加減運(yùn)算的意義是不一樣的,指針的運(yùn)算是以單元為單位進(jìn)行的。
char a[20];
int *ptr=(int *)a; //強(qiáng)制類(lèi)型轉(zhuǎn)換并不會(huì)改變 a 的類(lèi)型
ptr++;
在上例中,指針 ptr 的類(lèi)型是int*
,它指向的類(lèi)型是int
,它被初始化為指向整型變量 a。
ptr++
把指針 ptr 的值加上了 sizeof(int),在 32 位程序中,是被加上了 4,因?yàn)樵?2 位程序中,int 占 4 個(gè)字節(jié)。
由于地址是用字節(jié)做單位的,故 ptr 所指向的地址由原來(lái)的 變量a 的地址向高地址方向增加了4 個(gè)字節(jié)。
由于char 類(lèi)型的長(zhǎng)度是一個(gè)字節(jié),所以,原來(lái) ptr 是指向數(shù)組a的第0號(hào)單元開(kāi)始的四個(gè)字節(jié),此時(shí)指向了數(shù)組a中從第4號(hào)單元開(kāi)始的四個(gè)字節(jié)。
我們可以用一個(gè)指針和一個(gè)循環(huán)來(lái)遍歷一個(gè)數(shù)組,看例子:
int array[20]={0};
int *ptr = array;
for(i=0;i<20;i++)
{ (*ptr)++; ptr++;
}
這個(gè)例子將整型數(shù)組中各個(gè)單元的值加1。由于每次循環(huán)都將指針ptr加1 個(gè)單元,所以每次循環(huán)都能訪問(wèn)數(shù)組的下一個(gè)單元。
三、運(yùn)算符&和*
&
是取址符,*
是間接運(yùn)算符。
&a
的運(yùn)算結(jié)果是一個(gè)指針,指針的類(lèi)型是 a 的類(lèi)型加個(gè)*
,指針?biāo)赶虻念?lèi)型是 a
的類(lèi)型,指針?biāo)赶虻牡刂肥?a
的地址。
*p
的結(jié)果是 p 所指向的東西
int a=12;
int b;
int *p;
int **ptr;
p=&a; //&a 的結(jié)果是一個(gè)指針,類(lèi)型是int*,指向的類(lèi)型是int,指向的地址是a 的地址。
*p=24; //*p 的結(jié)果,在這里它的類(lèi)型是int,它所占用的地址是p所指向的地址,顯然,*p就是變量a。
ptr=&p; //&p 的結(jié)果是個(gè)指針,該指針的類(lèi)型是p 的類(lèi)型加個(gè)*, 在這里是int **。該指針?biāo)赶虻念?lèi)型是p 的類(lèi)型,這里是int*。該指針?biāo)赶虻牡刂肪褪侵羔榩 自己的地址。
*ptr=&b; //*ptr 是個(gè)指針,&b 的結(jié)果也是個(gè)指針,且這兩個(gè)指針的類(lèi)型和所指向的類(lèi)型是一樣的,所以用&b 來(lái)給*ptr 賦 值就是毫無(wú)問(wèn)題的了。
**ptr=34; // *ptr 的結(jié)果是 ptr 所指向的東西,在這里是一個(gè)指針,對(duì)這個(gè)指針再做一次*運(yùn)算,結(jié)果是一個(gè) int 類(lèi)型的變量。
四、指針和數(shù)組
數(shù)組的數(shù)組名其實(shí)可以看作一個(gè)指針。
int array[10]={0,1,2,3,4,5,6,7,8,9},value;
value = array[0]; //也可寫(xiě)成:value=*array;
value = array[3]; //也可寫(xiě)成:value=*(array+3); int *pa = array;
*pa; //訪問(wèn)了第0 號(hào)單元
*(pa+1); //訪問(wèn)了第1 號(hào)單元
上例中,一般而言數(shù)組名 array
代表數(shù)組本身,類(lèi)型是int[10]
,但如果把 array 看做指針的話,它指向數(shù)組的第 0 個(gè)單元,類(lèi)型是 int*
所指向的類(lèi)型是數(shù)組單元的類(lèi)型即 int
。
int array[10];
int (*ptr)[10];
ptr = &array;
上例中 ptr 是一個(gè)指針,它的類(lèi)型是int(*)[10]
,指向的類(lèi)型是int[10]
,我們用整個(gè)數(shù)組的首地址來(lái)初始化它。在語(yǔ)句 ptr=&array 中,array 代表數(shù)組本身。
五、指針和結(jié)構(gòu)體
可以聲明一個(gè)指向結(jié)構(gòu)類(lèi)型對(duì)象的指針。
struct MyStruct
{ int a; int b; int c;
};
struct MyStruct ss={20,30,40};
struct MyStruct *ptr=&ss; 聲明了一個(gè)指向結(jié)構(gòu)對(duì)象ss 的指針。
請(qǐng)通過(guò)指針ptr 來(lái)訪問(wèn)ss 的三個(gè)成員變量
ptr->a; //指向運(yùn)算符,或者可以這們(*ptr).a,建議使用前者
ptr->b;
ptr->c;
六、指針和函數(shù)
可以把一個(gè)指針聲明成為一個(gè)指向函數(shù)的指針。
int fun1(char *,int);
int (*pfun1)(char *,int);
pfun1 = fun1;
int a = (*pfun1)("abcdefg",7); //通過(guò)函數(shù)指針調(diào)用函數(shù)。
可以把指針作為函數(shù)的形參。
在函數(shù)調(diào)用語(yǔ)句中,可以用指針表達(dá)式來(lái)作為實(shí)參。
int fun(char *);
int a;
char str[]="abcdefghijklmn";
a = fun(str);
int fun(char *s)
{ int num=0; for(int i=0;;) { num+=*s;s++; } return num;
}
這個(gè)例子中的函數(shù) fun 統(tǒng)計(jì)一個(gè)字符串中各個(gè)字符的 ASCII 碼值之和。
七、指針類(lèi)型轉(zhuǎn)換
當(dāng)我們初始化一個(gè)指針或給一個(gè)指針賦值時(shí),賦值號(hào)的左邊是一個(gè)指針,賦值號(hào)的右邊是一個(gè)指針表達(dá)式。在我們前面所舉的例子中,絕大多數(shù)情況下,指針的類(lèi)型和指針表達(dá)式的類(lèi)型是一樣的,指針?biāo)赶虻念?lèi)型和指針表達(dá)式所指向的類(lèi)型是一樣的。
float f = 12.3;
float *fptr = &f;
int *p = (int*)&f; // 不能直接賦值,要經(jīng)過(guò)類(lèi)型強(qiáng)轉(zhuǎn)
這樣強(qiáng)制類(lèi)型轉(zhuǎn)換的結(jié)果是一個(gè)新指針,該新指針的類(lèi)型是 TYPE *
,它指向的類(lèi)型是 TYPE
,它指向的地址就是原指針指向的地址。而原來(lái)的指針p 的一切屬性都沒(méi)有被修改。(切記)
利用強(qiáng)轉(zhuǎn)我們還可以把指針的值當(dāng)作一個(gè)整數(shù)取出來(lái),也可以把一個(gè)整數(shù)值當(dāng)作地址賦給一個(gè)指針。
八、指針的安全問(wèn)題
char s='a';
int *ptr;
ptr=(int *)&s;
*ptr=1298;
指針 ptr 是一個(gè)int*
類(lèi)型的指針,它指向的類(lèi)型是int
。它指向的地址就是 s 的首地址。在 32 位程序中,s 占一個(gè)字節(jié),int 類(lèi)型占四個(gè)字節(jié)。
最后一條語(yǔ)句不但改變了 s 所占的一個(gè)字節(jié),還把和 s 相臨的高地址方向的三個(gè)字節(jié)也改變了。
九、簡(jiǎn)答的指針例子
一個(gè)類(lèi)型里會(huì)出現(xiàn)很多運(yùn)算符,他們也像普通的表達(dá)式一樣,有優(yōu)先級(jí)。
其優(yōu)先級(jí)和運(yùn)算優(yōu)先級(jí)一樣。所以我總結(jié)了一下其原則:從變量名處起,根據(jù)運(yùn)算符優(yōu)先級(jí)結(jié)合,一步一步分析。
1)這是一個(gè)普通的整型變量
int p;
2)首先從 P
處開(kāi)始,先與 *
結(jié)合,所以說(shuō)明 P 是一個(gè)指針。然后再與 int
結(jié)合,說(shuō)明 P 所指向的類(lèi)型為int
。所以 P
是一個(gè) 指向整型數(shù)據(jù)的指針
int *p;
3)首先從 P
處開(kāi)始,先與 []
結(jié)合,說(shuō)明 P
是一個(gè)數(shù)組,然后與 int
結(jié)合,說(shuō)明數(shù)組里的元素是整型的,所以 P
是一個(gè)由整型數(shù)據(jù)組成的數(shù)組
int p[3];
4)首先從 P
處開(kāi)始,先與 []
結(jié)合,因?yàn)槠鋬?yōu)先級(jí)比 *
高,所以 P
是一個(gè)數(shù)組。然后再與 *
結(jié)合,說(shuō)明數(shù)組里的元素類(lèi)型是指針,然后再與 int
結(jié)合,說(shuō)明 P 是一個(gè)由型數(shù)指針?biāo)M成的數(shù)組
int *p[3];
5)首先從 P
處開(kāi)始,先與 *
結(jié)合,說(shuō)明 P
是一個(gè)指針。然后再與 []
結(jié)合,說(shuō)明 P 所指向的內(nèi)容是一個(gè)數(shù)組,然后再與 int
結(jié)合,說(shuō)明數(shù)組里的元素類(lèi)型是整型。所以 P 是一個(gè)整型數(shù)據(jù)組成的數(shù)組的指針 。
int (*p)[3];
6)首先從 P
開(kāi)始,先與 *
結(jié)合,說(shuō)是 P
是一個(gè)指針。然后再與 *
結(jié)合,說(shuō)明 P 所指向的類(lèi)型也是指針。然后再與 int
結(jié)合,說(shuō)明 P 指向的元素是指向int類(lèi)型的指針
。
int **p;
7)從 P 處起,先與 () 結(jié)合,說(shuō)明 P 是一個(gè)函數(shù),然后進(jìn)入 () 里分析,說(shuō)明該函數(shù)有一個(gè)整型變量的參數(shù)。然后再與外面的 int 結(jié)合,說(shuō)明函數(shù)的返回值是一個(gè)整型數(shù)據(jù) 。
int p(int);
8)從 P 處開(kāi)始,先與指針結(jié)合,說(shuō)明P 是一個(gè)指針,然后與()結(jié)合,說(shuō)明 P 指向的是一個(gè)函數(shù)。然后再與 ()
里的 int
結(jié)合,說(shuō)明函數(shù)有一個(gè) int 型的參數(shù),再與最外層的int 結(jié)合,說(shuō)明函數(shù)的返回類(lèi)型是整型。所以 P 是一個(gè)指向有一個(gè)整型參數(shù)且返回類(lèi)型為整型的函數(shù)的指針
int (*p)(int);
可以先跳過(guò),不看這個(gè)類(lèi)型。從 P 開(kāi)始,先與()結(jié)合,說(shuō)明P 是一個(gè)函數(shù),然后進(jìn)入 ()
里面,與 int
結(jié)合,說(shuō)明函數(shù)有一個(gè)整型變量參數(shù)。
然后再與外面的*
結(jié)合,說(shuō)明函數(shù)返回的是一個(gè)指針。然后到最外面一層,先與[]結(jié)合,說(shuō)明返回的指針指向的是一個(gè)數(shù)組。
然后再與*
結(jié)合,說(shuō)明數(shù)組里的元素是指針,然后再與int 結(jié)合,說(shuō)明指針指向的內(nèi)容是整型數(shù)據(jù).所以P 是一個(gè)參數(shù)為一個(gè)整數(shù)據(jù)且返回一個(gè)指向由整型指針變量組成的數(shù)組的指針變量的函數(shù).
int *(*p(int))[3];