哈喽哈喽,大家好~
今天和大家分享一下C语言中用的比较多的一些库函数的模拟实现.
大家在使用库函数的时候,是不是也会和我一样很好奇,到底这些库函数背后是怎么做到的呢?
与其让好奇的种子长埋心间,不如就让它破土而出!
下面我们就来尝试模拟几个常用的库函数啵~
目录
一.模拟实现memcpy
首先我们可以看到它在函数传参的时候传递了三个参数,两个指针和一个无符号整数类型.
它能够实现将source中num个字节的数据复制到destination中去.数据可以包含\0.
但是如果source和destination有重叠的部分,那后果无法预知.
了解了我们需要的参数,和函数的作用,接下来我们就来动手操作一下吧.
#include <stdio.h>
#include <assert.h>
void* mymemcpy(void* dst, const void* src, size_t count)
{
void* ret = dst;
assert(dst);
assert(src);
while (count--)
{
*(char*)dst = *(char*)src;
dst = ++(char*)dst;
src = ++(char*)src;
}
return(ret);
}
在这里我用到了assert这个函数,他是用来断言两个地址非空的.
实现的过程其实很简单,但是因为每次copy一个字节,所以我们用到了指针的强制转换然后解引用交换内容.
让我们来试试,我们的mymemcpy和memcpy的作用是否相同吧~
在这里要注意一个汉字占两个字节哦!
输出完全一致.
二.模拟实现memmove
memmove是一个和memcopy很像的函数,我们可以看到他们的形参都是相同的.
不同之处就在于,memmove也可以复制字符串本身的内容覆盖自己.
举个例子:
我们选择用arr这个字符数组的前两个字节去覆盖这个字符数组下标为4、5,也就是它的第五第六个元素.输出结果由原来的hahaoo变为hahaha~
下面我们来动手操作一下吧!
在编写的过程中我们首先要考虑的是覆盖的情况:
假设我们要复制count个元素,我们可以从前向后覆盖原有元素,也可以从后向前覆盖.那么我们到底选择哪一种方式呢?
如下图所示:
如果我们要用绿色方框内的元素覆盖红色方框内的元素,那我们应该选择的是用绿色方框的第一个元素从前向后的覆盖红色方框.即arr[2]先覆盖arr[1],然后arr[3]覆盖arr[2].
否则如果先让arr[3]去覆盖,那arr[2]的内容就改变了.
同理,如果我们要用蓝色方框内的元素覆盖红色方框内的元素,这时候我们就应该从后向前覆盖了.
判断的方式就是,如果目的地址,小于源头地址,即第一种情况那就和memcpy一样,否则就是第二种情况,代码如下.
void * mymemmove(void* dst, const void* src, size_t count)
{
void* ret = dst;
assert(dst);
assert(src);
if (dst <src)
{
while (count--)
{
*(char*)dst = *(char*)src;
dst = (char*)dst + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dst + count)=*((char*)src+count);
}
}
return(ret);
}
我们可以看到,mymemmove和memmove的结果是完全相同的.
三.模拟实现strlen
strlen应该是一个我们常常会用到的很简单的函数,她是一个计算字符串长度的函数
只需要传一个参数,给出字符串首地址,直到遇到'\0'停止.计算出字符串中元素的个数.
模拟实现如下:
size_t my_strlen(const char*str)
{
int a = 0;
while (*str != '\0')
{
a++;
str++;
}
return a;
}
我们试着给出一个字符数组中存放四个字符,然后调用我们的自定义函数,输出为4.
四.模拟实现strcpy
strcpy是串拷贝函数,他可以将一个字符串的内容拷贝到另一个字符串中.
截止标志是'\0',连同'\0'一起拷贝过去.
模拟实现如下:
char* mystrcpy(char* destination, const char* source)
{
char* str = destination;
while ((*(destination++) = *(source++)) != '\0')
{
;
}
return str;
}
可以看到我们模拟实现的mystrcpy和strcpy输出的结果完全相同.(因为我打印的是字符串,会截止到'\0'前).
五.模拟实现strcmp
它是用来比较字符串,但是比较的并非是字符串的长度,而是字符串的内容.
从字符串的第一个元素开始比较,如果相同就继续比较,如果不相同就返回一个整数,整数大于零表示第一个字符串的元素>第二个字符串的元素,比较的是字符元素的ASCII码大小.
注:
※比较的不是两个字符串长度
※比较的是不同的第一个元素大小,不能说明后续所有元素大小关系.
下面我们来模拟实现一下~
int mystrcmp(char* str1, char* str2)
{
while(*str1 == *str2)
{
str1++;
str2++;
}
return *str1 - *str2;
}
我们可以看到,str2只有一个字母z,输出仍是str1<str2.mystrcmp和strcmp的结果一致.
六.模拟实现strcat
strcat函数是将字符串连接到另一个字符串后面,将原先的'\0'覆盖为连接的字符串内容,然后在新生成的字符串后面加上'\0'.
模拟实现如下:
char* mystrcat(char* str1, char* str2)
{
char* str = str1;
int i = 0;
while (*str)
{
str++;
}
while (*str++ = *str2++)
{
;
}
return str1;
}
在实现过程中,我们不能更改str1的地址,所以我们首先创建一个指针存放他的地址,然后我们通过这个指针来操作,str1的地址不会改变.
可以看出我们模拟实现的和strcat的功能一致.
七.模拟实现strstr
strstr函数是一个搜索一个字符串中是否包含另一个字符串的内容,如果包含则返回str1中含有str2的第一个元素的地址,如果不包含,则返回一个空指针.
我们尝试使用一下这个函数:
下面我们来模拟实现一下啵.
char*mystrstr (const char*str1,const char*str2)
{
const char*s1 = str1;
const char*s2 = str2;
char* p = str1;
if (*str2 == '\0')
{
return str1;
}
while (*p)
{
s1 = p;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && (*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return p;
}
p++;
}
return NULL;
}
输出如下,和strstr完全一致.