字符串
数组按常规顺序保存字符: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;}
这种方法比移动字符要快得多,对于长字符串更是如此。
随着继续学习和阅读更多代码,您会掌握越来越多的字符串操作技巧。勤加练习是关键。
评论
查看更多