大家好,我总结了字符串函数的模拟实现,函数原理,注意事项,细节讲解,还有有意思的的提问和回答。
目录
strlen
1,字符串长度函数,函数原型size_t strlen(const char* str);
2,模拟实现(三种方法)
#include<string.h>
#include<stdio.h>
#include<assert.h>
size_t my_strlen1(const char* str)
{
//模拟实现,计数器方法
assert(str);
int num = 0;
while (*str != '\0')
{
num++;
str++;
}
return num;
}
size_t my_strlen2(const char* str)
{
//递归方法
assert(str);
if (*str != '\0')
{
return 1 + my_strlen2(++str);
}
else
{
return 0;
}
}
size_t my_strlen3(const char * str)
{
//指针减指针=两个指针之间的个数
assert(str);
char* p = str;
while (*str != '\0')
{
str++;
}
return str-p;//大地址减小地址是正数
}
int main()
{
char arr[5] = { "abcd" };
printf("%d\n", strlen(arr));//找到\0,统计\0之前的个数
//三种方法模拟实现
printf("%d\n", my_strlen1(arr));//计数器
printf("%d\n", my_strlen2(arr));//递归
printf("%d\n", my_strlen3(arr));//指针减指针
return 0;
}
3,细节返回值size_t的理解
size_t=unsigned int无符号整数
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
//strlen("abc") - strlen("abcdef")=4294967293,因为-3在内存中的补码被认为无符号整型,一个非常大的数字
//(strlen("abcdef") - strlen("abc") =3
//(int)strlen("abc") - (int)strlen("abcdef")=-3,//因为size_t-size_t=size_t,int-int=int,前后类型不变
长度不受限制的字符串函数
strcpy
1,字符串拷贝函数,函数原型char* strcpy(char* dest, const char* str);
2,模拟实现
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;//保护原始数据返回值
while (*dest++ = *src++)//不是\0就赋值拷贝,\0也会被拷贝
{
;
}
return ret;
}
int main()
{
char arr1[] = { "xxxxx" };
char arr2[] = { "abc"};
//printf("%s\n", strcpy(arr1, arr2));
//模拟实现
printf("%s\n",my_strcpy(arr1, arr2));
return 0;
}
同学问:“while (*dest++ = *src++)
{
;
}我感觉这里不能把\0拷贝过去,0的话,while循环为假,还会赋值吗?“老师解答:“while循环为假,是不会再进行下一次循环判断,那块是一个赋值语句,还会执行的,赋值表达式是一定会被执行一下的。”
同学说:”行,我记住了。“
3,使用函数注意
- 拷贝目标空间足够大,即初始化数组要指定大小,要char arr[20]={"xxx"},不要char arr[] = {"a"};因为默认空间大小只有a和\0两个空间
- 拷贝源字符串必须有\0,只有遇到\0才会停止拷贝,且\0也会拷贝过去,不要char arr[]={'a','b','c'}这样没有\0
- 目标空间可以被修改,不能是常量字符串,不要char* p = "abcdef";(如下图会报错)
strcat
1,字符串追加函数,函数原型char* strcat(char* dest, const char* src)
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[20] = { "abc" };
char arr2[] = { "def" };
//printf("%s\n", strcat(arr1, arr2));//找到目标函数的\0,拷贝覆盖\0,和strcpy差不多
printf("%s\n", my_strcat(arr1, arr2));
return 0;
}
问:能不能自己追加自己?
答:现在的my_strcat不可以,导致\0被覆盖,永远遇不到\0了,死循环,而库函数里面的strcat可以自己追加自己
strcmp
1,字符串逐个字符的ASCII对比函数,函数原型int strcmp(const char* str1,const char* str2);
2,返回值:前者大于后者,返回一个大于0的数字;前者小于后者,返回一个小于0的数字,两者相等,返回0。在VS2022环境返回值是0,1,-1
3,模拟实现
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;//自加一定要写在里面,防止找到字符串末尾,不要写在上面,找到正确的字符,不要多加,两个字符不一样就及时跳出来,不要再加一次,不是+-1写反了,而是指针跳到了\0二者相等了
str2++;
}
if (*str1 < *str2)
{
return -1;
}
else
{
return 1;
}
//return str1-str2;//这个返回值大于0,小于0,也满足函数原型
}
int main()
{
char arr1[] = { "abc" };
char arr2[] = { "abe" };
printf("%d\n", strcmp(arr1, arr2));
printf("%d\n", my_strcmp(arr1, arr2));
return 0;
}
长度受限制的字符串函数
strncpy
int main()
{
char arr1[20] = {"abc"};
char arr2[20] = {"defg"};
printf("%s\n",strncpy(arr1, arr2, 6));
return 0;
}
strncat
strncmp
其他函数
strstr
字符串查找子字符串函数
char* my_strstr(const char* str1, const char* str2)
{
//处理特殊情况
assert(str1 && str2);
if (str2 == NULL)
{
return str1;
}
//初始化
char* s1 = str1;
char* s2 = str2;//保护原始数据,让s1,s2往后走
char* cp = str1;//记录开始查找的位置
//大循环包括小循环
while (*s1!='\0' && *s2!='\0')
{
//s1和cp统一
s1 = cp;
//不相等,往后走
while (*s1 != *s2)
{
s1++;
s2++;
}
//如果有一个相等,进入小循环检查
while (*s1 == *s2)
{
if (*s2 == '\0')
{
return cp;//成功匹配
}
s1++;
s2++;
}
//不对的话,cp++再进入下一次循环
cp++;
}
return cp;
}
int main()
{
char arr1[] = { "abbbbcf" };
char arr2[] = { "bbc" };
//printf("%s\n", strstr(arr1, arr2));
printf("%s\n", my_strstr(arr1, arr2));
return 0;
}
strtok
字符串按所给符号截取函数,第一次使用strtok,使用拷贝数组,函数会保存记忆标记的位置(可能与static有关),所以第二次使用函数传参数NULL即可,默认从上次的标记位置开始寻找下一个标记
int main()
{
char arr1[40] = { "zhangtianjing@text.com" };
char buf[40] = { "0" };
strcpy(buf, arr1);
char p[] = { "@." };//是数组
//普通用法
//printf("%s\n", strtok(buf, p));
//printf("%s\n", strtok(NULL, p));
//循环用法
char* ret = NULL;
for (ret=strtok(buf, p);ret!=NULL;ret=strtok(NULL,p))
{
printf("%s\n", ret);
}
return 0;
}
strerror
strerror把错误码翻译成错误信息的函数,errno是存储错误码的全局变量,相当于(下图)的i,使用strerror(errno)即可
perror打印错误信息函数,有自定义文字部分,等于printf+strerror,缺点是有时候我们不需要打印功能
今天的文章就到这结束了
喜欢的话可以点点赞哦
作者会继续更新文章的!