字符串
C 语言中的字符串在很大程度上与指针交织在一起。您必须熟悉前面文章中介绍的指针概念,才能有效地使用 C 字符串。然而,一旦您习惯了它们,您通常可以非常高效地执行字符串操作。
C 语言中的字符串简单来说就是字符数组。以下行声明了一个数组,该数组最多可以容纳 99 个字符的字符串。
广告
char str[100];
它按预期保存字符:str[0] 是字符串的第一个字符,str[1] 是第二个字符,依此类推。但是,为什么一个 100 元素的数组不能容纳 100 个字符呢?因为 C 使用空终止字符串,这意味着任何字符串的末尾都用 ASCII 值 0(空字符)标记,在 C 语言中也表示为 '\0'。
空终止符与许多其他语言处理字符串的方式大不相同。例如,在 Pascal 中,每个字符串都由一个字符数组组成,其中有一个长度字节,用于记录数组中存储的字符数量。这种结构使得 Pascal 在您查询字符串长度时具有明显的优势。Pascal 可以直接返回长度字节,而 C 必须计算字符直到找到 '\0'。这个事实使得 C 在某些情况下比 Pascal 慢得多,但在其他情况下则更快,我们将在下面的示例中看到。
由于 C 语言本身不提供对字符串的显式支持,所有字符串处理函数都在库中实现。字符串 I/O 操作(gets、puts 等)在 <stdio.h> 中实现,而一组相当简单的字符串操作函数在 <string.h> 中实现(在某些系统上是 <strings.h>)。
字符串不是 C 语言原生类型的事实迫使您创建一些相当迂回的代码。例如,假设您想将一个字符串赋值给另一个字符串;也就是说,您想将一个字符串的内容复制到另一个字符串。在 C 语言中,正如我们在上一篇文章中看到的,您不能简单地将一个数组赋值给另一个数组。您必须逐个元素地复制它。字符串库(<string.h> 或 <strings.h>)包含一个名为 strcpy 的函数来完成此任务。以下是在普通 C 程序中非常常见的一段代码
char s[100]; strcpy(s, "hello");
在这两行执行后,下图显示了 s 的内容
顶部图显示了带有字符的数组。底部图显示了字符的等效 ASCII 码值,这就是 C 语言实际如何看待字符串的方式(作为包含整数值的字节数组)。有关 ASCII 码的讨论,请参阅位和字节的工作原理。
以下代码演示了如何在 C 语言中使用 strcpy
#include <string.h> int main() { char s1[100],s2[100]; strcpy(s1,"hello"); /* copy "hello" into s1 */ strcpy(s2,s1); /* copy s1 into s2 */ return 0; }
在 C 语言中初始化字符串时,都会使用 strcpy。您可以使用字符串库中的 strcmp 函数来比较两个字符串。它返回一个整数,表示比较结果。零表示两个字符串相等,负值表示 s1 小于 s2,正值表示 s1 大于 s2。
#include <stdio.h> #include <string.h> int main() { char s1[100],s2[100]; gets(s1); gets(s2); if (strcmp(s1,s2)==0) printf("equal\n"); else if (strcmp(s1,s2)<0) printf("s1 less than s2\n"); else printf("s1 greater than s2\n"); return 0; }
字符串库中其他常用函数包括 strlen(返回字符串的长度)和 strcat(连接两个字符串)。字符串库还包含许多其他函数,您可以通过阅读手册页来仔细研究。
为了帮助您开始构建字符串函数,并帮助您理解其他程序员的代码(每个人似乎都有自己的一套用于程序中特殊目的的字符串函数),我们将看两个例子:strlen 和 strcpy。下面是一个严格的 Pascal 风格的 strlen 版本
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'。如果省略它,稍后会发生严重错误,因为字符串没有结束符,因此长度未知。另请注意,此代码效率非常低,因为 strlen 在每次 for 循环中都会被调用。为了解决这个问题,您可以使用以下代码。
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 的值作为其结果。
将指针与字符串结合使用有时可以显著提高速度,如果您稍加思考,就可以利用这些优势。例如,假设您想删除字符串中的前导空格。您可能倾向于将字符移到空格上以将其删除。在 C 语言中,您可以完全避免这种移动。
#include <stdio.h> #include <string.h> int main() { char s[100],*p; gets(s); p=s; while (*p==' ') p++; printf("%s\n",p); return 0; }
这比移动技术快得多,特别是对于长字符串。
随着您的深入和阅读其他代码,您将掌握许多其他字符串技巧。实践是关键。