库函数中有许多函数与字符串有关,一些常见的有strlen测量字符串的长度,strcpy拷贝字符串到新的字符串下,strcat进行字符串的追加等等,这篇文章会详细实现上述字符串函数的模拟实现。
1.strlen的模拟实现
首先我们先了解一下strlen函数,strlen是统计字符串内字符的数量的,而字符串的数量要么为正数,要么就是0,所以strlen返回值应该是大于0的,当然我们可以从strlen - C++ Reference (cplusplus.com)来看一下关于strlen的具体描述。
返回值是size_t类型的。
这里就要注意一个点:返回值是size_t类型的,所以直接判断不了两个size_t类型的相加减结果,比如我想比较一下两个字符串谁长谁短,那我写了下面的代码
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abcdef";
const char* str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");//这里判断是非常大的正数 所以打印第一个
//强转成int可以判断
}
else
{
printf("srt1>str2\n");
}
return 0;
}
上述代码按逻辑来讲,就是str2<str1的,应该打印else里面的内容,但实际打印的确是str2>str1,这是因为返回的值都是size_t类型的,所以相加减以后还是无符号整数,判断条件strlen(str2) - strlen(str1)最后得到的就是一个大于0的很大的正数,所以选择打印str2>str1。
所以以后应该注意不能这样判断两个字符串谁长谁短了。
现在开始模拟实现strlen
第一种
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char *p)
{
assert(p);
int count=0;
while (*p != '\0')
{
count++;
p++;
}
return count;
}
我们把字符串首字符的地址传给一个函数,函数的返回值类型是size_t类型,然后进行一个断言,防止传来的是NULL,(不了解断言可以看我主页关于指针的内容,里面有提到。)给一个while循环,然后只要字符不是'\0',就++,在循环外面定义一个count,然后只要不是'\0',count也++,这样就可以记录字符个数了,最后return count即可。
第二种
size_t my_strlen(char* p)
{
char* p1 = p;
while (*p != '\0')
{
p += 1;
}
return p - p1;
}
同样将首字符地址传给函数,先定义一个指针等于首字符指针,然后给一个while循环,只要不遇到'\0',就让p+=1,最后直接返回p-p1,两指针相减就是他们两个之间元素的个数。
第三种
size_t my_strlen(char* p)
{
if (*p = '\0')
return 0;
else
return 1 + my_strlen(p + 1);
}
第三种采用递归的方式,传给函数首元素的地址,然后进行判断,只要*p不是'\0',那么返回一个my_strlen(p+1)+1,前面返回的函数部分意思是指针接着往下走,直到最后遇到'\0'也就代表字符串结束的时候,后面的+1就相当于计数部分,如果不是'\0',就代表是一个元素,那么就+1,这样到最后遇到'\0'的时候,返回一个0,在加上之前的若干个1最后就是字符串的长度。
2.strcpy模拟实现
strcpy是将其参数中后一个字符数组的内容拷贝到前一个字符数组中的一个函数,具体函数的形式如图
拷贝完成以后返回一个拷贝好的数组的首元素地址。
那么我们要注意几个问题,首先你要拷贝到的字符数组必须可修改,如果不能修改的话,与函数功能冲突。其次就是你要拷贝到的字符数组空间要能够容纳的下拷贝的东西,不能越界使用空间。
下面进行模拟实现
char * my_strcpy(char* dest, const char* src)
{
asser(src != NULL);
asser(dest != NULL);
char* ret = dest;
while (*src != '\0')
{
*dest++ = *src++;
//因为是后置的所以先解引用等于完之后再++;
}
*dest = *src;
return ret;//还原dest指针
}
传给一个函数目标数组和被拷贝数组,然后判断一下两个指针是否为空,以防越界访问内存,接着给一个while循环,如果被拷贝数组不为'\0',那么就让目标数组等于被拷贝数组的内容即可,然后再++进行下一个元素的拷贝,这里是后置的++所以不用担心第一个元素不会被拷贝上的问题,接着遇到'\0',表示前面的元素都已经拷贝完成,再让目标数组最后存一个'\0'即可,可以让*dest='\0',也可以用我代码里的写法,反正到最后被拷贝数组也走到了'\0',直接另他们两个相等即可。
一种简单的写法
#include<stdio.h>
#include<assert.h>
char * my_strcpy(char* dest, const char* src)
{
asser(src != NULL);
asser(dest != NULL);
char* ret = dest;
while (*dest++ = *src++) //这里\0等完之后再结束
{
;
}
return ret;//还原dest指针
}
直接把相等放到判断条件里,如果是'\0'的话也是先让他们等于再结束,括号里面给一个 “ ; ” 代表空语句。
3.strcat的模拟实现
strcat函数就是将其参数里第二个参数中的内容追加到第一个参数后面,这里也要注意,目标数组要有足够大的容量可以盛得下追加后的字符数组。
我们进行模拟实现
char* my_strcat(char* dest, char* src)
{
assert(dest && src);//同时判断有没有NULL,如果有一个就报错
char* ret = dest;
while (*dest != '\0')
{
dest++;
}
while (*dest++ = *src++) //不能自己给自己追加,因为会把自己的\0给覆盖掉让他没有停下的一个指令会死循环 越界 程序崩掉
{
;
}
return ret;
}
这个大体跟strcpy是差不多的,就是让指针找到目标数组的'\0',接着'\0'被接下来的元素覆盖掉,然后再用strcpy那一套把被拷贝数组的东西追加给原数组即可。
注意:不能自己给自己追加,不能用str字符数组给str字符数组追加,因为str数组被覆盖掉'\0'已经没有'\0'了,那么会一直给自己追加,导致程序崩溃。
4.strcmp模拟实现
strcmp函数用来比较两个字符串数组的大小。
模拟实现
#include<stdio.h>
#include<assert.h>
int mystrcmp(const char* p1, const char* p2)
{
assert(p1 && p2);
while (*p1 == *p2)
{
if (*p1 == '\0')
return 0;
p1++;
p2++;
}
return *p1 - *p2;
}
用while循环一个一个比较字符,如果两个字符串数组都等于'\0'了,那么说明比较完毕两个相等,返回一个0,如果两字符串数组不相等了,返回两个元素相减的值,如果大于0,证明第一个数组元素大于第二个数组元素,反之说明小于。
上述三种函数(除strlen外)还有带n的版本,即strncpy,strncat,strncmp其实大同小异只是加了一个参数用来限制个数,使得函数比较安全,有兴趣可以到cplusplus.com - The C++ Resources Network里面去搜索一下了解了解,如果想了解怎么模拟实现可以私聊我或者评论。
在最上面的搜索框直接搜索函数名即可。
5.strstr的模拟实现
strstr就是在第一个参数的字符串内寻找有没有第二个字符串出现,比如第一个字符串数组为"abcde",第二个为"bcd",bcd出现在第一个字符串数组内了,那么返回第一个字符串数组中b的位置,如果没有出现的话,返回NULL。
接下来进行模拟实现
#include<stdio.h>
char* my_strstr(const char* p1, const char* p2)
{
if (*p2 == '\0')
return p1;
char* s1 = NULL;
char* s2 = NULL;
char* cur = p1;
while (*cur)
{
s1 = cur;
s2 = p2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return cur;
}
cur++;
}
return NULL;
}
思路是首先我们创建一个cur指针等于p1指针,然后while循环判断条件就是*cur,用来表明当前的位置,只要cur指针不为0的话我们就一直进行,括号内给一个条件cur++,直到遇到'\0'停止,括号内部进行判断,判断条件很简单我们写一个while循环,只要p1,p2不为'\0'且解引用后他俩相等我们就让他俩++后,继续判断直到p2等于'\0'了,也就代表p2字符串数组结束了,说明在p1中找到了p2,这里返回一个记录当前值的cur即可,因为cur是等于最初的p1的,而p1在判断的时候已经++了,指向后面的元素了,如果比较到一半发现不相同了,那就退出循环让cur++比较后面的看看有没有相等的时候,这里注意比较到一半p1已经++了,找到原始的已经不容易了,所以我们在最开始创建一个指针s1让它等于p1,再创建一个变量s2让他等于p2,这样p1、p2就不会变化了,我们就有一个参照物,就算判断到一半发现不相等了,我们在下一次判断时让s1等于一个cur(p1)即可,让s2等于p2即可,因为p1(cur)、p2没有变化,所以还在原来的位置。在cur(p1)等于'\0'后,说明没有找到p1里有p2,所以返回一个NULL空指针。
特殊情况:如果p2传一个'\0'空数组上来,我们直接返回p1即可。
6.strtok函数的使用
strtok是用来根据特殊符号(指定分隔符)把一个字符串数组给切割的。首先给他传参一个字符串数组,再传参一个字符串数组,第一个是原字符串数组,包含符号也包含字母的,第二个是包含指定符号的字符串数组。具体使用方法如下
#include<stdio.h>
#include<string.h>
int main()
{
char arr[12] = { "[email protected]" };
const char* sep = "@.";//无所谓顺序
char* ret = NULL;
ret = strtok(arr, sep);
printf("%s\n", ret);
//传完第一个arr2后接下来传空指针就能从它保留的位置开始往后读
ret = strtok(NULL , sep);
printf("%s\n", ret);
ret = strtok(NULL, sep);
printf("%s\n", ret);
return 0;
}
第一次传给一个arr首元素的地址,然后把分隔符传给函数,那么第一次printf的结果就是myh,这个函数进行了标记,把分隔符变成了NULL标记,所以下一次再传参传的是NULL和分割符,再遇到分隔符就是跟第一次一样,把分隔符变成了NULL标记,打印的是mmm,下一次依然是传NULL和分隔符,在遇到最后的NULL('\0')后便停止了,最后打印出yyy。
还有一种编程写法
#include<stdio.h>
#include<string.h>
int main()
{
char arr[12] = { "[email protected]" };
const char* sep = "@.";//无所谓顺序
char* ret = NULL;
for (ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
初始值就是第一次传参给strtok一个字符串函数,然后判断条件就是ret不(遇到)返回NULL,变化值为传参给strtok一个NULL。
7.strerror的使用
在不同的系统和C语言标准库的实现中都规定了一些错误码,⼀般是放在errno.h这个头文件中说明
的,C语言程序启动的时候就会使用一个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码,存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
printf("%s", strerror(1));
return 0;
}
以上就是一些常见字符函数的相关使用与模拟实现,如果有问题欢迎在评论区指正~
标签:字符,p1,函数,++,char,数组,字符串,NULL,模拟 From: https://blog.csdn.net/2302_81328814/article/details/139575529