0.说在前面的话:
- 字符串函数的基本使用要包含头文件:#include<string.h>
- 字符串以’\0’作为结束标志,
- 字符串函数出现的size_t就是unsigned int无符号整型
- 下面出现的assert是断言,要包含头文件**#include<assert.h>**
- 下面的介绍我将从🚗重要知识🚗函数原型🚗基本使用🚗模拟实现 四个方面来一一介绍
1.求字符串长度
1-1strlen求串长
- 全称:string length
- strlen函数返回的是字符串函数’\0’前面出现的字符个数,也就是可见长度,或者是有效长度
- 函数的返回值是size_t是无符号的(易错)
函数原型:size_t strlen(const char* str)
基本使用:
int main() { char arr1[] = "hello world";//[hello world\0] //char arr2[]={'h',\0','t'};//必须带有'\0',否则长度未知('\0'出现的位置随机) int len = strlen(arr1); printf("%d\n", len); return 0; }
易错知识:size_t(坑坑坑):–错误代码示例
int main() { char* p1 = "abc"; char* p2 = "abcdef"; if (strlen(p1) - strlen(p2) > 0)//关键点 { printf("haha\n"); } else { printf("hehe\n"); } return 0; }
我就不买关子;答案:haha
原因:(算术转换)
两个无符号整型相减得到-3,但是-3在内存读取的时候是以无符号数来看待,所以"符号位"的1其实把他变成了一个很大的数.(两个无符号整数相减还是一个无符号整型)
解决办法:
1.写成if( strlen(p1)>strlen(p2) )的形式
2.强制转换为if( (int)strlen(p1)-(int)strlen(p2) )的形式
模拟实现:
此函数我有专门讲过,欲知速戳三种方法模拟实现strlen函数
2.长度不受限的字符串函数
2-1strcpy拷贝
- 源字符串必须以’\0’结束
- 会将源字符串中的’\0’拷贝到目标空间中
目标空间必须足够大,以确保能存放源字符串
目标空间必须可变
函数原型:char* strcpy(char* dest,const char* src)
dest, 全称:destination,-指向用于存储复制内容的目标数组
src,全称:source, -指向要复制的字符串
dest,src的左右位置,也正符合左值空间,右值内容
基本使用 :
int main() { char arr1[30] ="XXXXXXXXX";//1.arr[]是通过直接将字符串放到arr[]这块内存中 2.不能省30且目标空间足以容纳源空间hello char* str = "hello";//字符指针str通过指向的是hello这块常量字符串 //char arr2[]={'h','e','l','\0','o'};正确 //char arr2[]={'h','e','l','l','o'};错误(没有遇到'\0',越界访问,停不下来) printf("%s\n", strcpy(arr, str));//1.返回char*类型的dest空间的起始地址 2.链式访问 }
运行结果:
简单图解:
模拟实现:
优化一下:
char* my_strcpy(char* dest, const char* src) { assert(dest&&src); char* ret = dest;//ret保存dest的初始位置(后面dest会移动) while (*dest++=*src++);//1.赋值 2.++ return ret; }
2-2strcat追加
- 源字符串必须以’\0’结束
- 会将源字符串中的’\0’拷贝到目标空间中(追加完的字符串的’\0’是源字符串的)
- 目标空间必须足够大,以确保能存放源字符串
- 目标空间必须可变(也就是目标空间必须是字符数组,不能是指针指向的常量字符串)
函数原型:char* strcat(char* dest,const char* src)
基本使用:
int main() { char arr1[30] = "hello "; char arr2[] = "world"; printf("%s\n", strcat(arr1, arr2)); return 0; }
运行结果:
简单图解:
易错知识:(不能给自己追加)–错误代码示例
printf("%s\n",strcat(arr1,arr2);//自己给自己追加
原因:在找到dest的‘\0’后,进行src的的一个字符的拷贝时将dest(其实也是src)的’\0’覆盖掉,追加将无法停下来
模拟实现
char* my_strcat(char* dest, const char* src)//1.能否被修改决定了是否加const 2.const修饰更安全 { assert(dest&&src); char* ret = dest; while (*dest)//1.找目的地空间的'\0' 2.*dest是*dest!='\0'的简化版 { dest++; } while (*dest++ = *src++);//将src的内容拷贝得到dest中 return ret; }
关于我的一点小理解:模拟strcat=模拟strlen+模拟strcpy
这样理解灵感:模拟strcat的两个步骤
2-3strcmp比较
- 对应比较字符的ASCII值(小写l比大写L的ASCII大)
- 若arr1>arr2,返回正数;
- 若arr1==arr2,返回0;
- 若arr1<arr2,返回负数;
函数原型:int strcmp(const char* str1,const char* str2)
基本使用:
int main() { char arr1[10] = "hello"; char arr2[10] = "heLLO"; int ret = strcmp(arr1, arr2); if (ret > 0)//VS才固定返回1,0,-1,为增加代码可移植性(通用性),不建议采用1,0,-1的方式 { printf("arr1>arr2\n"); } else if (ret == 0) { printf("arr1==arr2\n"); } else { printf("arr1<arr2\n"); } return 0; }
运行结果:
模拟实现:
//版本1: int my_strcmp(const char* str1, const char* str2) { while (*str1 && *str1++ == *str2++);//**在遇到\0之前都相等就一直比下去** return *--str1 - *--str2; } //版本2: int my_strcmp(const char* str1, const char* str2) { assert(str1&&str2); int ret = 0; while (!(ret = (*str1++) - (*str2++)) && (*str1));//妙不可言 return ret; }