1. 程式人生 > >雜論C語言指標筆記(一)

雜論C語言指標筆記(一)

雜論C語言指標筆記

指標是 C C++語言程式設計中最重要的概念之一,也是最容易產生困惑並導致程式出錯的問題之一。利用指標程式設計可以表示各種資料結構 , 通過指標可使用主調函式和被調函式之間共享變數或資料結構,便於實現雙向資料通訊;並能像組合語言一樣處理記憶體地址,從而編出精練而高效的程式。指標極大地豐富了C和 C++語言的功能。
接下來將談一下幾個內容:

a.判斷指標的型別

b.指標的運算

c.陣列和指標的同異

d.指向函式的指標

e.指標與抽象

閱讀C語言宣告--判斷指標的型別

優先順序規則

      宣告從名字開始

      優先順序依次是

              括號

      字尾操作符

              圓括號()表示函式

              方括號[]表示陣列

      字首操作符:星號*表示”指向...的指標“

如果const/volatile後面緊跟型別說明符(如int),則作用於其上,否則作用於左側緊鄰指標星號。

例:char * const *(*next)();

next是指向返回指向指向char型別的常量指標的指標的函式的指標

1、從語法的角度看,指標的型別是指把指標宣告語句中的指標名字去掉所剩下的部分。這是指標本身所具有的型別。
2、當你通過指標來訪問指標所指向的記憶體區時,指標所指向的型別決定了編譯器將把那片記憶體區裡的內容當做什麼型別來看待。從語法的角度看,指標所指向的型別是指標宣告語句中的指標名字和名字左邊的指標宣告符

*去掉所剩下的部分。
3、
指標的值或者叫指標所指向的記憶體區或地址,是指標本身儲存的數值,這個值將被編譯器當作一個地址,而不是一個一般的數值。在 32位程式裡,所有型別的指標的值都是一個 32位整數,因為 32位程式裡記憶體地址全都是 32位長。 指標所指向的記憶體區就是從指標的值所代表的那個記憶體地址開始,長度為 sizeof(指標所指向的型別 )的一片記憶體區。以後,我們說一個指標的值是 XX,就相當於說該指標指向了以 XX為首地址的一片記憶體區域;我們說一個指標指向了某塊記憶體區域,就相當於說該指標的值是這塊記憶體區域的首地址。
  指標所指向的記憶體區和指標所指向的型別是兩個完全不同的概念。在上例中,指標所指向的型別已經有了,但由於指標還未初始化,所以它所指向的記憶體區是不存在的,或者說是無意義的。
4、
指標本身所佔有的記憶體區是指標本身佔記憶體的大小,這個你只要用函式 sizeof(指標的 型別 )測一下就知道了。在 32位平臺裡,指標本身佔據了 4個位元組的長度。
指標本身佔據的記憶體這個概念在判斷一個指標表示式是否是左值時很有用。

指標的算術運算

參看:http://www.cnblogs.com/madengwei/archive/2008/02/18/1072422.html

賦值運算

指標變數初始化賦值如下:

int a;

int *ip=&a;

把一個變數的地址賦予指向相同資料型別的指標變數。例如:

int a;

int *ip;

ip=&a;             // 把整型變數 a 的地址賦予整型指標變數 ip

把一個指標變數的值賦予指向相同型別變數的另一個指標變數。例如:

int a;

int *pa=&a;

int *pb;

pb=pa;              // a 的地址賦予指標變數 pb

由於 pa,pb均為指向整型變數的指標變數,因此可以相互賦值。

把陣列的首地址賦予指向陣列的指標變數。例如:

int a[5],*pa;

pa=a;               // 陣列名錶示陣列的首地址,故可賦予指向陣列的指標變數 pa

也可寫為:

pa=&a[0];           // 陣列第一個元素的地址也是整個陣列的首地址也可賦予 pa

當然也可採取初始化賦值的方法:

int a[5],*pa=a;

以上是一些基本的陣列賦值方法,後面我們會詳細討論指標在陣列中的使用。

把字串的首地址賦予指向字元型別的指標變數。例如:

char *pc;

pc="c language";

或用初始化賦值的方法寫為:

char *pc=" c language ";

這裡應說明的是並不是把整個字串裝入指標變數, 而是把存放該字串的字元陣列的首地址裝入指標變數。

把函式的入口地址賦予指向函式的指標變數。例如:

int (*pf)();

pf=f;                //f 為函式名

加減運算

對於指向陣列的指標變數,可以加上或減去一個整數 n。設 ip是指向陣列 a的指標變數,則 ip+n,ip-n,ip++,++ip,ip--,--ip 運算都是合法的。指標變數加或減一個整數 n的意義是把指標指向的當前位置 (指向某陣列元素 )向前或向後移動 n個位置。應該注意,陣列指標變數向前或向後移動一個位置和地址加 1或減 1 在概念上是不同的。因為陣列可以有不同的型別, 各種型別的陣列元素所佔的位元組長度是不同的。如指標變數加 1,即向後移動 1 個位置表示指標變數指向下一個資料元素的首地址。而不是在原地址基礎上加 1。看如下例子:

char a[20];

int*ip=a;

...

ip++;

在上例中,指標 ip的型別是 int*,它指向的型別是 int,它被初始化為指向整形變數 a。接下來的第 3句中,指標 ip被加了 1,編譯器是這樣處理的:它把指標 ip的值加上了 sizeof(int),在 32位程式中,是被加上了 4。由於地址是用位元組做單位的,故 ip所指向的地址由原來的變數 a的地址向高地址方向增加了 4個位元組。

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

char a[20];

int*ip=a;

...

ip+=5;

在這個例子中, ip被加上了 5,編譯器是這樣處理的:將指標 ip的值加上 5 sizeof(int),在 32位程式中就是加上了 5 4=20。由於地址的單位是位元組,故現在的 ip所指向的地址比起加 5後的 ip所指向的地址來說,向高地址方向移動了 20個位元組。在這個例子中,沒加 5前的 ip指向陣列 a的第 0號單元開始的四個位元組,加 5後, ptr已經指向了陣列 a的合法範圍之外了。雖然這種情況在應用上會出問題,但在語法上卻是可以的。這也體現出了指標的靈活性。

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

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

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

關係運算

指向同一個陣列中的不同元素的兩個指標可以進行各種關係運算。例如:

ip1==ip2表示 ip1 ip2指向同一陣列元素

ip1>ip2表示 ip1處於高地址位置

ip1<ip2表示 ip2處於低地址位置

指標變數還可以與 0比較。設 ip為指標變數,則 ip==0表明 ip是空指標,它不指向任何變數; ip!=0表示 ip不是空指標。空指標是由對指標變數賦予 0值而得到的。例如:

#define NULL 0

int *ip=NULL;

 對指標變數賦 0值和不賦值是不同的。指標變數未賦值時,可以是任意值,是不能使用的。否則將造成意外錯誤。而指標變數賦 0值後,則可以使用,只是它不指向具體的變數而已。

取地址運算子‘ & ’和取內容運算子‘ *

取地址運算子 &是單目運算子,其結合性為自右至左,其功能是取變數的地址。

取內容運算子 *是單目運算子,其結合性為自右至左,用來表示指標變數所指的變數。在 *運算子之後跟的變數必須是指標變數。需要注意的是指標運算子 *和指標變數說明中的指標說明符 * 不是一回事。在指標變數說明中,‘ *’是型別說明符,表示其後的變數是指標型別。而表示式中出現的‘ *’則是一個運算子用以表示指標變數所指的變數。如下例子:

int a=12;

int b;

int *p;

int **ptr;

p=&a;   //&a 的結果是一個指標,型別是 int* ,指向的型別是 int ,指向的地址是 a

// 地址。

*p=24;   //*p 的結果,在這裡它的型別是 int ,它所佔用的地址是 p 所指向的地址。

ptr=&p; //&p 的結果是個指標,該指標的型別是 p 的型別加個 * ,在這裡是 int ** 。該

// 指標所指向的型別是 p 的型別,這裡是 int* 。該指標所指向的地址就是指標

//p 自己的地址。

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

**ptr=34;//*ptr 的結果是 ptr 所指向的東西,在這裡是一個指標,對這個指標再做一次 *

// 運算,結果就是一個 int 型別的變數。

關於括號組合

在解釋組合說明符時, 識別符號右邊的方括號和圓括號優先於識別符號左邊的“*”號,而方括號和圓括號以相同的優先順序從左到右結合。但可以用圓括號改變約定的結合順序。

閱讀組合說明符的規則是“從裡向外”。從識別符號開始,先看它右邊有無方括號或園括號,如有則先作出解釋,再看左邊有無*號。 如果在任何時候遇到了閉括號,則在繼續之前必須用相同的規則處理括號內的內容。

指標表示式

 一個表示式的最後結果如果是一個指標,那麼這個表示式就叫指標表式。所以指標表示式也具有指標所具有的四個要素 :指標的型別 ,指標所指向的型別 ,指標指向的記憶體區 ,指標自身佔據的記憶體。(預知後事如何,且聽下回分解!!!)