C语言——模拟实现strcpy,strcat,strstr,strchr,strcmp,memcpy,memmove函数

1.模拟实现strcpy

strcpy,即string copy(字符串复制)的缩写。
strcpy是一种C语言的标准库函数,strcpy把含有’\0’结束符的字符串复制到另一个地址空间,返回值的类型为char*。
strcpy(dest,src)函数是把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间中,而且src和dest所指内存区域不可以重叠。这就要求dest必须有足够的空间来容纳src的字符串。

思路:对src字符串一个一个进行拷贝,当遇到NULL时,dest停止拷贝。

参考代码】:

#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h> 
//源不需要被改所以加const修饰
char* my_strcpy(char* dest, const char* src) {
 //记录初始位置,返回ret 
 char* ret = dest; 
 assert(dest != NULL);
 assert(src != NULL);
 while (*dest++ = *src++)
 {
  ;
 }
 return ret;
 //方法二:
 //while (*src != '\0')
 //{
 // //将src指向的内容拷贝到dest指向的空间里
 // *dest = *src; 
 // src++;
 //  dest++;
 //}
 直到*dest = '\0'为止
 //*dest = '\0'; 
 //return ret;
}
int main() {
 char arr[20] = { 0 }; //目标dest
 char *str = "abcdef"; //源src
 my_strcpy(arr, str);
 printf("%s\n", arr);
 system("pause");
 return 0;
}

2.模拟实现strcat

原型:char *strcat(char *dest, const char *src)
功能:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。

注:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。

参考代码】:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
char* my_strcat(char*dest, const char* src) {
 char * ret = dest;
 assert(dest != NULL);
 assert(src != NULL);
 //找dest指向的'\0'
 while (*dest != '\0') {
  dest++;
 }
 //注意此处while不能写成 (*dest++ != '\0')
 //因为永远不能碰到'\0',到\0后又++
 //拷贝src指向的内容
 while (*dest++ = *src++) {
  ;
 }
 return ret;
}
int main() {
 char arr[20] = "hello ";
 my_strcat(arr, "word");
 printf("%s\n", arr);
 system("pause");
 return 0;
}

3.模拟实现strstr

strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回str2在str1中首次出现的地址;否则,返回NULL。
在这里插入图片描述

参考代码】:

#include<stdlib.h>
#include<stdio.h>
#include<assert.h>
char *my_strstr(const char* str1, const char* str2) {
 assert(str1);
 assert(str2);
 char *s1 = (char*)str1;
 char *s2 = (char*)str2;
 char *p = NULL;
 if (*str2 == '\0')
  return NULL;
 while (*s1) {
  p = s1;
  s2 = str2;
  while (*p && *s2 && (*p == *s2)) {
   p++;
   s2++;
  }
  if (*s2 == '\0')
   return s1;
  s1++;
 }
 return NULL;
}
int main() {
 char arr[] = "abcdefabcdef";
 char* ret = my_strstr(arr, "def");
 if (ret != NULL)
  printf("%s\n", ret);
 else
  printf("不存在");
 system("pause");
 return 0;
}

4.模拟实现strchr

strchr函数查找字符在指定字符串中从前面开始的第一次出现的位置,如果成功,则返回从该位置到字符串结尾的所有字符,如果失败,则返回 NULL。

原型:char *strchr( const char *string, int c )

参考代码】:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
char*my_strchr(const char *string, char c) {
 assert(string);
 while (*string != NULL) {
  if (*string == c) {
   //如果找到,则返回这个字符的地址
   return string;
  }
  else {
   string++;
  }
 }
 //找完全部,如果没有,则返回NULL
 return NULL;
}
int main() {
 char*arr = "abcdefe";
 char p = 'e';
 char *ret = my_strchr(arr, p);
 printf("%s\n", ret);
 system("pause");
 return 0;
}

5.模拟实现strcmp

原型:extern int strcmp(const char *s1,const char *s2);
规则:
当s1<s2时,返回为负数
当s1==s2时,返回值= 0
当s1>s2时,返回正数
即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇’\0’为止。如:
“A”<“B” “a”>“A” “computer”>“compare”
特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。

参考代码】:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2) {
 assert(str1);
 assert(str2);
 while (*str1 == *str2) {
  //判断两者相等中其中一个为0另外一个肯定也为0
  if (*str1 == '\0')
   return 0;
  str1++;
  str2++;
 }
 return *str1 - *str2;  //优化下列代码
 /*if(*str1 > *str2)
    return 1;
 else
    return -1;*/
}
int main() {
 //不能直接比较p,q因为其为地址
 char* p = "abcdef";
 char* q = "aaaaaaaaaaaaa";
 int ret = my_strcmp(p, q);
 if (ret < 0) {
  printf("p<q\n");
 }
 else if (ret == 0) {
  printf("p == q\n");
 }
 else {
  printf("p>q\n");
 }
 system("pause");
 return 0;
}

6.模拟实现memcpy

memcpy指的是c和c++使用的内存拷贝函数,memcpy函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中。
原型:void *memcpy(void *dest, const void *src, size_t n);

参考代码】:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t n) {
 void* ret = dest;
 assert(dest);
 assert(src);
 while (n) {
  //void*不能强制类型转换,所以类型转换为char*
  *(char*)dest = *(char*)src; 
  //(char*)dest++; //不可以,因为++是对dest作用的
  dest = (char*)dest + 1;
  src = (char*)src + 1;
  //memcpy结束的标志为n减到0
  n--; 
 }
 return ret;
}
int main() {
 int i = 0;
 int arr1[10] = { 0 }; //换成float等类型也适用
 int arr2[] = { 1,2,3,4,5,6,7 };//同上
 //无法用strcpy将arr2拷贝到arr1中
 //因为内存中1,2...的存储方式为:01 00 00 00,02 00 00 00
 //strcpy结束的标志为'\0',但是00中的'\0'不是所需的位置
 my_memcpy(arr1, arr2, 28);//arr2占用28个字节
 for (i = 0; i < 7; i++)
 {
  printf("%d ", arr1[i]);
 }
 printf("\n");
 system("pause");
 return 0;
}

7.模拟实现memmove

memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。
memmove 是一个内存操作函数,不是字符串操作函数,它可以处理多种类型的数据。其与memcpy最根本的区别为memmove可以反向拷贝。

下面我就具体解释一下memcpy的局限性:

举个例子:给上一个数组arr[] = 1 2 3 4 5 6 7 8 9,;现在你需要将 3 4 5 6拷贝到 5 6 7 8这几个数的位置去。如果你正序拷贝,你会发现将3拷到5的位置,4拷到6的位置,但是你再继续将5拷贝到7的位置的时候,你会发现5已经被改成了3,你再也无法将5拷贝到7的位置了。

下面我再用图片演示一下:
在这里插入图片描述
首先指针指的是首地址,当指针指向3的时候
在这里插入图片描述
你准备将3拷贝到5的位置
在这里插入图片描述
你5拷到7的时候,你会发现5所在的位置,已经被改成了3,这样你再也无法拷贝5这个数字了
在这里插入图片描述
这样你就会无法实现将3 4 5 6拷贝到 5 6 7 8。

所以,你需要进行反向的拷贝,就是从6开始向前拷贝,先将6拷到8的位置,再将5拷到7的位置,再将4拷到6的位置,再将3拷到5的位置,这样就不会遇到无法完全拷贝的问题。

现在我们需要进行分析,需要知道什么时候需要反向拷贝,什么时候不需要

你看,每当有你需要拷贝的数字的起始位置将要拷贝到你需要拷贝的位置发生了冲突,也就是将要被拷贝到的位置比将要拷贝数字的位置大,这样你就可以反向拷贝了。

下面我用图片演示一下:
在这里插入图片描述
你会看到,将3 4 5 6拷贝到5 6 7 8的位置,也就是
在这里插入图片描述
你会发现5所在的位置是大于3所在的位置的,如果是下面这样的
在这里插入图片描述
将2 3 4 5拷贝到 6 7 8 9的位置,虽然他们也是符合上面的条件,但是你并不需要将其反向拷贝,但是反向拷贝也不会有什么问题,所以,只要满足上面的这个条件,你就可以反向拷贝。

当然,如果他是正向的,也就是不满足上面的这个条件,你就直接拷贝就行了,和memcpy实现的原理一致。

参考代码】:

#define _CRT_SECURE_NO_WARNINGS 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
void* my_memcpy(void* dest, void* src, size_t n)
{
 void* ret = dest;
 char* str1 = (char*)dest; 
 char* str2 = (char*)src;
 assert(dest);
 assert(src);
 while (n--)
 {
  *str1++ = *str2++;
 }
 return ret;
}
void* my_memmove(void* dest, void* src, size_t n)
{
 void* ret = dest;
 char* str1 = (char*)dest;
 char* str2 = (char*)src;
 assert(dest);
 assert(src);
 if (str1 > str2)
 {
  while (n--)
  {
   *(str1 + n) = *(str2 + n);
  }
 }
 else
 {
  while (n--)
  {
   *str1++ = *str2++;
  }
 }
    return ret;
}
int main()
{
 int i = 0;
 int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 int arr2[10] = { 0 };
 int arr3[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
 my_memcpy(arr2, arr1, sizeof(arr1));
 my_memmove(arr3 + 5, arr3 + 4, 3 * sizeof(int));
 for (i = 0; i < 10; i++)
 {
  printf("%d ", arr2[i]);
 }
 printf("\n");
 for (i = 0; i < 10; i++)
 {
  printf("%d ", arr3[i]);
 }
 printf("\n");
 system("pause");
 return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值