因為是針對初學者或已將相關觀念淡忘的人作為複習參考, 可能有些地方的用詞不會太過於精確, 因此盡量以觀念上的理解為主要目標
範例程式如下:
#include "stdio.h"
int main()
{
/*--- part 1 ---*/
char *name[]={"jim", "harry", "ken"};
/*--- part 2 ---*/
printf("%s \n", *(name)); // result: jim
/*--- part 3 ---*/
printf("%c \n", **(name)); // result: j
/*--- part 4 ---*/
printf("%s \n", *(name)+1); // result: im
/*--- part 5 ---*/
printf("%s \n", *(name+1)); // result: harry
/*--- part 6 ---*/
printf("%s \n", *(name+1)+2); // result: rry
printf("%c \n", *(*(name+1)+2)); // result: r
return 0;
}
執行結果如下圖:
以下將按照code裡面各個部分進行講解(若有筆誤請告知)
---
part 1:
char *name[]={"jim", "harry", "ken"};上面這段C code的char *name[]={"jim", "harry", "ken"}
即宣告了三個位於連續位址的字串指標陣列元素(name[0], name[1], name[2]),
並分別令其指向三筆字串資料:"jim", "harry", "ken" (也就是將這些字串資料的address放到陣列元素內)
---
part 2:
printf("%s \n", *(name)); // result: jim在C語言中, 陣列名稱代表該陣列的第一個陣列元素的address (也就是該串陣列的起始位址)
所以name就是name[0]的address
所以*(name)就是name[0]元素的內容資料, 這個內容資料就是"jim"字串的第一個字元資料的address
而由於printf function裡面的%s吃的是字串資料的address, 並且會印出包含該address開始直到遇到字串結束字元(\0)為止的所有字元
所以printf("%s \n", *(name))會直接印出jim
---
part 3:
printf("%c \n", **(name)); // result: j承接part 2舉一反三可得知
**(name)就是"jim"字串資料中起始的字元資料('j')的address上面的資料(好像在繞口令...其實就是'j'啦)
而由於printf function裡面的%c是直接吃該address上面那個byte的資料(而非像%s那樣是吃字串資料的address並印出結束字元前的所有字元)
所以printf("%c \n", **(name)) 會印出j
---
part 4:
printf("%s \n", *(name)+1); // result: im已經由part 2知道*(name)就是"jim"字串資料的起始address (也就是j這個字元資料的address)
而因為name[0]裡面的這個內容是char資料型態的address, 所以當對這address做加法時, 該address的值會以字元資料長度(byte)為單位增加
(稍微題外話, 粗略來說, 電腦系統是一個byte對應一個address(此方式稱為byte addressing), 所以在對指向字元資料型態的位址值+1時, 實際位址值也是增加1。如果是32位元的整數型態資料位址, 其位址值加1時, 就會增加4, 因為32除以8等於4)
所以當我們把*(name)+1,就等同指向了j之後的下一個字元, 也就是i (註:運算子優先權順序是先*(name)然後才是+1)
printf function裡面的%s如同前面的解釋
所以printf("%s \n", *(name)+1); 會印出im
---
part 5:
printf("%s \n", *(name+1)); // result: harryname是name[0]的address
name+1相當於name[1]的address (對字串陣列元素address加1, 就變成下一個字串陣列元素的address)
所以*(name+1)即是name[1]的內容資料,也就是harry字串資料的起始字元資料(h)的address
故printf("%s \n", *(name+1)) 會印出harry
---
part 6:
printf("%s \n", *(name+1)+2); // result: rry
printf("%c \n", *(*(name+1)+2)); // result: r
由part 5可知*(name+1)就是第二陣列元素(name[1])中的字串資料harry的起始字元資料(h)的address (有點繞口令...)而對這個address進行加法,就會讓這個address變成是後面的字元資料的address
所以*(name+1)+2就代表著harry字串資料的第三個字元資料(r)的address,也就是h之後的第2個byte資料的address
所以printf("%s \n", *(name+1)+2)會印出rry
再舉一反三思考後可得知 *(*(name+1)+2)就是harry字串資料的第三個字元資料(r)的address的資料內容, 也就是r啦
所以printf("%c \n", *(*(name+1)+2))會印出r (如前面解釋過的, %c是直接吃資料本身, 而不是資料的address)
---
再來探討用指標陣列來儲存字串的一個好處
由範例程式可看出
我們是用 char *name[] = {"jim", "harry", "ken"}; 的方式來配置儲存這些字串的空間
這種作法,compiler會針對我們宣告的字串的字元數,自動配置出足夠容納這些字元的記憶體空間
但如果是純粹使用陣列來放置這些資料
必須用最大的字串數作為統一標準來配置出給每個字串的空間,但並非每個字串都像最大字串有這樣多的資料 (除非現在的compiler是否已聰明到可以自己優化這部分...)
char name[3][6] = {"jim", "harry", "ken"};
其中[6]是因為harry加上結數字元\0共須要有6個byte,但是jim和ken則只須要4個byte就可完整儲存,但是固定宣告了[6]給這兩個字串資料,有可能總共就多浪費了4個byte(各浪費2個),除非現在的compiler已聰明到可以自己優化這部分
不錯的介紹 簡單易懂!!
回覆刪除非常感謝您的鼓勵
刪除我才疏學淺,還有很多地方要學習,若有寫得不夠好的地方請大家見諒