C语言常用字符串函数总结

时间:2021-02-22 19:13:22
ANSI C中有20多个用于处理字符串的函数:

注意:const 形参使用了const限定符,表示该函数不会改变传入的字符串。因为源字符串是不能更改的。

 

strlen函数:

函数原型:unsigned int strlen(const char*)

用于统计字符串的长度。举例如下

 1 void fit(char *,unsigned int);
 2 
 3 int main(void)
 4 {
 5     char mesg [] = "Things should be as simple as possible,""but not simpler.";
 6 
 7     puts(mesg);
 8     fit(mesg, 38);
 9     puts(mesg);
10     puts("Let's look at some more of the string.");
11     puts(mesg + 39);  //我们在38位置(空字符\0)后的39位置开始打印缓冲区余下的字符串。
12 
13     return 0;
14 }
15 
16 void fit(char *string, unsigned int size)  //利用strlen函数,设计一个函数可以缩短字符串的长度。
17 {
18     if(strlen(string)>size)
19         string[size] = '\0';
20 }

 

strcat()函数:

函数原型:char *strcat(char *strDest, const char *strSrc)

接受两个字符串作为参数,把第2个字符串的备份附加在第1个字符串的末尾。并把拼接后形成的新字符串作为第1个字符串。第2个字符串不变。

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define SIZE 80
 4 
 5 char * s_gets(char * st, int n);
 6 
 7 int main()
 8 {
 9     char flower[SIZE];
10     char addon [] = "s smell like old shoes.";
11 
12     puts("What is your favorite flower?");
13     if(s_gets(flower,SIZE))
14     {
15         strcat(flower,addon);  //使用了strcat函数进行字符串拼接 16         puts(flower);
17         puts(addon);
18     }
19     else
20         puts("End of file encountered!");
21     puts("bye");
22 
23     return 0;
24 }
25 
26 char * s_gets(char * st, int n)
27 {
28     char * ret_val;
29     int i=0;
30 
31     ret_val = fgets(st, n, stdin); //读取成功,返回一个指针,指向输入字符串的首字符;
32     if(ret_val)
33     {
34         while(st[i]!='\n' && st[i]!='\0')
35             i++;
36         if(st[i] =='\n') //fgets会把换行符也吃进来了,fgets会在末尾自动加上\0;
37             st[i]='\0';
38         else
39             while(getchar() != '\n')
40                 continue;
41     }
42     return ret_val;
43 }

 

strncat()函数:

strcat()函数有个缺点就是无法检查第1个数组是否能容纳第2个字符串。如果给第1个数组的空间不够大,多出来的字符溢出到相邻存储单元时就会出问题。

函数原型:extern char *strncat(char *dest,char *src,int n);

参数说明:第3个参数指定了最大添加字符数;

这段代码会拼接两个字符,检查第1个数组的大小,重点是确定最大能添加多少字符数;

 1 #include<stdio.h>
 2 #include<string.h>
 3 
 4 #define SIZE 30
 5 #define BUGSIZE 13
 6 
 7 char * s_gets(char * st,int n);
 8 
 9 int main(void)
10 {
11     char flower[SIZE];
12     char addon [] = "s smell like old shoes.";
13     char bug[BUGSIZE];
14     int available;
15 
16     puts("What is your favorite flower?");
17     s_gets(flower,SIZE);
18     if((strlen(addon)+strlen(flower)+1)<=SIZE) //important 19  strcat(flower,addon);
20     puts(flower);
21     puts("What is your favorite bug?");
22     s_gets(bug,BUGSIZE);
23     available= BUGSIZE -strlen(bug)-124     strncat(bug,addon,available);
25     puts(bug);
26     return 0;
27 }
28 
29 
30 
31 char * s_gets(char * st, int n)
32 {
33     char * ret_val;
34     int i=0;
35 
36     ret_val = fgets(st, n, stdin); //读取成功,返回一个指针,指向输入字符串的首字符;
37     if(ret_val)
38     {
39         while(st[i]!='\n' && st[i]!='\0')
40             i++;
41         if(st[i] =='\n') //fgets会把换行符也吃进来了,fgets会在末尾自动加上\0;
42             st[i]='\0';
43         else   //其实是'\0'
44             while(getchar() != '\n')  //会把缓冲区后续的字符都清空
45                 continue;
46     }
47     return ret_val;
48 }

我们发现strcat()也会造成缓冲区溢出。但是它没有像gets()一样被废除。gets()的安全隐患来自使用程序的人。而strcat()的安全隐患来自粗心的程序员。我们无法控制用户会进行什么操作,但是可以控制程序做什么。因此C语言相信程序员。程序员也有责任保证strcat()使用的安全。

 缓冲区溢出漏洞:程序员必须保证strcat的第一个参数有足够的空间。编译器无法报错是因为,这个函数的参数是指针类型,函数中也只是通过指针来读写这些内存的。函数根本不知道第一个参数所指的内存空间到底够不够大,函数本身不会对此进行检查。函数的大致行为是找到第一个参数所指的内存中字符串结尾的位置,然后从此处开始写入第二个参数的字符,直到写完。如果向第一个参数写入过多的字符,有可能会引起问题,也有可能不会。这取决于内存空间后面的内存是否可用来读写。万一覆盖了内存空间重要数据,就会引起错误。所以这是严重的安全隐患。

 

strcmp()函数

函数原型:int strcmp(const char *str1,const char *str2);

strcmp()函数返回的具体值不重要,我们只在意该值是0还是非0;比较两个字符串是否相等;我们关注的是字符串是否相等;

如果真要关心返回值的话,要理解比较的机制:其实是ASCII码值的比较;大写的字母ASCII值比小写的字母小;

比较的是:第一个字符串,相对第二个字符串的大小

str1<str2 返回负值;(ASCII的差值)

str1=str2 等于0;

str1>str2 返回正值;(ASCII的差值)

 1 //检查程序是否要停止读取输入
 2 
 3 #include <stdio.h>
 4 #include <string.h>
 5 
 6 #define SIZE 80
 7 #define LIM 10
 8 #define STOP "quit"
 9 
10 char * s_gets(char *st, int n);
11 
12 int main(void)
13 {
14     char input[LIM][SIZE];
15     int ct =0;
16 
17     printf("Enter up to %d lines(type quit to quit):\n",LIM);
18     while(ct<LIM && s_gets(input[ct],SIZE)!=NULL && strcmp(input[ct],STOP)!=0)
19         ct++;
20     printf("%d strings entered\n",ct);
21 
22     return 0;
23 }
24 
25 
26 char * s_gets(char * st, int n)
27 {
28     char * ret_val;
29     int i=0;
30 
31     ret_val = fgets(st, n, stdin); //读取成功,返回一个指针,指向输入字符串的首字符;
32     if(ret_val)
33     {
34         while(st[i]!='\n' && st[i]!='\0')
35             i++;
36         if(st[i] =='\n') //fgets会把换行符也吃进来了,fgets会在末尾自动加上\0;
37             st[i]='\0';
38         else   //其实是'\0'
39             while(getchar() != '\n')  //会把缓冲区后续的字符都清空
40                 continue;
41     }
42     return ret_val;
43 }

 

strncmp()函数:

函数原型:extern int strcmp(char *str1,char * str2,int n)

参数说明:str1为第一个要比较的字符串,str2为第二个要比较的字符串,n为指定的str1与str2的比较的字符数。

函数功能:比较字符串str1和str2的前n个字符。

返回说明:返回整数值:当str1<str2时,返回值<0; 当str1=str2时,返回值=0; 当str1>str2时,返回值>0。

 1 //比较前五个字符是否相等
 2 #include <stdio.h>
 3 #include <string.h>
 4 
 5 #define LISTSIZE 6
 6 
 7 int main()
 8 {
 9     const char *list[LISTSIZE]=
10     {
11         "astronomy","astounding",
12         "astrophysics","ostracize",
13         "asterism","astrophobia"
14     };
15     int count =0;
16     int i;
17 
18     for(i=0;i<LISTSIZE;i++)
19     {
20         if(strncmp(list[i],"astro",5)==0)
21             printf("Found:%s\n",list[i]);
22             count++;
23     }
24     printf("The list contained %d words beginning" " with astro.\n",count);
25 
26     return 0;
27 }

 

strcpy()函数:

函数原型char *strcpy(char *strDest, const char *strSrc)

这个函数相当于字符串的赋值运算,拷贝字符串;

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define SIZE 40
 4 #define LIM 5
 5 char * s_gets(char * st, int n);
 6 
 7 int main(void)
 8 {
 9     char qwords[LIM][SIZE];
10     char temp[SIZE];
11     int i = 0;
12 
13     printf("Enter %d words beginning with q:\n",LIM);
14     while(i<LIM && s_gets(temp,SIZE))
15     {
16         if(temp[0]!='q')
17             printf("%s doesn't begin with q!\n",temp);
18         else
19         {
20             strcpy(qwords[i],temp);
21             i++;
22         }
23     }
24     puts("Here are the words accepted:");
25     for(i=0;i<LIM;i++)
26     {
27         puts(qwords[i]);
28     }
29     return 0;
30 }

声明数组将分配储存数据的空间,声明指针只分配储存一个地址的空间;

使用strcpy函数的时候,程序员有责任保证目标字符串有足够的空间储存源字符串的副本;

strcpy的一些属性:其返回类型是char *,第一,该函数返回的是第1个参数的值,即一个字符的地址;第二,第1个参数不必指向数组的开始。这个属性可用于拷贝数组的一部分。代码如下:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #define WORDS "beast"
 4 #define SIZE 40
 5 
 6 int main(void)
 7 {
 8     const char * orig = WORDS;
 9     char copy[SIZE]= "Be the best that you can be.";
10     char *ps;
11 
12     puts(orig);
13     puts(copy);
14     ps = strcpy(copy+7,orig);
15     puts(copy);
16     puts(ps);
17 
18     return 0;
19 }

注意:源字符串也会把空字符也拷贝在内。

 

strncpy()函数:更谨慎的选择

函数原型:char *strncpy(char *dest, char *src, int n);

strcpy()和strcat()都有同样的问题,就是不能检查目标空间能否容纳源字符串的副本。拷贝字符串用strncpy()更安全。第3个参数指明可拷贝的最大字符数

 1 //还是输入五个q开头的单词,但是对单词输入的长度有限制
 2 #include <stdio.h>
 3 #include <string.h>
 4 #define SIZE 40
 5 #define TARGSIZE 7
 6 #define LIM 5
 7 
 8 char * s_gets(char * st, int n);
 9 
10 int main(void)
11 {
12     char qwords[LIM][TARGSIZE];
13     char temp[SIZE];
14     int i=0;
15     
16     printf("Enter %d words beginning with q:\n",LIM);
17     while(i<LIM && s_gets(temp,SIZE))
18     {
19             if(temp[0]!='q')
20                 printf("%s doesn't begin with q!\n",temp);
21             else
22             {
23                 strncpy(qwords[i],temp,TARGSIZE-1);
24                 qwords[i][TARGSIZE-1] ='\0';
25                 i++;
26             }
27     }
28     puts("Here are the words accepted:");
29     for (i=0;i<LIM;i++)
30     {
31         puts(qwords[i]); //puts末尾自动加上换行符
32     }
33     
34     return 0;
35 }
36 
37 
38 char * s_gets(char * st, int n)
39 {
40     char * ret_val;
41     int i=0;
42 
43     ret_val = fgets(st, n, stdin); //读取成功,返回一个指针,指向输入字符串的首字符;
44     if(ret_val)
45     {
46         while(st[i]!='\n' && st[i]!='\0')
47             i++;
48         if(st[i] =='\n') //fgets会把换行符也吃进来了,fgets会在末尾自动加上\0;
49             st[i]='\0';
50         else   //其实是'\0'
51             while(getchar() != '\n')  //会把缓冲区后续的字符都清空
52                 continue;
53     }
54     return ret_val;
55 }

strncpy(target,source,n)把source中的n个字符或空字符之前的字符(先满足哪个条件就执行哪个)拷贝到target中。

如果source中的字符数小于n,则拷贝整个字符串,包括空字符。

但是有一种情况就是万一没有把末尾的空字符拷贝进去的话,就不是存的字符串了。所以一般要把n设置成比目标数组的大小少1,然后把数组最后一个元素设置为空字符。

代码如下:

1 strncpy(qwords[i],temp,TARGSIZE-1);
2 qwords[i][TARGSIZE-1] ='\0';

这样做确保是一个字符串。

 

sprintf()函数

函数原型:int sprintf( char *buffer, const char *format [, argument] … );

注意:它函数声明在stdio.h中,而不是在string.h中;该函数和printf()有点像。但是它是把数据写入字符串中,而不是打印在显示器上。

第一个参数是目标字符串的地址。其余参数和printf()相同,即格式字符串和待写入项的列表

sprintf 是个变参函数,使用sprintf 对于写入buffer的字符数是没有限制的,这就存在了buffer溢出的可能性。

 1 //格式化字符串
 2 #include <stdio.h>
 3 #define MAX 20
 4 char * s_gets(char * st, int n);
 5 
 6 int main(void)
 7 {
 8     char first[MAX];
 9     char last[MAX];
10     char formal[2*MAX+10];
11     double prize;
12 
13     puts("Enter your first name:");
14     s_gets(first,MAX);
15     puts("Enter your last name:");
16     s_gets(last,MAX);
17     puts("Enter your prize money:");
18     scanf("%lf",&prize);
19     sprintf(formal,"%s, %-19s: $%6.2f\n",last,first,prize);
20     puts(formal);
21 
22     return 0;
23 }
24 
25 
26 
27 
28 
29 char * s_gets(char * st, int n)
30 {
31     char * ret_val;
32     int i=0;
33 
34     ret_val = fgets(st, n, stdin); //读取成功,返回一个指针,指向输入字符串的首字符;
35     if(ret_val)
36     {
37         while(st[i]!='\n' && st[i]!='\0')
38             i++;
39         if(st[i] =='\n') //fgets会把换行符也吃进来了,fgets会在末尾自动加上\0;
40             st[i]='\0';
41         else   //其实是'\0'
42             while(getchar() != '\n')  //会把缓冲区后续的字符都清空
43                 continue;
44     }
45     return ret_val;
46 }

 

strchr函数:

函数原型:char *strchr(const char *s, int c)

如果s字符串中包含c字符,该函数返回指向s字符串首位置(指向字符)的指针;如果在字符串中未找到c字符,该函数则返回空指针;

 

strrchr函数:

函数原型:char *strrchr(const char * s,int c)

该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针;

 

strstr函数:

函数原型:char *strstr(const char * s1, const char * s2);

返回值是指向s1字符串中s2字符串出现的首位置。如果在s1中没有找到s2,则返回空指针。

 

strpbrk函数:

函数原型:char *strpbrk(const char * s1, const char * s2);

如果s1字符中包含s2字符串中的任意字符,该函数返回指向s1字符串首位置的指针;如果在s1字符串中未找到任何s2字符串中的字符,则返回空指针。

 

size_t strlen(const char * s)

该函数返回s字符串中的字符数,不包括末尾的空字符

size_t类型是sizeof运算符返回的类型。

C规定sizeof运算符返回一个整数类型,但是并未指定是哪种整数类型

所以size_t在一个系统中可以是unsigned int,而在另一个系统中可以是unsigned long。

string.h头文件针对特定系统定义了size_t。这样就可以跨平台了,屏蔽平台之间的差异性。

 

//fgets()读入一行输入时,在目标字符串的末尾添加换行符

//处理末尾换行符的方法之一是循环检测换行符,然后替换成\0

 1 char * s_gets(char * st, int n)
 2 {
 3     char * ret_val;
 4     int i=0;
 5 
 6     ret_val = fgets(st, n, stdin); //读取成功,返回一个指针,指向输入字符串的首字符;
 7     if(ret_val)
 8     {
 9         while(st[i]!='\n' && st[i]!='\0')
10             i++;
11         if(st[i] =='\n') //fgets会把换行符也吃进来了,fgets会在末尾自动加上\0;
12             st[i]='\0';
13         else   //其实是'\0'
14             while(getchar() != '\n')  //会把缓冲区后续的字符都清空
15                 continue;
16     }
17     return ret_val;
18 }

有其他办法,使用strchr()代替s_gets():

1 #include <stdio.h>
2 
3 char line[80];
4 char * find;
5 
6 fgets(line, 80, stdin);
7 find = strchr(line, '\n');
8 if(find)
9     * find ='\0';

 

 

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

注意点:

1 while(* string)

2 while(*string != '\0')

许多程序员会在while循环条件中使用第一种的测试条件;string指向空字符时,*string的值是0,即测试条件为假,while循环结束。作为C语言程序员应该熟悉方法,第二种的测试条件没有第一种简洁。

 

const char * string和const char string[]的区别:

从技术方面讲,两者等效且都有效;

使用方括号是为了提醒用户,实际处理的参数是数组;

如果要处理字符串,使用指针形式的话,实际参数可以是数组名,双引号括起来的字符串或声明为char *类型的变量。这种写法是为了提醒用户,实际参数不一定是数组;

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

演示1:嵌套函数的调用

 1 #include <stdio.h>
 2 
 3 void put1(const char *);
 4 int put2(const char *);
 5 
 6 int main(void)
 7 {
 8     put1("If I'd as much money");
 9     put1(" as I could spend,\n");
10     printf("I count %d characters.\n",
11            put2("I never would cry old chairs to mend."));
12 
13     return 0;
14 }
15 
16 void put1(const char * string)
17 {
18     while(*string)
19         putchar(* string++);
20 
21 }
22 
23 int put2(const char * string)
24 {
25     int count = 0;
26     while(*string)
27     {
28         putchar(*string++);
29         count++;
30     }
31     putchar('\n');
32 
33     return(count);
34 }

分析:使用printf()打印put2的值,但是为了获得put2的返回值,计算机必须先执行put2(),因此执行put2()的过程中,打印了put2中的字符串。