字符串
數組按常規順序保存字符:str[0]保存字符串的第一個字符,str[1]保存第二個,等等。但為什么一個大小為100的數組會容納不下大小為100的字符串呢?這是因為C使用零結尾的字符串,即所有字符串的末尾字符必須標記為ASCII的零值(null字符),在C語言中用0表示。
零結尾方式與許多其他編程語言處理字符串的方式是很不一樣的。例如,Pascal語言中的字符串是由一個字符數組和一個記錄數組長度的字節組成的。這種結構確實使Pascal在獲取字符串長度上占有優勢。Pascal只需直接返回長度計數即可,而C卻要對0之前字符進行一遍計數。結果是,在某些情況下C比Pascal要慢許多,而另一些時候卻反而更快。下面的例子也可以說明這一點。
因為C語言本身不直接支持字符串,所以全部的字符串操作都是由函數庫提供的。字符串的輸入/輸出操作(gets、puts之類)由
既然字符串不是C內置的類型,所以您不得不寫一些比較繁瑣的代碼。比如您要將一個字符串賦值給另一個字符串,即復制一個字符串的內容。正如我們在上一節看到的,C中不能簡單地在數組間賦值,而要將元素逐個復制。字符串函數庫
char s[100];
strcpy(s, "hello");
這兩行執行以后,s的內容如下圖所示:
圖中上面一行顯示數組中的字符,下面一行顯示數組中字符對應的ASCII碼,這也是實際上C中字符串的表示方法(包含整數的字節數組)。關于ASCII碼的討論請參見位和字節。
下面是一個在C中使用strcpy的例子:
#include
int main()
{char s1[100],s2[100];strcpy(s1,"hello"); /* 將 "hello" 復制到 s1 */strcpy(s2,s1); /* 將 s1 復制到 s2 */
return 0;}
在C中,strcpy用于初始化字符串。字符串庫中的strcmp函數用于比較兩個字符串,它返回一個整數報告比較結果。零表示兩個字符串相等,負數表示s1小于s2,正數表示s1大于s2。
#include
#include
int main()
{char s1[100],s2[100];gets(s1);gets(s2);if (strcmp(s1,s2)==0)
printf("相等n");else if (strcmp(s1,s2)<0)
printf("s1 小于 s2n");
else
printf("s1 大于 s2n");return 0;}
字符串庫中的常用函數還有strlen,它返回字符串的長度;以及strcat,它將兩個字符串連接起來。除此之外還有許多函數,詳見手冊頁。
為引導您編寫自己的字符串處理函數,并幫助您讀懂其他程序員的代碼(程序員寫程序的時候似乎都有自己的一套專用字符串函數),我們將考察兩個例子:strlen和strcpy。下面的strlen函數很像Pascal的代碼:
int strlen(char s[])
{int x;x=0;while (s[x] != '0')
x=x+1;return(x);}
多數C程序員會避免這種寫法,因為它看起來很低效。取而代之的常常是一種基于指針的寫法:
int strlen(char *s)
{int x=0;
while (*s != '0')
{x++;s++;}
return(x);}
這段代碼還可以化簡成:
int strlen(char *s)
{int x=0;while (*s++)
x++;return(x);}
我想一位真正的C語言高手還可以進一步將代碼縮減。
我在一臺MicroVAX上使用gcc不加優化地編譯了上面的三段代碼,然后將每段代碼都在一個長度為120的字符串上運行20,000 遍,得到的比較結果是:第一段代碼耗時12.3秒,第二段代碼耗時12.3秒,第三段代碼耗時12.9秒。結論是什么?對我來說,結論就是應該以自己最容易理解的方式編寫代碼。使用指針操作的代碼一般會更快一些,但上面strlen的例子說明也不盡然。
對于strcpy函數,我們可以如法炮制:
strcpy(char s1[],char s2[])
{int x;for (x=0; x<=strlen(s2); x++)
s1[x]=s2[x];}
注意,在for循環中使用<=是很關鍵的,這樣一來復制過程就包含上'0'。一定要復制'0'。否則程序到后面將發生嚴重的錯誤,因為字符串失去了結束標志就無法確定其長度。還請注意這段代碼的效率很低,這是因為for循環每次都要調用strlen函數。為解決此問題,您可以使用下面的代碼:
strcpy(char s1[],char s2[])
{int x,len;len=strlen(s2);for (x=0; x<=len; x++)
s1[x]=s2[x];}
類似地可以寫出指針版本。
strcpy(char *s1,char *s2)
{while (*s2 != '0')
{*s1 = *s2;s1++;
s2++;}}
并進一步化簡為:
strcpy(char *s1,char *s2)
{while (*s2)
*s1++ = *s2++;}
只要您愿意,甚至可以寫成while (*s1++ = *s2++);。用strcpy將一個長度為120的字符串復制10,000遍,四個版本分別耗時415秒、14.5秒、9.8秒和10.3秒。如您所見,在這里指針明顯提升了程序的性能。
從字符串庫中strcpy的函數原型可以看出,它被設計為返回一個指向字符串的指針:
char *strcpy(char *s1,char *s2)
多數字符串函數都會返回一個字符串指針作為結果。strcpy返回的是s1的值。
使用指針操作字符串有時會帶來明顯的速度提升。編寫程序前要想一想如何利用指針的這種優勢。例如,您要刪除某字符串開始處的空格。您可能會將后面的字符前移來覆蓋掉前面的空格。其實,您連一個字符也不用移動:
#include
#include
int main()
{char s[100],*p;gets(s);p=s;
while (*p==' ')
p++;printf("%sn",p);return 0;}
這種方法比移動字符要快得多,對于長字符串更是如此。
隨著繼續學習和閱讀更多代碼,您會掌握越來越多的字符串操作技巧。勤加練習是關鍵。
評論
查看更多