C库提供了多个处理字符串的函数,ANSI-C把这些函数的原型放在string.h头文件中。其中最常用的函数有strlen()、strcat()、strcmp()、strncmp()、strcpy()和strncpy()。另外,还有sprintf()函数,其原型在stdio.h头文件中。
1 strlen()函数
strlen()函数用于统计字符串的长度。下面的函数可以缩短字符串的长度,其中用到了strlen():
void fit(char *string, unsigned int size){ if (strlen(string) > size) string[size] = '0';}
该函数要改变字符串,所以函数头在声明形式参数string时没有使用const限定符。程序test_fit.c中的程序测试了fit()函数。注意代码中使用了C字符串常量的串联特性。
/* test_fit.c -- try the string-shrinking function */#include #include /* contains string function prototypes */void fit(char *, unsigned int);int main(void){ char mesg[] = "Things should be as simple as possible," " but not simpler."; puts(mesg); fit(mesg,38); puts(mesg); puts("Let's look at some more of the string."); puts(mesg + 39); return 0;}void fit(char *string, unsigned int size){ if (strlen(string) > size) string[size] = '0';}
下面是该程序的输出:
Things should be as simple as possible, but not simpler.
Things should be as simple as possible
Let's look at some more of the string.
but not simpler.
fit()函数把第39个元素的逗号替换成'0'字符。puts()函数在空字符处停止输出,并忽略其余字符。然而,这些字符还在缓冲区中,下面的函数调用把这些字符打印了出来:
puts(mesg + 39);
表达式mesg + 39是mesg[39]的地址,该地址上存储的是空格字符。所以puts()显示该字符并继续输出直至遇到原来字符串中的空字符。下图演示了这一过程。
![12d2a1f1746a4508ca9aa0fff39226b4.png](https://i-blog.csdnimg.cn/blog_migrate/8d124699b3d55143e29e0afd3ff87cb5.jpeg)
The puts() function and the null character.
注意
一些ANSI之前的系统使用strings.h头文件,而有些系统可能根本没有字符串头文件。
string.h头文件中包含了C字符串函数系列的原型,因此程序test_fit.c要包含该头文件。
2 strcat()函数
strcat()(用于拼接字符串)函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型是char *(即,指向char的指针)。strcat()函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。
程序str_cat.c演示了strcat()的用法。该程序还使用了程序清单11.10的s_gets()函数。回忆一下,该函数使用fgets()读取一整行,如果有换行符,将其替换成空字符。
/* str_cat.c -- joins two strings */#include #include /* declares the strcat() function */#define SIZE 80char * s_gets(char * st, int n);int main(void){ char flower[SIZE]; char addon[] = "s smell like old shoes."; puts("What is your favorite flower?"); if (s_gets(flower, SIZE)) { strcat(flower, addon); puts(flower); puts(addon); } else puts("End of file encountered!"); puts("bye"); return 0;}char * s_gets(char * st, int n){ char * ret_val; int i = 0; ret_val = fgets(st, n, stdin); if (ret_val) { while (st[i] != 'n' && st[i] != '0') i++; if (st[i] == 'n') st[i] = '0'; else // must have words[i] == '0' while (getchar() != 'n') continue; } return ret_val;}
该程序的输出示例如下:
What is your favorite flower?
wonderflower
wonderflowers smell like old shoes.
s smell like old shoes.
bye
从以上输出可以看出,flower改变了,而addon保持不变。
3 strncat()函数
strcat()函数无法检查第1个数组是否能容纳第2个字符串。如果分配给第1个数组的空间不够大,多出来的字符溢出到相邻存储单元时就会出问题。当然,可以用strlen()查看第1个数组的长度。注意,要给拼接后的字符串长度加1才够空间存放末尾的空字符。或者,用strncat(),该函数的第3个参数指定了最大添加字符数。例如,strncat(bugs, addon, 13)将把addon字符串的内容附加给bugs,在加到第13个字符或遇到空字符时停止。因此,算上空字符(无论哪种情况都要添加空字符),bugs数组应该足够大,以容纳原始字符串(不包含空字符)、添加原始字符串在后面的13个字符和末尾的空字符。程序join_chk.c 使用这种方法,计算avaiable变量的值,用于表示允许添加的最大字符数。
/* join_chk.c -- joins two strings, check size first */#include #include #define SIZE 30#define BUGSIZE 13char * s_gets(char * st, int n);int main(void){ char flower[SIZE]; char addon[] = "s smell like old shoes."; char bug[BUGSIZE]; int available; puts("What is your favorite flower?"); s_gets(flower, SIZE); if ((strlen(addon) + strlen(flower) + 1) <= SIZE) strcat(flower, addon); puts(flower); puts("What is your favorite bug?"); s_gets(bug, BUGSIZE); available = BUGSIZE - strlen(bug) - 1; strncat(bug, addon, available); puts(bug); return 0;}char * s_gets(char * st, int n){ char * ret_val; int i = 0; ret_val = fgets(st, n, stdin); if (ret_val) { while (st[i] != 'n' && st[i] != '0') i++; if (st[i] == 'n') st[i] = '0'; else // must have words[i] == '0' while (getchar() != 'n') continue; } return ret_val;}
下面是该程序的运行示例:
What is your favorite flower?RoseRoses smell like old shoes.What is your favorite bug?AphidAphids smell
读者可能已经注意到,strcat()和gets()类似,也会导致缓冲区溢出。为什么C11标准不废弃strcat()