模拟实现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;
}

运行结果:

序言

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


相关文章
|
2月前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
66 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
2月前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
83 6
|
3月前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
84 6
|
3月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
245 13
|
3月前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
105 11
|
3月前
|
大数据 C语言
C 语言动态内存分配 —— 灵活掌控内存资源
C语言动态内存分配使程序在运行时灵活管理内存资源,通过malloc、calloc、realloc和free等函数实现内存的申请与释放,提高内存使用效率,适应不同应用场景需求。
|
3月前
|
存储 C语言 开发者
C 语言指针与内存管理
C语言中的指针与内存管理是编程的核心概念。指针用于存储变量的内存地址,实现数据的间接访问和操作;内存管理涉及动态分配(如malloc、free函数)和释放内存,确保程序高效运行并避免内存泄漏。掌握这两者对于编写高质量的C语言程序至关重要。
83 11
|
3月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
92 1
|
3月前
|
存储 C语言 计算机视觉
在C语言中指针数组和数组指针在动态内存分配中的应用
在C语言中,指针数组和数组指针均可用于动态内存分配。指针数组是数组的每个元素都是指针,可用于指向多个动态分配的内存块;数组指针则指向一个数组,可动态分配和管理大型数据结构。两者结合使用,灵活高效地管理内存。

热门文章

最新文章