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