memcpy和memmove函数的介绍和模拟实现

简介: memcpy和memmove函数的介绍和模拟实现

1. memcpy函数

1.1 函数的声明

void * memcpy ( void * destination, const void * source, size_t num );

  • destination

destination指向的是接收数据的目标,其类型转化为void*类型。

  • source

source指向提供数据复制的源头, 其类型转化为void*类型。

  • num

按字节为单位进行复制,size_t类型是无符号的整型类型

将destination指向的地址返回

1.2 函数的功能

  • 第一段

按字节为单位,从source位置开始将多少个字节数的数据复制给destination的内存块中。

  • 第二段

source和destination指针指向的底层数据类型和函数无关,结果是以二进制进行数据复制。

  • 第三段

函数不会检查是否有null或者'\0'在source中,它总是会准确复制num个字节数的数据。

  • 第四段

为了避免溢出,destination和source各自指向的数组大小至少是移动num个字节的大小,且不能出现destination和source指向的位置重叠(如果想要重叠,memmove是一个更安全的方法)。

1.3 函数的使用

/* memcpy example */
#include <stdio.h>
#include <string.h>
struct {
 char name[40];
 int age;
} person, person_copy;
int main ()
{
 char myname[] = "Pierre de Fermat";
 /* using memcpy to copy string: */
 memcpy ( person.name, myname, strlen(myname)+1 );
 person.age = 46;
 /* using memcpy to copy structure: */
 memcpy ( &person_copy, &person, sizeof(person) );
 printf ("person_copy: %s, %d \n", person_copy.name, person_copy.age );
 return 0;
}

1.4 函数的模拟实现(重点)

1.4.1 模拟分析

(1)首先,我们不知道要被复制的数据是什么类型,所以参数只能是void*来接收数据,既然不知道是什么数据类型,所以我们也不清楚指针要怎么跳跃去复制,所以只能一个一个字节去复制,这就必须在函数体内把destination和source指向强转成char*(这里就会有同学问了,为什么能直接用char*接收呢?答:因为char*只能接受char*的指针,而void*类型虽然不能直接访问,但是可以接收任何类型的指针)。且观察mencpy,发现需要一个目标指针destination、源头指针source和字节个数size_t  num。

(2)source指针指向的内容是不需要改变的,所以我们可以加上const修饰,把里面的数据保护起来(const void* source)。

(3)为了实现链式访问,我们要将传进来的目标起始地址(destination)返回。由于这个函数在执行的时候会改变destination存储的内容,所以我们要重新创建一个void*类型的指针来代替destination指针移动。

(4)为了避免传进来的地址是空指针,我们需要用assert来断言传进来的地址不是空指针。

1.4.1 模拟实现

//自我实现memcpy的功能
void* my_memcpy(void* destination, const void* source, size_t num) {
  //先判断destination和source是不是为空
  assert(destination && source);
  void* tmp = destination;
  while (num--) {
    //记住强转数据类型并不会永久改变变量的数据类型
    *(char*)tmp = *(char*)source;
    ((char*)tmp)++;
    ((char*)source)++;
  }
  return destination;
}
int main() {
  int a[4] = { 0 };
  int b[4] = { 1,2,3,4 };
  int num = sizeof(b);
  my_memcpy(a, b, num);
  for (int i = 0;i < 4;i++) {
    printf("%d ", a[i]);
  }
  return 0;
}

2. memmove函数

2.1 函数的声明

void * memmove ( void * destination, const void * source, size_t num );

  • destination

destination指向的是接收数据的目标,其类型转化为void*类型。

  • source

source指向提供数据复制的源头, 其类型转化为void*类型。

  • num

按字节为单位进行复制,size_t类型是无符号的整型类型。

将destination指向的地址返回 。

2.2 函数的功能

  • 第一段

按字节为单位,从source位置开始将多少个字节数的数据复制给destination的内存块中。复制就像使用了中间缓冲区一样,允许重叠复制。

  • 第二段

source和destination指针指向的底层数据类型和函数无关,结果是以二进制进行数据复制。

  • 第三段

函数不会检查是否有null或者'\0'在source中,它总是会准确复制num个字节数的数据。

  • 第四段

为了避免溢出,destination和source各自指向的数组大小至少是移动num个字节的大小。

2.3 函数的使用

/* memmove example */
#include <stdio.h>
#include <string.h>
int main()
{
  char str[] = "memmove can be very useful......";
  memmove(str + 20, str + 15, 11);
  puts(str);
  return 0;
}

2.4 函数的模拟实现(重点)

2.4.1 模拟分析

  • memcpy和memmove的比较

为什么需要memmove?就得了解memcpy和memmove的区别!

这个还要从上面的memcpy函数说起。因为memcpy函数不能将一个数组的中的数据拷贝到自身(也就是目标数据是自己,源数据也是自己,只不过是一个数组里面不同的位置的数据拷贝到另外一个位置上),如果像这样拷贝就会出现重叠拷贝,会导致结果不是我们预期的结果。

//使用我们自己的模拟的memcpy函数
int main()
{
  int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  my_memcpy(arr + 2, arr, 24);//预期出现结果为1 2 1 2 3 4 5 6 9 10
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);//实际出现结果
  }
  return 0;
}

  • 细节分析

2.4.2 模拟实现

//自我实现memmove的功能
void* my_memmove(void* destination, const void* source, size_t num) {
  //先判断destination和source是不是为空
  assert(destination && source);
  void* tmp = destination;
  //从前往后走
  if (destination < source) {
    while (num--) {
      *(char*)tmp = *(char*)source;
      ((char*)tmp)++;
      ((char*)source)++;
    }
  }
  //从后往前走
  else {
    while (num--) {
      *((char*)tmp + num) = *((char*)source + num);
    }
  }
  return destination;
}
int main() {
  int b[10] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(b, b + 3, 16);//b+3:是从4开始,预期结果:4,5,6,7,5,6,7,8,9,10
  for (int i = 0;i < 10;i++) {
    printf("%d ", b[i]);
  }
  printf("\n");
  int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(a+2, a, 24);//a+3:是从4开始,预期结果:1,2,3,1,2,3,4,8,9,10
  for (int i = 0;i < 10;i++) {
    printf("%d ", a[i]);
  }
}

相关文章
|
6月前
|
安全 C++
内存函数 memcpy 和 memmove 的讲解和模拟实现
内存函数 memcpy 和 memmove 的讲解和模拟实现
29 0
|
1月前
|
编译器 C++
memmove函数和memcpy函数的模拟实现
memmove函数和memcpy函数的模拟实现
9 1
|
1月前
|
编译器 C++
C++中memcpy函数的实现
C++中memcpy函数的实现
|
3月前
带你全面了解四大内存操作函数memset(),memcpy(),memmove(),memcmp()(附模拟实现)
带你全面了解四大内存操作函数memset(),memcpy(),memmove(),memcmp()(附模拟实现)
|
3月前
memmove内存拷贝函数
memmove内存拷贝函数
17 0
|
3月前
|
算法 编译器 C语言
memcpy内存拷贝函数
memcpy内存拷贝函数
37 0
【memcpy和memmove函数的详解】
我们知道,strcpy函数是拷贝字符串的,但是它并不能拷贝例如整型,结构体之类的东西,strcpy有一定的局限性,memcpy函数可以说涵盖了所有类型数据的拷贝。
|
11月前
|
IDE 编译器 开发工具
对于memcpy和memmove的区别,以及模拟实现memcpy和memmove
对于memcpy和memmove的区别,以及模拟实现memcpy和memmove
|
11月前
|
存储 编译器 C语言
【C进阶】——内存操作函数memcpy、memmove、memcmp、memset详解及其模拟实现
【C进阶】——内存操作函数memcpy、memmove、memcmp、memset详解及其模拟实现
155 0