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