浅谈内存函数以及模拟实现

简介: 浅谈内存函数以及模拟实现

1.memcpy

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

函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。


接下来我们使用一下这个函数:

1. #include<string.h>
2. int main()
3. {
4.  int arr1[10] = { 0 };
5.  int arr2[] = { 1,2,3,4,5 };
6.  memcpy(arr1, arr2, 20);
7.  return 0;
8. }

经过调试之后可以发现arr1里面存放了1,2,3,4,5,这个就是memcpy的基本用法。

2.模拟实现memcpy

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<string.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
  assert(dest && src);
  while (num)
  {
  *(char*)dest = *(char*)src;
  dest = (char*)dest + 1;
  src = (char*)src + 1;
  num--;
  }
}
int main()
{
  int arr1[10] = { 0 };
  int arr2[] = { 1,2,3,4,5 };
  my_memcpy(arr1, arr2, 20);
  return 0;
}

src里面的内容是不会被改变的,所以使用const限制一下。num是拷贝的字节数,我们每次拷贝一个字节。先使用while循环,循环num次,每次num--,dest和src是void*的指针,我们强制类型转换为char*的指针,然后解引用,将src赋给dest,dest和src这时就应该往后走一个字节,因为强制类型转换是临时的,所以还是需要强制类型转换为char*,然后+1。

当我们想把1,2,3,4,5放到3,4,5,6,7的位置上时就会出现问题,当空间不重叠内存的时候使用memcpy是可以的,像这种不重叠内存的拷贝需要使用memmove。

int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  memcpy(arr + 2, arr, 20);
  return 0;
}

3.memmove

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


和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

如果源空间和目标空间出现重叠,就得使用memmove函数处理。


int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  memcpy(arr + 2, arr, 20);
  return 0;
}



使用memmove的话就能很好的解决上面的问题。

4.模拟实现memmove

我们先来想一下,如果有一个数组:

int arr[]={1,2,3,4,5,6,7,8,9,10};


假如我们要把1,2,3,4,5拷贝到3,4,5,6,7这个位置上去,使得数组变成:

int arr[]={1,2,1,2,3,4,5,8,9,10};

那我们就需要先将5放在7的位置上,4放在6的位置上,以此类推,才能达到效果,也就是从后向前拷贝。


如果我们要把3,4,5,6,7拷贝到1,2,3,4,5这个位置上,就要先将3放到1的位置上,以此类推,也就是从前往后拷贝。

所以说拷贝的方式会根据需求而改变。

那么什么时候从前往后拷贝,什么时候从后往前拷贝呢?

还是用一个数组举例:

int arr[]={1,2,3,4,5,6,7,8,9,10};

如果dest这块空间在src这块空间的左边且重叠,也就是要把高地址的数据拷贝到低地址处,就需要从前往后拷贝。


如果dest这块空间在src这块空间的右边且重叠,也就是要把低地址的数据拷贝到高地址处,就需要从后往前拷贝。


如果dest这块空间和src这块空间不重叠,不管从前往后还是从后往前都是可以的,当然是在空间充足的情况下,不能越界。

一开始我们还是需要保存起始位置的地址,用作返回值。使用if判断dest是否小于src,如果小于则进入for循环,循环sz次,这里的方法是跟上面的memcpy一样的。当dest>src时,我们使用while循环比较方便,因为我们从后向前拷贝是先将后面的数据开始,所以当我们将条件设置成sz--时,由于是后置--,也是循环sz次,但是*((char*)src + sz)就刚好能够拿出最后一个数据拷贝到*((char*)dest + sz),当然使用for循环也是可以的。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
void* my_memmove(void* dest,const void* src,size_t sz)
{
  assert(dest && src);
  void* ret = dest;
  if (dest < src)//从前向后
  {
    for (int i = 0; i < sz; i++)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest+1;
      src = (char*)src + 1;
    }
  }
  else//从后向前
  {
    while (sz--)
    {
      *((char*)dest + sz) = *((char*)src + sz);
    }
  }
  return ret;
}
int main()
{
  int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
  my_memmove(arr, arr + 2, 20);
  for (int i = 0; i < 10; i++)
  {
    printf("%d ", arr[i]);
  }
  return 0;
}

5.memset

void * memset ( void * ptr, int value, size_t num );

这个函数就是将ptr指向的前num个字节的数据设置成指定的值(value),再把起始位置返回。


下面是memset的使用:

int main()
{
  char arr[] = "hello world";
  memset(arr + 6, 'x', 3);
  printf("%s\n", arr);
  return 0;
}

但是要注意的是num的单位是字节。如果我们想要把这个数组的内容设置成10个1,像下面这样就已经出错了,这样是把每个字节都设置成1,而不是整型。

1. int main()
2. {
3.  int arr[10] = { 0 };
4.  memset(arr, 1, 40);
5.  return 0;
6. }


6.memcmp

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

memcmp这个函数就是比较两块空间的前num个字节的内容,如果一样则返回0,小于则返回<0,大于则返回>0。需要注意的是在比较时也是一对字节进行比较,和strcmp的区别就是strcmp是进行字符的比较,而memcmp是进行内存块的比较。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[] = { 1,2,3,4 };
  int ret=memcmp(arr1, arr2, 16);
  printf("%d ", ret);
  return 0;
}

相关文章
|
2月前
10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。
10分钟让你学会内存函数:memcpy,memmove,memset,memcmp的用法及模拟实现。
34 2
|
2月前
|
存储 C语言
C语言学习记录——动态内存函数介绍(malloc、free、calloc、realloc)
C语言学习记录——动态内存函数介绍(malloc、free、calloc、realloc)
42 1
|
2月前
|
C语言
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
【C语言】:动态内存管理函数malloc,calloc,realloc和free的介绍的介绍
36 0
|
22小时前
|
C语言
【C语言篇】字符和字符串以及内存函数详细介绍与模拟实现(下篇)
perror函数打印完参数部分的字符串后,再打印⼀个冒号和⼀个空格,再打印错误信息。
|
23小时前
|
存储 安全 编译器
【C语言篇】字符和字符串以及内存函数的详细介绍与模拟实现(上篇)
当然可以用scanf和printf输入输出,这里在之前【C语言篇】scanf和printf万字超详细介绍(基本加拓展用法)已经讲过了,这里就不再赘述,主要介绍只针对字符的函数.
|
6天前
【C初阶】内存函数:memcpy+memmove+memset+memcmp
【C初阶】内存函数:memcpy+memmove+memset+memcmp
|
1月前
|
存储 缓存 C语言
【C语言】字符函数,字符串函数,内存函数
C语言中的字符串函数和内存函数
24 0
【C语言】字符函数,字符串函数,内存函数
|
2月前
|
C语言
字符串和内存函数(1)
字符串和内存函数(1)
31 7
|
2月前
|
C语言
【C语言】:4大内存函数
【C语言】:4大内存函数
18 2
|
2月前
字符串和内存函数(2)
字符串和内存函数(2)
30 5