字符函数和字符串函数
- 1、strlen函数
- 1.1 strlen函数功能介绍
- 1.2 模拟实现strlen函数
- 2、strcpy函数
- 2.1 strcpy函数功能介绍
- 2.2 模拟实现strcpy函数
- 3、strcat函数
- 3.1 strcat函数功能介绍
- 3.2 模拟实现strcat函数
- 4、strcmp函数
- 4.1 strcmp函数功能介绍
- 4.2 模拟实现strcmp函数
- 5、strncpy函数
- 5.1strncpy函数功能介绍
- 6、strncat函数
- 6.1strncat函数功能介绍
- 7、strncmp函数
- 7.1 strncmp函数功能介绍
- 8、strstr函数
- 8.1 strstr函数功能介绍
- 8.2 模拟实现strstr函数
- 9、strtok函数
- 9.1 strtok函数功能介绍
- 10、strerror函数
- 10.1 strerror函数功能介绍
- 11、memcpy函数
- 11.1 memcpy函数功能介绍
- 11.2 模拟实现mencpy函数
- 12、memmove函数
- 12.1 memmove函数功能介绍
- 12.2 模拟实现memmove函数
- 13、memcmp函数
- 13.1 memcmp函数功能介绍
- 14、memset函数
- 14.1 memset函数功能介绍
- 15、总结
1、strlen函数
1.1 strlen函数功能介绍
strlen函数是求字符串长度的函数。
size_t strlen(const char* str);
strlen函数展示
#include #include int main() { char arr1[] = "abcdef";//这种写法在字符串结尾自动添上了'' char arr2[] = { 'a','b','c','d','e','f' };//这种写法求不了数组中储存的字符串长度,字符串结尾没有'',可以主动往里面放'' char arr3[20] = { 'a','b','c','d','e','f' }; //可以求字符串长度,因为给定了数组空间大小, //在存储未满时,后面自动填充了数字0,数字0对应空字符'' int len1 = strlen(arr1);//字符串以''作为结束标志,strlen函数返回的是在字符串中前面出现的字符个数(不包含'') int len2 = strlen(arr2); int len3 = strlen(arr3); printf("%dn", len1); printf("%dn", len2); printf("%dn", len3); if (strlen("abc") - strlen("qwerty") > 0) { printf(">n");//strlen的返回值是无符号数,无符号数-无符号数还是无符号数,大于0 } else { printf("<=n"); } return 0; }
打印结果
- 字符串以‘’作为结束标志,strlen函数返回的是在字符串中‘’前面出现的字符个数(不包含‘’)。
数组arr1与数组arr3都有‘’所以能计算出字符串长度是6,数组arr2储存的字符串在结尾没有‘’,会一直往后找‘’,直到找到为止,计算出的字符串长度是随机值。- 由上得出,函数参数指向的字符串必须要以‘’结尾。
- 为什么代码中的条件语句,得到的结果是 >号?
函数的返回值为size_t,是无符号类型的。无符号数-无符号数还是无符号数,大于0
1.2 模拟实现strlen函数
#include #include size_t my_strlen(const char* str) //const保护指针所指向的内容不被改变 { int count = 0; assert(str != NULL);//当str等于空指针时,编译会报错 while (*str) { str++; count++; } return count; } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("%dn", len); return 0; }
打印结果
- 采用计数器的形式,对一个一个字符进行计数,直到找到空字符‘’停止计数。模拟实现strlen函数还有递归的方法、指针-指针的方法。
- 库函数assert()
assert函数是一个断言函数,功能为:当括号中的条件不满足时,在程序进行编译的过程中会报错。
为了避免传过来的是个空指针,而编译人员又没有发现,使用assert函数进行断言,assert(str != NULL); 或者这样写assert(str);- const修饰指针变量
- const如果放在 * 的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,但是指针变量本身的内容可以改变。
- const如果放在 * 的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容可以通过指针改变。
- 我们的目的只是对字符数组中储存的字符串进行计数,为了避免改变指针指向内容,在参数用const对指针进行修饰。
2、strcpy函数
2.1 strcpy函数功能介绍
strcpy函数是对字符串进行拷贝的函数。
char* strcpy(char* destination, const char* source);
strcpy函数展示
#include #include int main() { char arr1[20] = { 0 }; char arr2[] = "abcdef"; char* arr3 = "fvgdf";//不能作为目标空间,因为要求目标空间可变,但是这是一个常量字符串,常量是不可修改的 char arr4[] = "123456"; strcpy(arr1, arr2); //源字符串arr2的''也会拷贝过去,所以要求源字符串必须有''。 strcpy(arr4, arr2); printf("%sn", arr1); printf("%sn", arr4); return 0; }
打印结果
- 源字符串必须以‘’结束,会将源字符串中的‘’拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
arr1与arr4作为目标空间都是可变的,arr3是一个指针,指向常量字符串“abcdef”,常量字符串不可变,所以arr3不能作为目标空间。
2.2 模拟实现strcpy函数
#include #include char* my_strcpy(char* dest, const char* src) { assert(src && dest);//这两个指针都不能为空 char* ret = dest; while (*src) { *dest = *src; dest++; src++; } *dest = *src;//将最后一个''也拷贝过去。 return ret; } int main() { char arr1[20] = { 0 }; char* arr2 = "hello bit"; my_strcpy(arr1, arr2); printf("%sn", arr1); printf("%sn", my_strcpy(arr1, arr2));//l链式访问,函数的返回值直接作为另一个函数(printf函数)的参数 return 0; }
打印结果
- strcpy函数返回的是目标空间的起始位置。
- 使用库函数对目标指针与源指针进行断言,避免出现传参过来的是空指针。
- 为什么要设置返回类型,而不是设置成void*?
有了返回值才能实现链式访问。- 什么是链式访问?
把一个函数的返回值作为另外一个函数的参数。如:
printf(“%sn”, my_strcpy(arr1, arr2));my_strcpy函数的返回值直接作为 printf函数的参数。给 printf函数一个字符串的起始位置就是一次向后进行打印,直到‘’。- 当指针访问到源字符串的‘’时,循环中止。为了将‘’拷贝过去,再跳出循环后,还要再进行拷贝一次。
还能对上述my_strcpy函数进行优化
#include char* my_strcpy(char* dest, const char* src) { assert(src && dest);//这两个指针都不能为空 char* ret = dest; while (*dest++ = *src++)//更为简便的写法,最后的''也能拷贝过去 { ; } return ret; //strcpy函数返回的是目标空间的起始位置。 }
- while (* dest++ = * src++)
每一次循环得到的结果是赋值后的结果,也就是 *dest,当指针访问到源字符串的 ‘’ 时,先将源字符串的 ‘’ 拷贝过去,此时循环条件 *dest== ‘’ ,循环中止。一次性将 ‘’ 拷贝到了目标字符串。
3、strcat函数
3.1 strcat函数功能介绍
strcat函数是字符串追加函数,在一个字符串的结尾 ‘’处开始进行追加其他字符串。
char* strcat(char* destination, const char* source);
strcat函数展示
#include #include int main() { char arr1[20] = "hello"; char arr2[] = " bit"; strcat(arr1, arr2);//从目标字符串的‘’处开始追加,源字符串的‘’也会追加过去 printf("%sn", arr1); return 0; }
打印结果
- 源字符串必须以‘’结束,目标字符串也得有‘’,从目标字符串的‘’处开始追加,目标字符串 ‘’被覆盖。
- 目标空间必须右足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改,即不能是常量字符串。
- 字符串不能自己给自己追加,即 strcat(arr2, arr2); ,会陷入死循环,为什么?
如上图所示,当源字符串开始往目标字符串的 ‘’ 开始一一拷贝完毕以后,源字符串要将自己的 ‘’ 也拷贝过去,用来结束拷贝。因为源字符串与目标字符串是同一块字符串,之前的 ‘’处已经被字符 ‘b’覆盖了。所以拷贝不会停止,进入死循环。
3.2 模拟实现strcat函数
#include char* my_strcat(char* dest, char* src) { char* ret = dest; assert(dest && src); while (*dest) { dest++; //找目标空间的‘’ } while (*dest++ = *src++)//从目标空间的‘’处开始拷贝 { ; } return ret; //strcat函数返回的是目标空间的起始位置。 }
- strcat函数本质上可以认为是一个拷贝函数,只不过要先找到目标函数的结尾 ‘’ 处,从目标函数的结尾 ‘’ 处开始拷贝。
- strcat 函数的返回值是目标函数的起始位置,所以返回类型是 char*。
4、strcmp函数
4.1 strcmp函数功能介绍
strcmp函数是字符串比较函数,比较的是两个字符串中对应字符的ASCII值。
int strcmp(const char* str1,const char* str2);
strcmp函数展示
#include #include int main() { char arr1[] = "abcdef"; char arr2[] = "abq"; int ret = strcmp(arr1, arr2);//返回值是大于0,小于0,等于0的整数 if (ret > 0) { printf(">0n"); } else if (ret == 0) { printf("==n"); } else { printf("<0n"); } return 0; }
打印结果
- 如何判断两个字符串的大小?
两个字符串的大小比较的是两个字符串中对应字符的ASCII值,如果相同,就比较下一对,直到不同或者都遇到 ‘’, ‘’也要参与比较。如代码中的两个字符串前两个字符对应相等,第三个字符‘c’的ASCII值小于字符‘q’,所以arr1中的字符串小于arr2中的字符串。- 第一个字符串大于第二个字符串,则返回大于0的数字。
- 第一个字符串等于第二个字符串,则返回0。
- 第一个字符串小于第二个字符串,则返回小于0的数字
4.2 模拟实现strcmp函数
#include int my_strcmp(const char* s1, const char* s2) { assert(s1 && s2); while (*s1 == *s2) { if (*s1 == '') { return 0; } s1++; s2++; } if (*s1 > *s2) { return 1; } else { return -1; } }
- 目标字符串与源字符串都不能改变,所以加上const修饰。
- 避免目标字符串的指针与源字符串的指针为空指针,所以对其断言。
- 在这里,将返回值设定成了 -1、0、1。
strcpy函数、strcat函数、strcmp函数都是长度不受限制的字符串函数。下面分别介绍strncpy函数、strncat函数、strcnmp函数,这些是长度受限制的字符串函数,即可以规定长度进行拷贝、追加、比较。
5、strncpy函数
5.1strncpy函数功能介绍
类比strcpy函数,不过多了一个参数----->要拷贝几个字符的个数。
char* strncpy(char* destination,const char* source,size_t num);
strncpy函数展示
int main() { char arr1[] = "abcdef"; char arr2[] = "qwertyuiop"; strncpy(arr1, arr2, 6);//可以指定拷贝的字符个数 printf("%sn", arr1); return 0; }
打印结果
- 拷贝了6个字符从源字符串到目标空间
如果源字符串的长度小于num个,则拷贝完源字符串之后,在目标的后边追加0,直到num个。如下所示
int main() { char arr1[] = "abcdef"; char arr2[] = "qwe"; strncpy(arr1, arr2, 6);//要拷贝的字符个数大于字符串中字符真正的个数,后面自动填充‘’,直到6个。 printf("%sn", arr1); return 0; }
- 前面三个 ‘’,是为了满足六个字符,最后一个 '’是自动添加的,用来结尾。
6、strncat函数
6.1strncat函数功能介绍
类比strncat函数,不过多了一个参数----->要追加几个字符的个数。
char* strncat(char* destination,const char* source,size_t num);
strncat函数展示
#include #include int main() { char arr1[20] = "abcdef"; char arr2[] = "qwertyuiop"; strncat(arr1, arr2, 5);//追加5个字符过去,同时‘’也会追加过去。当追加的个数大于源字符串的字符个数时,只会补加一个‘’。 printf("%sn", arr1); return 0; }
打印结果
- 追加5个字符过去,同时‘’也会追加过去。
- 当追加的个数num,大于源字符串的字符个数时,不会追加num个 ‘’ ,只会补加一个 ‘’ 用以结尾。
7、strncmp函数
7.1 strncmp函数功能介绍
类比strcmp函数,不过多了一个参数----->要比较几个字符的个数。
int strncmp(const char* str1,const char* str2,size_t num);
strncmp函数展示
#include #include int main() { char arr1[] = "abcdef"; char arr2[] = "abcdq"; char arr3[] = "qwertyuiop"; int ret = strncmp(arr1, arr2, 4); int rets = strncmp(arr1, arr3, 4); printf("%dn", ret); printf("%dn", rets); return 0; }
打印结果
- arr1与arr2比较前4个字符,相等。
- arr1与arr3比较前4个字符,arr1储存的字符小于arr3储存的字符。
下面介绍字符串查找函数
8、strstr函数
8.1 strstr函数功能介绍
strstr函数是子串查找函数,看str2是不是str1的子串,是的话,返回的是str2在str1中第一次出现的地址,如果不是,返回空指针。
char* strstr(const char* str1 ,const char* str2);
strstr函数展示
#include #include int main() { char arr1[] = "abcdefabcdef"; char arr2[] = "cdef"; char* ret = strstr(arr1, arr2);//找不到,返回空指针。找到了,返回的是子串第一次出现的地址。 if (NULL == ret) { printf("找不到子串"); } else { printf("%sn",ret); } return 0; }
打印结果
- cdef是子串,返回第一次出现在arr1中的地址,即第一个c的地址,printf函数根据返回的地址,依次向后打印字符串。
- 如果不是子串,返回空指针。
8.2 模拟实现strstr函数
#include char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* s1 = str1; const char* s2 = str2; const char* cur = str1; while (*cur) { s1 = cur; s2 = str2; while (*s1 && s2 && (*s1 == *s2)) { s1++; s2++; } if (*s2 == '') { return (char*)cur; //找到了 } cur++; } return NULL; //找不到 }
- 将str1与str2分别赋给s1与s2,利用s1与s2往字符串后面遍历查找相同的部分。
- cur 是用来记录str1与str2字符串第一次出现相等字符时的位置,等找到子字符串后,用来作为返回值。
- 如图所示,第一次进入,在两个字符串的起始位置,字符不相等,所以cur不会在这记录,cur++;
- 第二个while循环是用来往后找字符串相同的部位的。第二个while循环的循环条件首先要保证 *s1与 *s2不为空字符,为空字符的话,说明s1或者s2已经找到字符串结尾了,循环中止。*s1与 *s2指向的字符不相等,循环也要中止。
- 如果第二个while循环跳出来后,判断一下 *s2是否为空字符,为空字符的话,说明s2已经遍历一遍,s2中所有的字符都有在s1中被找到。此时s2中储存的字符串是s1的子字符串。
- 第二个while循环跳出来后,并且又判断没有找到,将cur记录的位置进行修正,往后走一步,cur++;,此时第一个while进行第二次循环,将cur的位置赋值给s1,s1被修正(s1=cur++,说明s1此时的位置是上次开始记录位置的后一个,从这里开始重新与s2进行比对),再将s2的起始位置修正回来,重新进行比对。
- 如果 cur 都往后遍历到空字符了,还是没有找到,说明s2不是s1的子字符串,返回空指针NULL。
- 函数参数的设定都用const修饰了,所以 *s1与 *s2与 *cur都要用const修饰,使得 = 左右两边变量的权限是保持一致的,不然 = 右边的变量收到const修饰,= 左边的变量不受到const修饰。
- 返回类型是char* ,返回值是记录位置,cur之前被const所修饰了,此时要改回来,强制类型转换----->char*。
9、strtok函数
9.1 strtok函数功能介绍
char* strtok(char* str,const char* sep);
- sep参数是个字符串,定义了用作分隔符的字符集合
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用 ‘’ 结尾,返回一个指向这个标记的指针。
- strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容,并且可修改。
- strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回NULL指针。
strtok函数展示
#include #include int main() { char arr[] = "zhengguobin@163.com"; char buf[30] = { 0 }; strcpy(buf, arr); const char* sep = "@."; printf("%sn",strtok(buf, sep));//只找第一个标记 printf("%sn", strtok(NULL, sep));//是从保存好的开始继续往后找 printf("%sn", strtok(NULL, sep)); return 0; }
打印结果
- 由打印结果可见,字符串根据分割标记被分割了。
- 如果再添加一个printf(“%sn”, strtok(NULL, sep));,则会打印(NULL)。因为返回值是 NULL。
上面的打印过程可以优化,不用写三个打印函数。如下
#include #include int main() { char arr[] = "zhengguobin@163.com"; char buf[30] = { 0 }; strcpy(buf, arr); const char* sep = "@."; char* str = NULL; for (str =