0.前言
- C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串 中或者 字符数组 中。
- 字符串常量 适用于那些对它不做修改的字符串函数。
在本篇文章里,我见给大家介绍字符串函数以及如何去模拟实现字符串函数。首先,我们先学习一下strlen函数。
1.求字符串长度函数strlen
size_t strlen ( const char * str );
关于strlen函数,我们需要知道一下几点:
字符串以'\0' 为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包
含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的。
学会模拟实现strlen函数。
这几点对我们学习strlen这个库函数是相当重要的,现在来依次介绍以上几点。
上面的代码很好地将字符数组中的字符个数求了出来,那接下来我们看一下下面的代码。
可以看到,这两段代码其实是差不多的,但是输出结果却出现了这么大的差别。为什么呢?原因就是strlen函数返回的是在字符串中 '\0' 前面出现的字符个数,而上面代码中的数组arr的'\0'不知道在哪里出现。所以输出结果就是随机值。
现在我们来学习第三点,这点非常的重要,我将通过下面的代码给大家讲解。
#include <stdio.h> #include <string.h> int main() { if (strlen("abc") - strlen("abcdef") > 0) printf(">\n"); else printf("<\n"); return 0; }
这段代码的输出结果会是什么呢?是:>
为什么呢?
因为strlen()返回的是无符号整数,虽然3-6小于零,但是-3被解读成一个无符号数的话,将会是一个非常大的正数,所以输出结果为> 。
模拟实现strlen函数
①指数器方法
定义一个变量来充当计算器,统计字符的个数。
#include <stdio.h> #include <string.h> #include <assert.h> int my_strlen(const char* str) { assert(str != NULL);//断言str不等于空指针 int count = 0;//计数器 while (*str != '\0') { count++; str++; } return count; } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("len = %d\n", len); return 0; }
②递归
#include <stdio.h> #include <string.h> #include <assert.h> int my_strlen(const char* str) { assert(str != NULL);//断言str不等于空指针 if (*str != '\0') return 1 + my_strlen(str + 1); else return 0; } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("len = %d\n", len); return 0; }
③指针-指针版本
#include <stdio.h> #include <string.h> #include <assert.h> int my_strlen(const char* str) { assert(str != NULL);//断言str不等于空指针 char* begin = str; while (*str != '\0') { str++; } return str - begin; } int main() { char arr[] = "abcdef"; int len = my_strlen(arr); printf("len = %d\n", len); return 0; }
以上就是模拟实现strlen函数的三种方法了。模拟实现strlen函数时,我们也要注意几个点。第一,就是要对传过来的指针进行断言,确保它不是空指针;第二,就是用const修饰传过来的指针,因为我们不需要修改它的内容。
现在我们可以使用我们自己定义的my_strlen函数来比较两个字符串的长度了,因为该函数的返回类型是int(有符号整型)。
2.长度不受限制的字符串函数
2.1strcpy
char * strcat ( char * destination, const char * source );
对于strcpy函数,我们需要注意一下几点:
- 该字符串是字符串的拷贝。
- 源字符串必须以 '\0' 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 学会模拟实现strcpy函数
strcpy的正确用法
#include <stdio.h> #include <string.h> int main() { char arr[20] = { 0 }; strcpy(arr, "hello world"); printf("%s", arr); return 0; }
观察strcpy是否会将源字符串的 '\0' 拷贝到目标空间
#include <stdio.h> #include <assert.h> #include <string.h> int main() { char arr[20] = "#################"; char* p = "hello world"; strcpy(arr, p); printf('%s\n', arr); return 0; }
对比上面的两个图片,就可以发现strcpy函数会将源字符串的 '\0' 拷贝到目标空间。
strcpy的错误示范
①原字符串没有以'\0'结尾
解决方案:写程序的是要注意要给字符数组加上 '\0' ,如果没有 '\0' 的话,strcpy就会找 arr2 后面的位置有没有 '\0' ,然后造成越界访问。
②目标空间不能容纳原字符串的内容
解决方案:确保目标空间足够大,能够容纳源字符串的内容。
③ 目标空间不可修改
解决方案:目标空间不能是常量字符串,必须是字符数组。
模拟实现strcpy函数
#include <stdio.h> #include <assert.h> #include <string.h> char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest++ = *src++) { ; } return ret; } int main() { char arr[20] = "#################"; const char* p = "hello world"; printf("%s\n", my_strcpy(arr, p)); return 0; }
为什么 my_strcpy 函数的返回值不设置为 void,而设置为char*。为什么呢?因为 my_strcpy 函数的返回值为 char* ,能够实现函数的链式访问(把一个函数的返回值作为另外一个函数的参数)。
2.2strcat
char * strcat ( char * destination, constchar * source );
对于strcat函数,我们需要注意一下几点:
- 字符串必须以 '\0' 结束。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 目标中的'\0'被源字符串的第一个字符覆盖。
- 学会模拟实现strcat函数。
代码示例
#include <stdio.h> #include <string.h> int main() { char arr[20] = "hello"; strcat(arr, " world");//字符串追加(连接) printf("%s\n", arr); return 0; }
模拟实现strcat函数
#include <stdio.h> #include <string.h> #include <assert.h> char* my_strcat(char* dest, const char* source) { char* ret = dest; assert(dest && source); //1.找到目标字符串中的'\0' while (*dest) { dest++; } //2.将源字符串追加过去 while (*dest++ = *source++)//赋值 { ; } return ret;//返回目标空间的起始地址 } int main() { char arr1[20] = "hello "; char arr2[] = "world"; //my_strcat(arr1, arr2); printf("%s\n", my_strcat(arr1, arr2)); return 0; }
注意:my_strcat函数不能实现自己追加自己,如果这样做的话,会导致数组越界访问,最后程序崩掉。
#include <stdio.h> #include <string.h> #include <assert.h> char* my_strcat(char* dest, const char* src) { char* cur = dest; while (*cur) { cur++; } while (*cur++ = *src++) { ; } return dest; } int main() { char arr[20] = "Joy"; my_strcat(arr, arr); printf("%s\n", arr); return 0; }
所以,使用 my_strcat 和 strcat 函数,一定不能自己追加自己,同时需要注意源字符串要包含 '\0',否则也会出现BUG。还有一个小细节就是,my_strcat 函数和 strcat函数也会将原字符串的 '\0' 拷贝到目标空间中去。
2.3strcmp
intstrcmp (constchar * str1, constchar * str2 );
关于strcmp函数,我们需要注意一下几点:
- 第一个字符串大于第二个字符串,则返回大于0的数字。
- 第一个字符串等于第二个字符串,则返回0。
- 第一个字符串小于第二个字符串,则返回小于0的数字。
- 学会模拟实现strcmp函数。
代码示例
#include <string.h> #include <stdio.h> int main() { char* p = "bbc"; char* q = "abcdef"; if (strcmp(p, q) > 0) printf(">\n"); else if (strcmp(p, q) == 0) printf("=\n"); else printf("<\n"); return 0; }
模拟实现strcmp函数
#include <stdio.h> #include <assert.h> int my_strcmp(const char* s1, const char* s2) { assert(s1 && s2); while (*s1 == *s2) { //字符串s1和字符串s2相等 if (*s1 == '\0') { return 0; } s1++; s2++; } if (*s1 > *s2) { return 1; } else { return -1; } } int main() { char* p = "abcd"; char* q = "abc"; int ret = my_strcmp(p, q); if (ret == 0) { printf("p == q\n"); } else if (ret > 0) { printf("p>q\n"); } else printf("p<q\n"); return 0; }
#include <stdio.h> #include <assert.h> int my_strcmp(const char* s1, const char* s2) { assert(s1 && s2); while (*s1 == *s2) { if (*s1 == '\0') { return 0; } s1++; s2++; } return *s1 - *s2; } int main() { char* p = "abcd"; char* q = "abc"; int ret = my_strcmp(p, q); if (ret == 0) { printf("p == q\n"); } else if (ret > 0) { printf("p>q\n"); } else printf("p<q\n"); return 0; }
注意:使用 my_strcmp 函数 和 strcmp函数时,两个字符串一定要包含 '\0',否则将会造成数组越界,出现意想不到的结果。
#include <stdio.h> #include <assert.h> #include <string.h> int my_strcmp(const char* s1, const char* s2) { assert(s1 && s2); while (*s1 == *s2) { if (*s1 == '\0') { return 0; } s1++; s2++; } return *s1 - *s2; } int main() { char arr1[] = { 'a','b','c' }; char arr2[] = { 'a','b','c' }; int ret = my_strcmp(arr1, arr2); if (ret == 0) { printf("arr1 == arr2\n"); } else if (ret > 0) { printf("arr1>arr2\n"); } else printf("arr1<arr2\n"); return 0; }
以上就是这篇博客的全部内容了,如果大家觉得有收获的话,可以点个赞支持一下哦!