第一章為程序設(shè)計(jì)基礎(chǔ),本文為1.8.3 指針數(shù)組中的第二要點(diǎn):字符串與指針的指針、第三要點(diǎn):字符串與二維數(shù)組。
>>>>1.字符串與指針的指針
除了作為sizeof或&的操作數(shù)外,指針數(shù)組的數(shù)組名在表達(dá)式中等價(jià)一個(gè)雙重指針常量,其右值為數(shù)組變量的首地址。比如:
int main(int argc, char *argv[])
其完全等價(jià)于
int main(int argc, char **argv)
顯然,如果要訪問(wèn)一個(gè)指針數(shù)組,使用指向指針的指針最為方便,但稍不注意偶爾也會(huì)寫出錯(cuò)誤的程序,詳見(jiàn)程序清單 1.45。
程序清單1.45一個(gè)錯(cuò)誤的范例程序
1 #include
2 int main(void)
3 {
4 char **pKeyWord;
5 static char * keyWord[5] = {"eagle", "cat", "and", "dog", "ball"};
6
7 pKeyWord = keyWord;
8 while(**pKeyWord != NULL)
9 printf("%s\n", *(pKeyWord++));
10 return 0;
11 }
由于字符串末尾的空字符或'\0'與空指針或NULL的值0恰好相等,因此上述代碼的編譯和執(zhí)行結(jié)果都是正確的,但確實(shí)是一個(gè)錯(cuò)誤的示例。因?yàn)榇蠖鄶?shù)編譯器在編譯程序清單 1.45(9)時(shí)進(jìn)行了類型轉(zhuǎn)換,將**pKeyWord由char類型轉(zhuǎn)換成了void *,或?qū)ULL由void *轉(zhuǎn)換成了char類型,但在編譯時(shí)一般會(huì)給出一條警告信息,因?yàn)榭兆址钦麛?shù)類型,而空指針是指針類型。
之所以寫出這樣的代碼,說(shuō)明程序員完全沒(méi)有理解“""”和NULL的區(qū)別。如果編譯器完全禁止char與指針之間的相互轉(zhuǎn)換,則上述代碼可能編譯失敗。由此可見(jiàn),需要認(rèn)真對(duì)待編譯器給出的每一條警告信息,并分析出現(xiàn)警告信息的原因,而不是僅僅編譯通過(guò)、程序執(zhí)行結(jié)果正確就萬(wàn)事大吉了。
程序清單 1.46針對(duì)程序清單 1.45的一種解決方案。其首先判斷*keyWord是否為空指針,如果為空指針,則退出循環(huán);如果不是,則輸出顯示該字符串,然后將pKeyWord加1指向下一個(gè)字符串。
程序清單 1.46 用指針數(shù)組變量與雙重指針變量處理多個(gè)字符串
1 #include
2 int main(void)
3 {
4 char **pKeyWord;
5 static char * keyWord[6] = {"eagle", "cat", "and", "dog", "ball", "NULL"};
6
7 pKeyWord = keyWord;
8 while(*pKeyWord != NULL)
9 printf("%s\n", *(pKeyWord++));
10 return 0;
11 }
由于指針類型的數(shù)組也是一維數(shù)組,因此雙重指針的算術(shù)運(yùn)算與普通指針的算術(shù)運(yùn)算十分相似。當(dāng)pKeyWord指向keyWord時(shí),keyWord[i]、pKeyWord[i]、*(keyWord+i)與*(pKeyWord+i)是等效的訪問(wèn)指針數(shù)組變量元素的4種表現(xiàn)形式。keyWord[i]指向了第i個(gè)字符串的首地址,即第i個(gè)字符串第1個(gè)字符的地址。若訪問(wèn)指針keyWord[0]所指向的目標(biāo)變量,則*keyWord[0]的值是字符串"Monday"的第1個(gè)字符M。當(dāng)然,*keyWord[0]也可以寫成*pKeyWord[0]、**keyWord、**pKeyWord等表現(xiàn)形式。
程序清單 1.47是針對(duì)程序清單 1.45的另一種解決方案。其首先判斷*pKeyWord所指向的是否為空字符串(即只包含'\0'的字符串,也就是字符串第0個(gè)元素為'\0'的字符串),如果為空字符串,則退出循環(huán);如果不是,則輸出顯示該字符串,然后將pKeyWord加1指向下一個(gè)字符串。
程序清單1.47用指針數(shù)組變量與雙重指針變量處理多個(gè)字符串(2)
1 #include
2 int main(void)
3 {
4 char **pKeyWord;
5 static char * keyWord[6] = {"eagle", "cat", "and", "dog", "ball", "NULL"};
6
7 pKeyWord = keyWord;
8 while(**pKeyWord != ""[0]) // ""[0]等價(jià)于'\0'
9 printf("%s\n", *(pKeyWord++));
10 return 0;
11 }
在實(shí)際的應(yīng)用中,程序清單 1.47(9)中的“""[0]”是一種非常少見(jiàn)的用法。如果用'\0'代替它則功能一樣,執(zhí)行效率還稍微高一點(diǎn)。由于字符串常量是只讀字符數(shù)組,因此字符串常量“""”就是只有字符串結(jié)束字符'\0'的字符串常量,即數(shù)組變量的第0個(gè)元素的值為'\0'。由于“""”是一個(gè)數(shù)組變量,因此可以使用下標(biāo)運(yùn)算符對(duì)“""”進(jìn)行求值運(yùn)算,獲得指定的數(shù)組元素,從而得到“""[0]”的值'\0'。一般來(lái)說(shuō),在大多數(shù)程序中都直接使用'\0'不使用""[0],而程序清單 1.47(8)之所以使用“""[0]”有兩重意義:
●與程序清單 1.47(5)對(duì)應(yīng),使程序的含義更清晰。當(dāng)*pKeyWord指向最后一個(gè)字符串的第0個(gè)元素時(shí),則結(jié)束循環(huán);注意:這個(gè)字符串的第0個(gè)元素與其他任何一個(gè)字符串的第0個(gè)元素都不相同。
●可移植性更好。如果將來(lái)C語(yǔ)言的字符修改了結(jié)束字符的定義,則程序也不必修改。比如,為了支持中文,將一個(gè)中文字作為一個(gè)字符,則字符類型必須修正,因?yàn)樗辉偈?位,所以其結(jié)束字符也可能修改。
如果要存儲(chǔ)靜態(tài)的表格式數(shù)據(jù),當(dāng)然應(yīng)該用數(shù)組。搜索程序必須知道數(shù)組中有多少個(gè)元素,對(duì)這個(gè)問(wèn)題的處理方法是傳遞一個(gè)數(shù)組長(zhǎng)度參數(shù)。這里采用的另一種方法是選擇指針數(shù)組方式。即:
const char * keyWord[6] = {"eagle", "cat", "and", "dog", "ball", NULL};
即在表尾增加了一個(gè)NULL指針,這個(gè)NULL指針使函數(shù)在搜索這個(gè)表時(shí)能夠檢測(cè)到表的結(jié)束,而無(wú)需預(yù)先知道表的長(zhǎng)度,其相應(yīng)的搜索范例程序詳見(jiàn)程序清單 1.48。
程序清單1.48搜索范例程序
1 int lookup(char *word, char *keyword[])
2 {
3
4 for(int i = 0; keyiWord[i] != NULL; i++)
5 if(strcmp(word, keyWord[i] == 0))
6 return i;
7 return -1;
8 }
在C語(yǔ)言中,字符串?dāng)?shù)組參數(shù)可以是char *keyiWord[]或char **keyWord,雖然它們都是等價(jià)的,但前一種形式能將參數(shù)的使用方式表達(dá)得更清楚。
這里采用的搜索算法稱為順序搜索,它逐個(gè)查看每個(gè)數(shù)據(jù)是不是要找的那一個(gè)。如果數(shù)據(jù)的個(gè)數(shù)不多,順序搜索也很快。標(biāo)準(zhǔn)庫(kù)中提供了一些函數(shù),它們可以處理某些特定類型的順序搜索問(wèn)題,比如,strchr和strstr能搜索給定的字符串中的字符或子串,如果對(duì)某個(gè)數(shù)據(jù)類型有這種函數(shù)就應(yīng)該直接使用它。
雖然搜索看起來(lái)非常簡(jiǎn)單,但它的工作量與被搜索數(shù)據(jù)的個(gè)數(shù)成正比。如果要找的的數(shù)據(jù)并不存在,而數(shù)據(jù)量加倍也會(huì)使搜索的工作量加倍。這是一種線性關(guān)系,其運(yùn)行時(shí)間是數(shù)據(jù)規(guī)模的線性函數(shù),因此這種搜索也被稱為線性搜索。
>>>>2.字符串與二維數(shù)組
有兩種風(fēng)格描述C風(fēng)格的字符串?dāng)?shù)組,即二維數(shù)組和指針數(shù)組,比如:
char keyWord[][6] = {"eagle", "cat", "and", "dog", "ball"};
char * keyWord[5] = {"eagle", "cat", "and", "dog", "ball"};
其中,第1個(gè)聲明創(chuàng)建了一個(gè)二維數(shù)組,詳見(jiàn)圖 1.16(a)。第2個(gè)聲明創(chuàng)建了一個(gè)指針數(shù)組,每個(gè)指針元素都初始化為指向各個(gè)不同的字符串常量,相加圖 1.16(b)。
圖 1.16 矩形數(shù)組和不規(guī)則數(shù)組
如果改用二維數(shù)組代替指針數(shù)組修改程序清單 1.44,這兩種方法使用了相同的初始化列表,顯示字符串的for循環(huán)代碼也相同,因此只要修改形參和局部變量的聲明即可。由于數(shù)組變量名的值是指針,因此無(wú)論傳遞給函數(shù)的是指針還是數(shù)組名,函數(shù)都能運(yùn)行。盡管它們的聲明不同,但從某些方面看起來(lái),它們非常相似,兩者都代表5個(gè)字符串。當(dāng)使用一個(gè)下標(biāo)時(shí)都分別表示一個(gè)字符串,但兩者的類型并不相同。當(dāng)使用2個(gè)下標(biāo)時(shí)都分別表示一個(gè)字符,比如,keyWord[1][2]表示keyWord數(shù)組中第2個(gè)指針指向的字符串的第3個(gè)字母't'。初看上去二維數(shù)組的效率似乎低一些,因?yàn)樗恳恍械拈L(zhǎng)度都被固定為剛好能容納最長(zhǎng)的關(guān)鍵字,但它不需要任何指針。另一方面,指針數(shù)組也要占用內(nèi)存,但是每個(gè)字符串常量占用的內(nèi)存空間只是它本身的長(zhǎng)度。
如果它們的長(zhǎng)度差不多,那么二維數(shù)組形式更緊湊一些。如果各個(gè)字符串的長(zhǎng)度差別很大,絕大多是字符串都很短,只有少數(shù)幾個(gè)很長(zhǎng),那么使用指針數(shù)組形式會(huì)更緊湊一些,取決于指針?biāo)加玫目臻g是否小于每個(gè)字符串都存儲(chǔ)于固定長(zhǎng)度的行所浪費(fèi)的空間。實(shí)際上,除了非常巨大的表,它們之間的差別是非常小的,所以根本不重要。除非要改變其中的任何一個(gè)字符串,二維數(shù)組是更好的選擇。
-
指針
+關(guān)注
關(guān)注
1文章
480瀏覽量
70576 -
C語(yǔ)言編程
+關(guān)注
關(guān)注
6文章
90瀏覽量
21110 -
周立功
+關(guān)注
關(guān)注
38文章
130瀏覽量
37660 -
數(shù)組
+關(guān)注
關(guān)注
1文章
417瀏覽量
25968
原文標(biāo)題:周立功:用數(shù)組,構(gòu)造一個(gè)雙重指針
文章出處:【微信號(hào):ZLG_zhiyuan,微信公眾號(hào):ZLG致遠(yuǎn)電子】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論