手把手教你玩转内存函数(含模拟实现)

简介: 手把手教你玩转内存函数(含模拟实现)

一、memcpy

1.认识memcpy

以下关于函数定义的图片均出自:cplusplus.com - The C++ Resources Network

memcpy函数的功能是从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中   memcpy的返回类型为void*,参数类型一个为void*,一个为const void*,还有一个是无符号整型即size_t   之所以一个用const修饰,另一个不用const修饰是因为我们要修改的目标只是dest所指向的内容,并不会修改到src所指向的内容。要注意一个点就是:这个函数操作的是字节,它不是根据你传递给它的那个类型来给予权限,它是一个字节一个字节的修改的。

2.使用memcpy

#include<stdio.h>
#include<string.h>//memcpy位于这个头文件中
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 4,3,1,2,6,8,7,11,10,9 };
  memcpy(arr1, arr2, 20);
   //将arr1所指向地址的20个字节一个一个转换为arr2所指向地址的20个字节
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }//将修改之后的数组打印出来
}

 

3.拓展:模拟实现memcpy

首先我们要明确我们这个函数要实现的目标,它的目标是将dest所指向地址的20个字节一个一个转换为src所指向地址的20个字节   那么我们的参数得有两个地址,这样才能访问dest和src所指向的内容并通过地址一个字节一个字节的访问,但我们并不知道用户会传递什么样的数据类型给我们,因此我们这两个数据的类型首先得是void*,又因为,我们只是对dest所指向的内容进行修改,src所指向的内容并不会被修改,故src参数的类型得是const void*接着我们还应该要有知道要拷贝多少个字节,因此我们还需要一个整型参数。


基本的框架已知,接下来就是实现


我们知道了两份数据的地址,一个是用来拷贝数据的数据,一个是拷贝内容,用来拷贝数据的数据得被转化为拷贝内容,又因为是一个字节一个字节的拷贝,所以在拷贝前,我们得把两个数据都强制类型转换为char*型的因为char*的操作权限为1个字节数,也就是+1只会跳过一个字节,这样便能够实现一个字节一个字节的将数据拷贝。


思路也有了,代码自然就出来了

#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* strc, size_t num)
{
  assert(dest && strc);//断言,避免使用者传递空指针
  int i = 0;
  for (i = 0; i < num; i++)//通过循环的方式一个字节一个字节的跳动
  {
    *((char*)dest + i) = *((char*)strc + i);//将对应的字节拷贝到目标数据上
  }
  return dest;//返回地址
}
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 4,3,1,2,6,8,7,11,10,9 };
  int a = 0;
  scanf("%d", &a);
  my_memcpy(arr1, arr1 + 2, a);
  //memcpy(arr1, arr2, 20);
  for (int i = 0; i < 10; i++)//通过循环将拷贝后的结果打印出来
  {
    printf("%d ", arr1[i]);
  }
}

二、memmove

1.认识memmove

memmove的作用其实和memcpy是一样的,唯一的区别则是memmove在地址有重叠的时候同样可以进行拷贝,而memcpy在内容有重叠的时候拷贝可能会出现问题   当然不一样的编译器实现memcpy也是不一样的,有的编译器对memcpy的实现和memmove的实现是一模一样的。等下我们就来展示,按我们方法写的memcpy在拷贝重叠内容时出现的问题。可以这么说memcpy能做的memmove也能做。memmove的返回类型为void*,三个参数两个为空类型的地址,其中一个地址所指向的内容不可改变(被const修饰),一个为无符号整型。

2.使用memmove

#include<stdio.h>
#include<string.h>
#include<assert.h>
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
    memmove(arr1 + 4, arr1 + 2, 20);
   //将该数组的3,4,5,6,7拷贝在该数组的5,6,7,8,9
  for (int i = 0; i < 10; i++)//通过循环将拷贝完成后的数据打印出来
  {
    printf("%d ",*(arr1+i));
  }
}

那么之前为什么说我们的memcpy在拷贝重叠地址内容时会出错了,这里笔者画下图给大家解释一下,假设我们有这样一个整型数组

那么当我们想将这儿的4,5,6,7拷贝成2,3,4,5,按之前的代码就应写成这副模样

#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memcpy(void* str1, const void* str2, int num)
{
  assert(str1 && str2);
  int i = 0;
  for (i = 0; i < num; i++)
  {
    *((char*)str1+i) = *((char*)str2+i);
  }
  return str1;
}
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memcpy(arr1+3, arr1+1, 16);
   //arr1+3的地址所指向的为该数组中的4,arr1+1所指向的为该数组中的2
   //16个字节为4个整型的长度
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
}

那按照我们之前所写的memcpy就是通过一个字节一个字节的变化先将4拷贝成2,再将5拷贝成3,这时停一下,我们看到第二张图

此时此刻,数组应该是这般模样

那么此时此刻你就会发现一个问题,我们的目标是将4,5,6,7拷贝成2,3,4,5也就是这样

但我们继续拷贝下去那就是将2,3,2,3上的后面两个2,3拷贝到6,7上呈现出来的就会是这般

3.拓展:模拟实现memmove

核心问题已经说出来了,接下来就提一下解决方案,很简单,我们不难看出,有问题的其实就在重叠的部分,这里我直接说结论了,我们得先将重叠部分给拷贝到目标上再将其他的部分拷贝到目标上才是合理的   继续举我们之前的这个例子


你想象一下,在拷贝的时候,我们先将5拷贝到6上,再将4拷贝到7上,再将2拷贝到4上,最后再将3拷贝到5上,也就是从后拷贝到前,还会有之前的问题吗?显然不会,另一种重叠的情况,就从前拷贝到后就行,不重叠的时候怎么来都行。总结一下,当dest<src时从前拷贝到后,当dest>src时从后拷贝到前,也就是我们这种情况,这个你得自己画下图才能完美的理解


上代码

#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memmove(void* dest,const void* src,int num)
{
  int i = 0;
  assert(dest && src);
  if (dest < src||dest==src)
  {
    for (i = 0; i < num; i++)
    {
      *((char*)dest + i) = *((char*)src + i);
    }
  }
  else if (dest > src)
  {
    while(num--)
    {
      *((char*)dest + num) = *((char*)src + num);
    }
  }
  return dest;
}
int main()
{
  int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr1+4, arr1+2, 20);
    //memmove(arr1 + 4, arr1 + 2, 20);
  //1 2 3 4 3 4 5 6 7 10
  for (int i = 0; i < 10; i++)
  {
    printf("%d ",*(arr1+i));
  }
}

三、memcmp

1.认识memcmp

memcmp的作用是在内存的存储层面上比较两个数据的大小,num为它们比较的字节个数

举个例子,如果我要比较两个整型数据,我就要传两个整型地址,同时还要传一个大小为4的整型变量(或者说无符号整型),如此才能够通过一个字节一个字节的比较从而得知是哪个整型大。当ptr1所指向地址的内容小于ptr2所指向的内容时返回一个小于0的数,大于返回大于0的数,等于返回0,跟之前strcmp接近    memcmp函数的返回类型为整型,参数为两个空类型指针和一个无符号整型,这两个空类型指针地址所指向的内容是不可被修改的(const),之所以这样设计因为我们只是比较大小,不需要改变对应内容的值。

2.使用memcmp

#include<stdio.h>
#include<string.h>//memcmp位于这个头文件中
int main()
{
  int a = 3;
  int b = 4;
  printf("%d",memcmp(&a, &b, 4));//将比较结果打印出来
}

2c1dcb92df37465381e679b624555b13.png

这里笔者就不对它进行模拟实现了,主要是不会,在尝试模拟的过程中,曾想过通过将数据强制类型转换成char*型的然后一个字节一个字节的比较,听上去很合理是吧,但是它有一个致命的问题,那就是权重问题,比方说7和11这两个数,设它们的类型是整型,那么它们的存储就是以二进制的形式进行存储的

在32位机器上它们的存储就是

1. 00000000000000000000000000000111//7
2. 00000000000000000000000000001011//11

但我们不可能一遇到不一样的就判断出这两个数据的大小,如果一遇到不一样的就判断,那就i会出现7大于11的搞笑情况。这个笔者实在不会搞,如果你们知道这个函数是怎么实现的,可以把相应链接发在评论区,非常感谢!

四、memset

1.认识memset

memset() 的作用是在一段内存块中填充某个给定的值,它的返回类型为空类型指针,三个参数类型,一个是地址,一个是整型,一个是无符号整型,memset的作用是将num个字节的数据非替换成value

2.使用memset

#include<stdio.h>
#include<string.h>//memset位于这个头文件中
int main()
{
  char arr1[] = "abc_def";
  memset(arr1,'x', 2);
//将arr1所给地址的内容,一个字节一个字节的修改成'x',共修改两个字节
  printf("%s", arr1);
}

 

3.拓展:模拟实现memset

#include<stdio.h>
#include<string.h>
void* my_memset(void*dest,int src,size_t num)
{
  int i = 0;
  for (i = 0;i<num; i++)
  {
    *((char*)dest + i) = (char)src;
    //通过强制类型转换和解引用,一个字节一个字节的实现目的
  }
  return dest;
}
int main()
{
  char arr1[] = "abc_def";
  //memset(arr1,'x', 3);
  my_memset(arr1, 'x',3);
  printf("%s", arr1);
}

好了,今天的分享到这儿就结束了,感谢各位友友的来访,祝各位友友前程似锦

相关文章
|
18天前
|
程序员 C语言
C语言库函数 — 内存函数(含模拟实现内存函数)
C语言库函数 — 内存函数(含模拟实现内存函数)
29 0
|
29天前
|
编译器 C语言 C++
【C语言】memset()函数(内存块初始化函数)
【C语言】memset()函数(内存块初始化函数)
27 0
|
29天前
|
编译器 C语言 C++
【C语言】memcpy()函数(内存块拷贝函数)
【C语言】memcpy()函数(内存块拷贝函数)
42 0
|
1月前
|
C语言
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
模拟实现C语言中经典库函数,字符相关的函数与内存相关的函数
|
1月前
|
算法 C语言
C语言中的内存函数你知道多少呢?
C语言中的内存函数你知道多少呢?
|
1月前
|
编译器 C语言
【C语言】动态内存函数介绍
【C语言】动态内存函数介绍
|
1月前
|
C语言
C语言里面的内存函数
C语言里面的内存函数
15 0
|
1月前
|
C语言
c语言内存函数的深度解析
c语言内存函数的深度解析
32 3
|
3天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)