C字符串库函数(一)【自用】
文章目录
- 1. strlen()
- 2. strcpy()
-
- 源字符串必须以`'\0'`结束
- 会将源字符串中的`'\0'`拷贝到目标空间
- 目标空间必须足够大
- 目标空间必须可变
- 模拟strcpy()函数
-
- 🍎 常规方法
- 🍎 改进
- 3. strcat()
-
- strcat()从目标字符串`'\0'`处开始追加,所以源字符串里必须有`'\0'`
- 模拟strcat()函数
- 4. strcmp()
-
- strcmp函数的比较原理
- strcmp的返回值为>0,<0或0,具体的值视编译器而定
- 模拟strcmp函数
- 字符串直接用>. <比较,比较的是什么?
- 5. strstr()
-
- 模拟strstr函数
1. strlen()
size_t strlen ( const char* str );
- 函数返回的是字符串不含
\0
的字符个数- 参数指向的字符串必须要以
\0
结束- 函数的返回类型是size_t,无符号整型,不是int
strlen()返回不带’\0’的字符个数
char str[] = "abcde";int num = strlen(str);
num的值是5。
其实str数组的组成是这样的。它有6个元素,最后一个元素'\0'
我们看不到,而strlen也不会计算这个'\0'
参数指向的字符串必须以’\0’结束
不含'\0'
会出错的
函数的返回类型是size_t,不是int
其中第三点是一个易错点。strlen返回的起始不是int
型,而是无符号整型unsigned long
,
在我的编译器中可以看到,这段程序可以运行起来,但是有报错,不能用%d
,而是%lu
平常的应用中可能无伤大雅,但是我们来看这个例子
int main(){ if(strlen("abc") - strlen("abcdef") > 0) { printf(">\n"); } else { printf("<=\n"); } return 0;}
"abc"是3个字符,"abcdef"是6个字符,3-6 = -3,应该输出<=
然而实际上呢
)
因为strlen(“abc”)返回的是 无符号的3
strlen(“abcedf”)返回的是 无符号的6
最后的结果-3如果按照无符号数来算将是一个很大的数,else语句永远不会被执行
这里我们就要改进一下,进行强制类型转化才能得到想要的
模拟strlen()函数
🍎 非递归方法
size_t my_strlen(const char* str){ int count = 0; while(*str != '\0') { count++; str++; } return count;}
🍎 递归方法
size_t my_strlen(const char* str){ while(*str != '\0') { return my_strlen(str + 1) + 1; } return 0;}
🍎 指针-指针
size_t my_strlen(const char* str){ char* start = str; while(*str != '\0') { str++; } return (str-start)/sizeof(char);}
2. strcpy()
char* strcpy( char* destination , const char* source );
- 将source指向空间中包括’\0’在内的字符串拷贝到destination所指空间中
- 源字符串必须以
'\0'
结束- 会将源字符串中的
'\0'
拷贝到目标空间- 目标空间必须足够大,以确保能存放源字符串
- 目标空间必须可变
- 返回目标空间的起始地址
源字符串必须以'\0'
结束
int main(){ char arr1[20] = {0}; char arr2[] = {'a','b','c'};//应改为{'a','b','c','\0'} strcpy(arr1,arr2); return 0;}
arr2没有'\0'
,这段代码会让程序崩溃
会将源字符串中的'\0'
拷贝到目标空间
int main(){ char arr1[20] = "XXXXXXXX"; char arr2[] = "abcde"; strcpy(arr1,arr2); printf("%s\n",arr1);}
比如这段代码,最开始arr1和arr2数组是这样的
而strcpy语句执行后
可以看到’\0’也被拷贝进了arr1里
目标空间必须足够大
arr1不够大,报错力~。因为arr2有4个元素,arr1[2]是肯定不够的
在有的编译器中(比如vs),目标空间如果没有源字符串空间大也是能强行塞进去的,不过程序仍然是崩溃的,因为arr1的栈空间被破坏了,所以作为程序员,我们必须让目标空间足够大!
目标空间必须可变
int main(){ char* arr1 = "Atsuki"; char arr2[] = {'a','b','c','\0'}; strcpy(arr1,arr2); printf("%s\n", arr1); return 0;}
这样一段代码程序也是崩溃的,因为arr1是字符指针,字符指针指向的是常量字符串,不可变~
模拟strcpy()函数
🍎 常规方法
//返回类型是char*,因为函数返回值是目标空间起始地址char* my_strcpy(char* dest, const char* src){ char* ret = dest;//保存目标空间的起始地址作返回值,因为后面dest就变了 while(*src != '\0') { *dest = *src; dest++; src++; } *dest = *src;//当*src为'\0'时,循环已经结束,'\0'还未拷贝过去,因此有了这一句 return ret;}
🍎 改进
char* my_strcpy(char* dest,const char* src){ char* ret = dest; while(*dest++ = *src++) { ; } return ret;}//这段代码妙在循环条件,改进前'\0'是在循环外赋值的,这个方法直接达到了循环完成所有拷贝//赋值表达式的值是右侧表达式的值//当*src == '\0'时,*dest = *src这个表达式的值为0,循环结束
3. strcat()
char* strcat ( char* destination, const char* source) ;
- 在目标字符串后追加源字符串的拷贝。目标字符串的
'\0'
被源字符串的第一个字符覆盖,形成的新字符串的末尾包括'\0'
。- 源字符串必须以
'\0'
结束- 目标空间必须足够大,能容纳下源字符串的内容
- 目标空间必须可修改
strcat()从目标字符串'\0'
处开始追加,所以源字符串里必须有'\0'
最后arr1里A覆盖了原本的'\0'
,但是这个数组放不下拼接后的数组,所以目标空间一定要主动设定到足够大
如果我们让字符串里本来就有'\0'
呢?
源字符串的拷贝从第一个\0
就开始插入了。
模拟strcat()函数
char* my_strcat(char* dest, char* src){ assert(dest && src); char* ret = dest; //找目标空间的'\0' while(*dest ) { dest ++; } //拷贝 while((*dest++ = *src++)) { ; } return ret;}
4. strcmp()
int strcmp( const char* str1, const char* str2);
- 第一个字符串大于第二个字符串,则返回大于0的数字
- 第一个字符串等于第二个字符串,则返回0
- 第一个字符串小于第二个字符串,则返回小于0的数字
strcmp函数的比较原理
strcmp函数比较的不是字符串长度,而是比较字符串对应位置上的字符的大小(ascii码值),如果相同,就比较下一对,直到不同或者都遇到\0
ret1,一直比较到arr1的a
与arr2的'\0'
比较,最终得出arr1大,正数
ret2,比较到arr2的s
和arr3的a
,最终得出arr2大,正数
ret3,和ret2一样, 但是t
比s
小,所以得出一个负数
strcmp的返回值为>0,<0或0,具体的值视编译器而定
我用的sublime返回的就是两个字符ascii码相减的值
而如果是vs就会返回1,-1
模拟strcmp函数
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;}
字符串直接用>. <比较,比较的是什么?
是地址
char arr1[] = "abcde";char arr2[] = "abc";if(arr1 < arr2){ //数组名是数组首元素地址,所以这段代码比较的是两个数组的地址}if("abc" < "abcde"){ //字符串也有自己的地址,是字符串首字母的地址}
5. strstr()
char* strstr( const char* str1, const char* str2 );
- 判断str2是不是str1的子串
- 返回str2第一次出现在str1中的首字母地址,如果str2不是str1的子串返回空指针
char str1[] = "abcdefgcdeg";char str2[] = "cde";char* ret = strstr(str1,str2);
比如这段代码,str1中有两个"cde"
,strstr返回的就是第一次出现的cde的c的地址
看这个结果。因为ret是第一个cde的首字母地址,所以%s顺着这个地址把它后面的字符都打印出来了。
模拟strstr函数
char* my_strstr(char* str1,char* str2){ assert(str1 && str2); char* s1 = str1; char* s2 = str2; char* cur = str1; while(*cur) { s1 = cur; s2 = str2; while(*s1 && *s2 && (*s1 == *s2)) { s1++; s2++; } if(*s2 == '\0') { return cur; } cur++; } return NULL;}