星期六, 4月 17, 2010

C語言指標(編輯中)

1. 指標機制
記得十年前一走到書局,一堆堆的電腦書都是有關於C語言的,但是現在走到書局C語言的只佔了不到10%,難道C語言變得越來越不重要了嗎?


其實覺得不然,一般電子用晶片,燒錄晶片中的程式除了組合語言外,一般最常用的就是C語言,而電子廠商也會發布自己晶片用的C語言編譯器供使用者編譯,所以這一個語言應該不可能被任何語言取代才是,雖然它已經誕生N年了。

C語言一個最具代表性的機能即為直接操作物件的記憶體位。透過指標操作,可以使我們程式更為靈活、便利。一般變數無法處理的也可以用指標方式來達成我們所需要的結果。使用指標可以用類似於組合語言的方式來處理程式,透過指標可以讓我們的程式更為簡節、強大。

但是指標的觀念是非常不易理解的,所以讀書時老師常說,程式語言學的好不好看指標學的好不好就知道了。所以將提供多指標範例,讓我們可以對指標有更多一層的了解。

2. 組合語言變數的處理方式
以前組合語言的處理方式為:
-----將25存入位址2266
-----將33存入位址5666
3. C語言變數的處理方式
如果需要處理大量的變數時,將記憶體中的變數一一記住是不可能的事情。所以我們會將記憶體的位址取一個容易記住的外號。例如:
----位址2266稱為data1。位址5666稱為data2
決定完外號後,我們便可以利用外號來存取記憶體位址。例如:
-----將data1內放入25
-----將data2內放入33
而data1及data2即使我們所稱的變數。利用變數的操作,即使我們忘記了記憶體位址也沒有任何關係。這是因為在編譯器內有一個位址與變數名對應表,透過這一個對應表我們便可以存取變數到正確的記憶體位址。

例圖~~~~~~~~

這裡舉一個編譯器內部的運作過程:
char data1,data2;

例圖~~~~~~~~

在組合語言中指派變數的方法分成直接指派及間接指派

例圖~~~~~~~~

而一般的C語言變數並無法達到這種機能,因為一般的C語言變數即為"資料變數",看字就知道是是用來儲存資料的啦!!

4. 專門儲存位址的變數
有鑑於此,所以C語言便使用了一種有別於一般變數,一直儲存著位址的變數==>指標,所以
一般變數==>儲存資料
指標==>儲存位址
所以指標即是儲存記憶體位址用的變數。例如,指標的值為1000,代表著1000號位址而不是資料1000。因此我們便可以利用這一個指標間接的將變數值取出。
a=*var;
上式說明,我們將指標var所指向的值間接取出給a。

5. 指標的演算
如此一來我們便可以將c語言的一般變數和指標變數分開來對待。但是既然不同有如何讓他們一起工作呢??
變數值==>指標中的值(位址)裡的資料
放置變數值的記憶體位址==>指標的值
為了讓兩者可以有一個交流的管道,於是準備了指標運算子:
&:位址運算子,將物件的位址取出
*:間接取值運算子,將指標的值(位址)中的資料取出
假設aa為一般變數值,pp為指標
pp=&aa; -------取aa的位址放入pp中
aa=*pp;--------將PP所只向的位址中的資料交給aa
如此一來便可以達成如組合語言一般的直接指派及間接指派。

圖~~~~~~~~~~~~~~~~~

6. 指標的宣告
由於指標所指向的的資料型別不同會影響(*pp)時,所去出的BYTE數

圖~~~~~~~~~~~~~~~~~~~~

所以宣告指標時需要說明所指向的資料型別。
TYPE *VAR;
知道資料型別後編譯器便可以清楚的知道間接取值時,記憶體中的起始位址和終點位址
資料型別變數和指標變數的比較一覽表我們可以用下圖來表示。
表1-1

7.指標的初期化
一般人的通常會發生以下這一種錯誤
int *pp;
*pp=10;
上述這一個範例看似好像沒錯,但是仔細一看其實有其破綻,如果一個指標變數在未分配一個位址給它之前如何分配變數值給它呢??
一般指標的初期化分成下列數種方法:
int d;
int *pp=&d;             //變數記憶體位址指定給PP

char s[10];                
char *p=s;              //P中為S陣列帶頭位址
char *p=&s[3];          //P中為S[3]位址

char *p="ABCD";          //在記憶體中建立一個ABCD字串,然後把帶頭位址交給P
char *p=(char *)malloc(100);   //在記憶體中規畫一個位址,然後把帶頭位址交給P

8. 指標及陣列
一般我們將屬性相同的資料放入同一個陣列之中,而陣列的名稱及位該串陣列帶頭位址的指標,所以一般的陣列除了可以透過索引的方式取值外,還可以透過類似指標的方式來操作陣列值。
char ch,ss[]="ABCDE";
char *p=ss;
ch=ss[3];
ch=p[3];
以上兩個例子是透過一般索引的方式來操作陣列變數。以下說明利用指標方式來操作變數。
ch=*(ss+3);
ch=*(p+3);
當然我們也可以用++來讓位址遞增,而遞增的單位即為指標變數所佔的BYTE數。

9. 指標及文字列
其實指標語文字列、或指標與陣列其實觀念十分相近,唯一不同處
char ss[4]="ABCD";
char *p="ABCD";
這兩者不一樣的地方在,第一種方式先在記憶體中建立一個陣列再將值填入,第二種方式為先在記憶體中建立一個文字串然後再將該文字串的先頭位址交給指標。

10. 指標的指標
其實指標的指標也不是這樣的難以了解,只要將表示式做一點變換相信會更容易了解。
**argv;
*(*argv);
可以想像成括號內的指標為一個一元陣列,加上外面的星號,可以理解成括號裡的每一個陣列又分別可存放列一個陣列即為argv[][]。

11. 函數指標
函數指標即為指向函數先頭位址的指標,函數指標的括號所在是很重要的
例如:
int* A();          //A為返回值為int變數位址之函數
int (*A)();        //這樣才是函數指標

12. 函數引數為指標
一般函數引數可以分為傳值及傳址兩種,而如果函數的引數為指標的話可以視為傳址
例如:   void  test(void (*pfun)(inta),int *a,int *b)
其實上式看似複雜,但是只要把握一個原則會發現指標的操作都是一樣的,那就是凡是遇到指標變數"那就給它一個位址吧!!!"

沒有留言:

張貼留言