模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数

简介: 模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
  • 前言: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;
}

运行结果:

序言

成大事不在于力量的大小,而在于能坚持多久。希望各位也能每天坚持学习,能够坚持也就是一种最大的天赋!如若写的不好的地方也希望各位指出。


相关文章
|
9天前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
30 10
|
3天前
|
存储 算法 程序员
C语言:库函数
C语言的库函数是预定义的函数,用于执行常见的编程任务,如输入输出、字符串处理、数学运算等。使用库函数可以简化编程工作,提高开发效率。C标准库提供了丰富的函数,满足各种需求。
|
8天前
|
机器学习/深度学习 C语言
【c语言】一篇文章搞懂函数递归
本文详细介绍了函数递归的概念、思想及其限制条件,并通过求阶乘、打印整数每一位和求斐波那契数等实例,展示了递归的应用。递归的核心在于将大问题分解为小问题,但需注意递归可能导致效率低下和栈溢出的问题。文章最后总结了递归的优缺点,提醒读者在实际编程中合理使用递归。
32 7
|
8天前
|
存储 编译器 程序员
【c语言】函数
本文介绍了C语言中函数的基本概念,包括库函数和自定义函数的定义、使用及示例。库函数如`printf`和`scanf`,通过包含相应的头文件即可使用。自定义函数需指定返回类型、函数名、形式参数等。文中还探讨了函数的调用、形参与实参的区别、return语句的用法、函数嵌套调用、链式访问以及static关键字对变量和函数的影响,强调了static如何改变变量的生命周期和作用域,以及函数的可见性。
22 4
|
6天前
|
存储 C语言
【c语言】字符串函数和内存函数
本文介绍了C语言中常用的字符串函数和内存函数,包括`strlen`、`strcpy`、`strcat`、`strcmp`、`strstr`、`strncpy`、`strncat`、`strncmp`、`strtok`、`memcpy`、`memmove`和`memset`等函数的使用方法及模拟实现。文章详细讲解了每个函数的功能、参数、返回值,并提供了具体的代码示例,帮助读者更好地理解和掌握这些函数的应用。
9 0
|
6天前
|
C语言
【c语言】qsort函数及泛型冒泡排序的模拟实现
本文介绍了C语言中的`qsort`函数及其背后的回调函数概念。`qsort`函数用于对任意类型的数据进行排序,其核心在于通过函数指针调用用户自定义的比较函数。文章还详细讲解了如何实现一个泛型冒泡排序,包括比较函数、交换函数和排序函数的编写,并展示了完整的代码示例。最后,通过实际运行验证了排序的正确性,展示了泛型编程的优势。
8 0
|
9天前
|
算法 C语言
factorial函数c语言
C语言中实现阶乘函数提供了直接循环和递归两种思路,各有优劣。循环实现更适用于大规模数值,避免了栈溢出风险;而递归实现则在代码简洁度上占优,但需警惕深度递归带来的潜在问题。在实际开发中,根据具体需求与环境选择合适的实现方式至关重要。
9 0
|
5月前
|
程序员 C语言 C++
【C语言基础】:动态内存管理(含经典笔试题分析)-2
【C语言基础】:动态内存管理(含经典笔试题分析)
|
5月前
|
程序员 编译器 C语言
【C语言基础】:动态内存管理(含经典笔试题分析)-1
【C语言基础】:动态内存管理(含经典笔试题分析)
|
Java 数据库连接 C语言
C语言进阶教程(内存分配常见问题分析)
C语言进阶教程(内存分配常见问题分析)
69 0