0. 前言
Hello,大家好久不见,这段时间由于军训的原因,导致一直没有更新,这次专门写了一篇文章,对字符串函数和内存函数做了一个归纳,写作时间比较赶,内容可能有些粗糙,如有错误,还请指正!接下来我们就进入正题,一起深剖这些函数吧!
1. 字符串操作函数
1.1 长度不受限制的字符串函数
1.1.1 strlen
size_t strlen ( const char * str);
函数细节
- 字符串以’\0’ 作为结束标志,strlen函数返回的是在字符串中’\0’ 前面出现的字符个数(不包含’\0’ )。
- 参数指向的字符串必须要以’\0’ 结束。
- 注意函数的返回值为size_t,是无符号的( 易错)。
使用方法
#include<stdio.h> #include<string.h> int main() { char ch = 'a';//c语言有字符类型 "abcdef";//没有字符串类型 char arr1[] = "abcdef"; printf("%zu\n", strlen(arr1));//统计\0前的字符个数 char arr2[10] = {'a','b','c','d','e','f'};//6 //不完全初始化,其余元素默认初始化为0,为\0的ascii码值 printf("%zu\n", strlen(arr2)); char arr3[] = { 'a','b','c','d','e','f' };//随机值 //没有\0 printf("%zu\n", strlen(arr3)); //可能遇到的bug //strlen的返回类型为size_t if (strlen("abc") - strlen("abcdef") > 0)//3 - 6 = -3转换为无符号是一个极大的数字 { printf(">\n");//ok } else { printf("<\n"); } //解决方案:强转或直接比大小 //if ((int)strlen("abc") - (int)strlen("abcdef") > 0) //if (strlen("abc") > strlen("abcdef")) }
运行结果:
模拟实现
计数器:
int my_strlen(const char* str) { assert(str); int count = 0; while (*str) { count++; str++; } return count; }
递归:
int my_strlen(const char* str) { if (*str) return 1 + my_strlen(str + 1); else return 0; }
指针 - 指针:
int my_strlen(const char* str) { char* ret = str; while (*str) { str++; } return str - ret; }
运行结果:
1.1.2 strcpy
char *strcpy( char *strDestination, const char *strSource );
字符串拷贝
函数细节
- 源字符串必须以’\0’ 结束。
- 会将源字符串中的’\0’ 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串,否则程序会奔溃。
- 目标空间必须可变,若目标空间为常量字符串,程序也会奔溃。
使用方法
int main() { char arr1[20] = { 0 }; //char* arr1 = "hello world";//奔溃 //arr1指向的是常量字符串,常量是不可修改的 char arr2[] = "abcdef"; //char arr2[] = { 'a','b','c' };//崩溃 //若没有\0就会一直往后找,程序会崩溃 strcpy(arr1, arr2); //会把\0也拷贝过去,所以字符串源字符串一定要有\0 printf("%s\n", arr1); return 0; //若目标空间无法存放源字符串,程序就会奔溃 }
模拟实现
char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest++ = *src++); return ret; } int main() { char arr1[20] = { 0 }; char arr2[] = "abcdef"; char* ret = my_strcpy(arr1, arr2); printf("%s\n", ret); return 0; }
运行结果:
1.1.3 strcat
char *strcat( char *strDestination, const char *strSource );
字符串追加
函数细节
源字符串必须以’\0’ 结束,否则会往后一直找\0,在\0的位置追加,可能会导致追加空间不足的情况,若找不到\0则会一直向后访问,可能会有结果,但是已经造成了越界访问,很危险。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改。
源字符串的\0会被一起拷贝到目标字符串中。
使用方法
int main() { char arr1[20] = "hello\0XXXXXX";//调试一下 //会把源字符串的\0一起拷贝到arr1中 //并且目标字符串需要有\0,arr2会追加到arr1的后面 //若没有\0 char arr2[] = { 'h','e','1','1','0' };//追加就放不下了 char arr3[] = "anduin"; //强制运行可能会有结果 strcat(arr1, arr3); strcat(arr2, arr3); printf("%s\n", arr1); printf("%s\n", arr2); //找不到\0会往后一直找\0,然后再\0的位置追加 //其实这里已经造成了越界访问,很危险 return 0; }
运行结果:
分析:
函数细节我们提到strcat会在目标字符串的\0处开始拷贝,并且会把\0一起拷贝到目标字符串,那么真的是这样吗?
根据调试结果,发现我们总结的完全到位!
模拟实现
图:
char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest;//拷贝一份地址 //找目标空间的\0 while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " bit"; char* ret = my_strcat(arr1, arr2); printf("%s", ret); return 0; return 0; }
运行结果:
问题
那strcat能否给自己追加呢?
让我们试试:
char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest;//拷贝一份地址 //找目标空间的\0 while (*dest) { dest++; } //拷贝 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char* ret = my_strcat(arr1, arr1); printf("%s", ret); return 0; }
运行结果:
分析:
我们发现程序崩了,单凭这一点,就说明字符串无法给自己追加!
字符串时完全不可以给自己追加的,目标字符串在找到\0之后,会使用源字符串在\0处对内容进行追加,源字符串结束的标准是找到\0,但是源字符串和目标字符串相同,原先\0的位置会被覆盖成目标字符串的第一个字符,源字符串和目标字符串的地址相同,那么改变目标字符串的地址也改变了源字符串的地址,这样源字符串就失去了\0,程序就永远不会停止,造成程序奔溃!