深入理解C語言指標的奧秘

2022-09-24 04:54:03 字數 4595 閱讀 4594

指標的概念

指標是乙個特殊的變數,它裡面儲存的數值被解釋成為記憶體裡的乙個位址。 要搞清乙個指標需要搞清指標的四方面的內容:指標的型別,指標所指向的型別,指標的值或者叫指標所指向的記憶體區,還有指標本身所佔據的記憶體區。

讓我們分別說明。

先宣告幾個指標放著做例子:

例一:(1)int*ptr;

(2)char*ptr;

(3)int**ptr;

(4)int(*ptr)[3];

(5)int*(*ptr)[4];

如果看不懂後幾個例子的話,請參閱我前段時間貼出的文章《如何理解c和c ++的複雜型別宣告》。

指標的型別

從語法的角度看,你只要把指標宣告語句裡的指標名字去掉,剩下的部分就是這個指標的型別。這是指標本身所具有的型別。讓我們看看例一中各個指標的型別:

(1)int*ptr;//指標的型別是int*

(2)char*ptr;//指標的型別是char*

(3)int**ptr;//指標的型別是int**

(4)int(*ptr)[3];//指標的型別是int(*)[3]

(5)int*(*ptr)[4];//指標的型別是int*(*)[4]

怎麼樣?找出指標的型別的方法是不是很簡單?

指標所指向的型別

當你通過指標來訪問指標所指向的記憶體區時,指標所指向的型別決定了編譯器將把那片記憶體區里的內容當做什麼來看待。

從語法上看,你只須把指標宣告語句中的指標名字和名字左邊的指標宣告符*去掉,剩下的就是指標所指向的型別。例如:

(1)int*ptr;//指標所指向的型別是int

(2)char*ptr;//指標所指向的的型別是char

(3)int**ptr;//指標所指向的的型別是int*

(4)int(*ptr)[3];//指標所指向的的型別是int()[3]

(5)int*(*ptr)[4];//指標所指向的的型別是int*()[4]

在指標的算術運算中,指標所指向的型別有很大的作用。

指標的型別(即指標本身的型別)和指標所指向的型別是兩個概念。當你對c越來越熟悉時,你會發現,把與指標攪和在一起的"型別"這個概念分成"指標的型別"和"指標所指向的型別"兩個概念,是精通指標的關鍵點之一。我看了不少書,發現有些寫得差的書中,就把指標的這兩個概念攪在一起了,所以看起書來前後矛盾,越看越糊塗。

指標的值,或者叫指標所指向的記憶體區或位址

指標的值是指標本身儲存的數值,這個值將被編譯器當作乙個位址,而不是乙個一般的數值。在32位程式裡,所有型別的指標的值都是乙個32位整數,因為32位程式裡記憶體位址全都是32位長。 指標所指向的記憶體區就是從指標的值所代表的那個記憶體位址開始,長度為si zeof(指標所指向的型別)的一片記憶體區。

以後,我們說乙個指標的值是xx,就相當於說該指標指向了以xx為首位址的一片記憶體區域;我們說乙個指標指向了某塊記憶體區域,就相當於說該指標的值是這塊記憶體區域的首位址。

指標所指向的記憶體區和指標所指向的型別是兩個完全不同的概念。在例一中,指標所指向的型別已經有了,但由於指標還未初始化,所以它所指向的記憶體區是不存在的,或者說是無意義的。

以後,每遇到乙個指標,都應該問問:這個指標的型別是什麼?指標指的型別是什麼?該指標指向了**?

指標本身所佔據的記憶體區

指標本身佔了多大的記憶體?你只要用函式sizeof(指標的型別)測一下就知道了。在32位平台裡,指標本身佔據了4個位元組的長度。

指標本身佔據的記憶體這個概念在判斷乙個指標表示式是否是左值時很有用。

指標的算術運算

指標可以加上或減去乙個整數。指標的這種運算的意義和通常的數值的加減運算的意義是不一樣的。例如:

例二:1、chara[20];

2、int*ptr=a;

......

3、ptr++;

在上例中,指標ptr的型別是int*,它指向的型別是int,它被初始化為指向整形變數a。接下來的第3句中,指標ptr被加了1,編譯器是這樣處理的:它把指標ptr的值加上了sizeof(int),在32位程式中,是被加上了4。

由於位址是用位元組做單位的,故ptr所指向的位址由原來的變數a的位址向高位址方向增加了4個位元組。

由於char型別的長度是乙個位元組,所以,原來ptr是指向陣列a的第0號單元開始的四個位元組,此時指向了陣列a中從第4號單元開始的四個位元組。

我們可以用乙個指標和乙個迴圈來遍歷乙個陣列,看例子:

例三:intarray[20];

int*ptr=array;

...//此處略去為整型陣列賦值的**。

...for(i=0;i<20;i++)

這個例子將整型陣列中各個單元的值加1。由於每次迴圈都將指標ptr加1,所以每次迴圈都能訪問陣列的下乙個單元。

再看例子:

例四:1、chara[20];

2、int*ptr=a;

......

3、ptr+=5;

在這個例子中,ptr被加上了5,編譯器是這樣處理的:將指標ptr的值加上5乘sizeof(int),在32位程式中就是加上了5乘4=20。由於位址的單位是位元組,故現在的ptr所指向的位址比起加5後的ptr所指向的位址來說,向高位址方向移動了20個位元組。

在這個例子中,沒加5前的ptr指向陣列a的第0號單元開始的四個位元組,加5後,ptr已經指向了陣列a的合法範圍之外了。雖然這種情況在應用上會出問題,但在語法上卻是可以的。這也體現出了指標的靈活性。

如果上例中,ptr是被減去5,那麼處理過程大同小異,只不過ptr的值是被減去5乘sizeof(int),新的ptr指向的位址將比原來的ptr所指向的位址向低位址方向移動了20個位元組。

總結一下,乙個指標ptrold加上乙個整數n後,結果是乙個新的指標ptrnew,ptrnew的型別和ptrold的型別相同,ptrnew所指向的型別和ptrold所指向的型別也相同。ptrnew的值將比ptrold的值增加了n乘sizeof(ptrold所指向的型別)個位元組。就是說,ptrnew所指向的記憶體區將比ptrold所指向的記憶體區向高位址方向移動了n乘sizeof(ptrold所指向的型別)個位元組。

乙個指標ptrold減去乙個整數n後,結果是乙個新的指標ptrnew,ptrnew的型別和ptrold的型別相同,ptrnew所指向的型別和ptrold所指向的型別也相同。ptrnew的值將比ptrold的值減少了n乘sizeof(ptrold所指向的型別)個位元組,就是說,ptrnew所指向的記憶體區將比ptrold所指向的記憶體區向低位址方向移動了n乘sizeof(ptrold所指向的型別)個位元組。

運算子&和*

這裡&是取位址運算子,*是...書上叫做"間接運算子"。

&a的運算結果是乙個指標,指標的型別是a的型別加個*,指標所指向的型別是a的型別,指標所指向的位址嘛,那就是a的位址。

*p的運算結果就五花八門了。總之*p的結果是p所指向的東西,這個東西有這些特點:它的型別是p指向的型別,它所占用的位址是p所指向的位址。

例五:inta=12;

intb;

int*p;

int**ptr;

p=&a;

//&a的結果是乙個指標,型別是int*,指向的型別是int,指向的位址是a的位址。

*p=24;

//*p的結果,在這裡它的型別是int,它所占用的位址是p所指向的位址,顯然,*p就是變數a。

ptr=&p;

//&p的結果是個指標,該指標的型別是p的型別加個*,在這裡是int **。該指標所指向的型別是p的型別,這裡是int*。該指標所指向的位址就是指標p自己的位址。

*ptr=&b;

//*ptr是個指標,&b的結果也是個指標,且這兩個指標的型別和所指向的型別是一樣的,所以用&b來給*ptr賦值就是毫無問題的了。

**ptr=34;

//*ptr的結果是ptr所指向的東西,在這裡是乙個指標,對這個指標再做一次*運算,結果就是乙個int型別的變數。

指標表示式

乙個表示式的最後結果如果是乙個指標,那麼這個表示式就叫指標表式。

下面是一些指標表示式的例子:

例六:inta,b;

intarray[10];

int*pa;

pa=&a;//&a是乙個指標表示式。

int**ptr=&pa;//&pa也是乙個指標表示式。

*ptr=&b;//*ptr和&b都是指標表示式。

pa=array;

pa++;//這也是指標表示式。

例七:char*arr[20];

char**parr=arr;//如果把arr看作指標的話,arr也是指標表示式

char*str;

str=*parr;//*parr是指標表示式

str=*(parr+1);//*(parr+1)是指標表示式

str=*(parr+2);//*(parr+2)是指標表示式

由於指標表示式的結果是乙個指標,所以指標表示式也具有指標所具有的四個要素:指標的型別,指標所指向的型別,指標指向的記憶體區,指標自身佔據的記憶體。

好了,當乙個指標表示式的結果指標已經明確地具有了指標自身佔據的記憶體的話,這個指標表示式就是乙個左值,否則就不是乙個左值。

在例七中,&a不是乙個左值,因為它還沒有佔據明確的記憶體。*ptr是乙個左值,因為*ptr這個指標已經佔據了記憶體,其實*ptr就是指標pa,既然pa已經在記憶體中有了自己的位置,那麼*ptr當然也有了自己的位置。

陣列和指標的關係

如果對宣告陣列的語句不太明白的話,請參閱我前段時間貼出的文章《如何理解c和c++的複雜型別宣告》。

c中如何深入理解「事件與委託」

中如何深入理解 事件與委託 初學者必看 2008年01月19日星期六 22 57 事件是特殊的委託 這是個人理解 首先介紹乙個為什麼要在事件中引入委託這個概念 事件是物件傳送的訊息,以發訊號通知操作的發生。操作可能是由使用者互動 例如滑鼠單擊 引起的,也可能是由某些其他的程式邏輯觸發的。引發事件的物...

C語言中指標的考點

考點一變數的位址和指標 1 位址和指標的定義 1 記憶體位址 計算機的記憶體是以位元組為單位的一片連續的儲存空間,每個位元組都有乙個編號,這個編號就稱為記憶體位址。一般情況下,在程式中只需指出變數名,無須知道每個變數在記憶體中的具體位址。程式中對變數驚喜訪問操作,實際上也就是對某個位址的儲存單元進行...

超經典的C語言指標講解

pointer 1 a pointer 2 b if a printf a d,b d pointer 1,pointer 2 return 0 void swap int p1,int p2 子涵數中將p1指向的變數 a 與p2指向的變數 b 互換,即將a和b的值互換,也就是a中放的是9,b中放的...