- 前言:C语言中拥有非常多的库函数,仅仅知道它们是不够的,在知道它们的原理后,去模拟实现它能够帮助我们更好的掌握这些库函数。
PS(在面试时,部分企业会让你来模拟实现一些库函数)
模拟实现strcmp
在模拟实现一个库函数之前,我们要先了解这个库函数的作用
这里我们可以通过这个查看C语言库函数的网站去查找:查找库函数
由此我们可以知道,strcmp的作用是将俩个字符串相比较,如果前者比后者长则返回大于0的数,反正则返回小于0的数,相等则返回等于0的数
代码实现思路:
分为三种情况:
①俩者所有字符全部相等 --> 返回0
②前者比后者小 --> 返回 -1
③前者比后者大 --> 返回1
代码实现:
#include <stdio.h> #include <assert.h> //模拟实现strcmp int my_strcmp(const char* str1, const char* str2)//用const修饰防止传过来的指针值被修改 { assert(str1 && str2);//放在传过来的是空指针 while (*str1++ == *str2++)//如果字符相等则进入循环看它们是否一一对应相等 //如果不相等则跳出循环 { if (*str1 == '\0')//如果*str1等于'\0'时,说明俩个字符串全部遍历完并且一一对应相等 return 0; } if (*str1 > *str2)//如果不相等判断字符的大小,前置大则返回1 return 1; else return -1; } int main() { char arr1[100] = {0}; char arr2[100] = {0}; gets(arr1); gets(arr2); int ret = my_strcmp(arr1, arr2);//保存返回值 printf("%d\n", ret); return 0; }
运行结果:
模拟实现strcat
还是一样的,在实现一个库函数之前,先了解该库函数的作用
注:将一个字符串,拼接到另一个字符串后面,该字符串不能是同一个字符串,并返回目标字符串的地址。
代码实现思路:
- 找到目标空间地址的尾部
- 将另一个字符串衔接上去
代码实现:
//模拟实现strcat char* my_strcat(char* str1, char* str2) { //1.找到目标地址的尾部 //2.衔接上去 assert(str1 && str2); char* tmp = str1;//保留目标空间的起始地址 while (*str1++)//找到目标地址的尾部'\0' { if (*str1 == '\0')//找到'\0' { while ( *str1++ = *str2++)//将地址衔接上去 { ; } } } return tmp; } int main() { char arr1[100] = {0}; char arr2[100] = {0}; gets(arr1); gets(arr2); my_strcat(arr1, arr2); printf("%s\n", arr1); return 0; }
运行结果:
模拟实现strstr
工欲善其事,必先利其器。如果我们想做好一件事,首先就要做好充分的准备工作。所以我们先了解其作用再来实现!
其作用是:在一个字符串中,找另一个字符串,如果没有找到则返回NULL,找到了就返回该字符串,并将原函数中该字符串后面的结果一并输出。
代码实现思路:
s1,s2(都是俩个字符串的指针)代表在s1中找s2
如果我们让s1,s2直接向匹配我们会发现,我们哪怕找到了想对应的字符,但是由于s1是不断的在往后走,我们并不能直接找到字符在相等的时候的位置,此时我们想要在通过原本的指针去找到相等的起始位置是十分困难的。因此,我们需要在引入一个指针去帮住我们记录下当俩个字符串完全相等的时候的起始地址。
我们让指针cp帮我们早到原本的地址,让s1去判断是否相等,让字符完全匹配时,返回cp的地址即可
代码实现:
//模拟实现strstr const char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); const char* cp;//记录开始匹配的位置 const char* s1;//记录str1的位置 const char* s2;//记录str2的位置 if (*str2 == '\0') { return str1; } cp = str1;//让cp去记录开始匹配的位置 while (*cp) { s1 = cp; s2 = str2;//让s2代替原本的str2去动 while (*s1 && *s2 && *s1 == *s2)//s1,s2 !='\0' 并且s1 == s2 { //找到它们的相同值,看他是否对应 s1++; s2++; } if(*s2 == '\0')//如果全部找完,发现s2是'\0'说明找到了 { return cp;//返回之前所留的cp记录的就是一开始的位置 } cp++;//如果没有找到,就让cp往后加一继续重复刚刚的过程 } return NULL;//如果全部运行了都没找到说明没有找到,返回空指针; } int main() { char arr1[] = "mnabbbefghij"; char arr2[] = "bbb"; char* ret = my_strstr(arr1, arr2); if (ret == NULL) { printf("找不到\n"); } else { printf("%s\n", ret); } return 0; }
运行结果:
模拟实现memcpy
依然来先看该库函数的作用:
可以将任意同类型的数据,拷贝到另一个同类型的数据中去,但不能拷贝重叠内存
代码实现思路:
首先在实现的过程中,我们要知道,我们并不知道使用者会传过来什么类型的数据,因此我们在实现的过程中用俩个 void* 类型的指针来接受,并且我们要让使用者传过来他所需要拷贝的数据有多少字节。为什么需要知道字节?因为我们并不知道它的类型,我们将传过来的地址转换成char*类型,让它一次加1走一个字节俩俩交换,就能实现任意数据的交互了。
代码实现:
//模拟实现memcpy -- 不重叠的内存拷贝可以使用memcpy void* my_memcpy(void* arr1, const void* arr2, size_t num) { char* tmp = arr1; assert(arr1 && arr2); while (num--) { *(char*)arr1 = *(char*)arr2;//强制类型转换是一种临时变量 arr1 = (char*)arr1 + 1;//让它们一次走一个字节,走一次交换换一次 arr2 = (char*)arr2 + 1; }//全部字节交换完成后它们的地址也就全部交换完成了 return tmp; } int main() { int arr1[] = { 1,2,3,4,5,6 }; int arr2[] = { 0,9,2,2,9 }; my_memcpy(arr1, arr2, 16); int i = 0; for (i = 0; i < 6; i++) { printf("%d ", arr1[i]); } return 0; }
运行结果:
模拟实现memmove
还是老规矩,运行代码之前先来看它的作用
前面我们说讲的memcpy可以拷贝同类型的数据,但不能拷贝重叠内存,而这个库函数就可以完美实现拷贝重叠内存
代码实现思路:
我们要知道在同一个数据中拷贝数据的时候会出现俩种情况:
①把低地址的值覆盖到高低址
②把高地址的值覆盖到低地址
因此我们要对俩种情况进行分类讨论
代码实现:
//模拟实现memmove -- 可以拷贝重叠内存 void* my_memove(void* dest, void* src, size_t sz) { char* tmp = dest; assert(dest && src); if (dest < src)//分俩种情况 { //从前向后 while (sz--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { //从后往前 while (sz--) { *((char*)dest+sz) = *((char*)src + sz);// 让src与dest找到最后一个数,从后往前赋值 } } return tmp; } int main() { int arr[] = { 1,3,2,7,8 }; my_memove(arr+2, arr , 12); int i = 0; for (i = 0; i < 5; i++) { printf("%d ", arr[i]); } return 0; }
运行结果:
序言
成大事不在于力量的大小,而在于能坚持多久。希望各位也能每天坚持学习,能够坚持也就是一种最大的天赋!如若写的不好的地方也希望各位指出。