浅谈C库函数——memcpy、memmove、memcmp、memset函数

简介: 浅谈C库函数——memcpy、memmove、memcmp、memset函数

一、memcpy

简要描述:内存拷贝,可以拷贝任何数据类型的对象。

1.函数声明

下面是 memcpy() 函数的声明

从存储区 source 复制 num 个字节的数据 拷贝到存储区 destnation。

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

2.函数参数

destnation -- 指向用于存储拷贝内容的目标数组,类型强制转换为 void* 指针。

source -- 指向要拷贝的数据源,类型强制转换为 void* 指针。

num -- 要被拷贝的数据字节数

3.函数返回值

返回目标存储区destnation的起始地址


4.用法  

#include<string.h>
#include<stdio.h>
void test1()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  //把arr1的前5个数据拷贝到arr2中
  memcpy(arr2, arr1, 20);
  printf("%d", arr2);
}
void test2()
{
  float arr3[] = { 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0 };
  float arr4[10] = { 0.0 };
  //把arr3的前3个数据拷贝到arr4中
  memcpy(arr4, arr3, 12);
  printf("%d", arr4);
}
int main()
{
  test1();
  test2();
  return 0;
}

5.模拟实现

void*的类型不能直接解引用和运算,所以我们考虑强制类型转换为char*的类型最合适。

#include<assert.h>
void* my_memcpy(void* dest, void* src, int num)
{
  void* ret = dest;
  assert(dest && src);
  while (num--)
  {
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  return ret;
}
void test3()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[10] = { 0 };
  my_memcpy(arr2, arr1, 20);
}
int main()
{
    test3();
    return 0;
}

那么问题来了,假设我们要把arr1中的1,2,3,4,5的数据拷贝到3,4,5,6,7的位置,即数组数据由1,2,3,4,5,6,7,8,9,10 变为 1,2,1,2,3,4,5,8,9,10 这样子的结果呢?


其实,我们会发现这样的结果打印的话(只需将上面的目标空间地址改为arr+2),得到的是1,2,1,2,1,2,1,8,9,10   这是因为,我们在开始拷贝的1,2时,已经把后面的3,4给覆盖掉了,往后拷贝就只能拿到1,2两个数字了。


image.png

image.png

所以,我们发现在内存重叠的时候,使用memcpy可能会出现意想不到的结果。


由此,我们需要知道memmove这个库函数了 。


二、memmove函数

简要描述:


在重叠内存块这方面,memmove() 是比 memcpy() 更安全的方法。如果目标区域和源区域有重叠的话,memmove() 能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后源区域的内容会被更改。如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。


1.函数声明

从存储区 source 复制 num 个字节的数据 拷贝到存储区 destnation

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

2.函数参数

destnation -- 指向用于存储拷贝内容的目标数组,类型强制转换为 void* 指针。

source -- 指向要拷贝的数据源,类型强制转换为 void* 指针。

num -- 要被拷贝的数据字节数。


3.函数返回值

返回目标存储区destnation的起始地址


4.用法

在内存重叠的前提下,将1,2,3,4,5 的数据拷贝到 3,4,5,6,7 的目标区域中

#include<stdio.h>
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  //将1 2 3 4 5五个数据拷贝到 3 4 5 6 7的位置
  memmove(arr1 + 2, arr1, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

5.模拟实现

思路:我们可以看到,之所以memcpy函数不能拷贝重叠区域的数据,是因为将源空间的数据从头开始拷贝导致的覆盖,所以针对这种情况,我们可以从后往前一一拷贝。


但是只有这一种情况吗?假设,我们调换刚才的源目两空间的位置,将3,4,5,6,7 拷贝到1,2,3,4,5 之中呢?我们就需要从前往后拷贝了。


image.png

所以我们要针对不同的情况使用不同的解决方案呢来解决。

image.png

如图可以得出,当dest在②or③的位置(dest < src),我们选择从后往前拷贝,当

dest在①的位置(dest >= src ),我们选择从前往后拷贝。

代码实现:

#include<assert.h>
void* my_memmove(void* dest, void* src, int num)
{
  void* ret = dest;
  assert(dest && src);
  if (dest < src)
  {
     //从前往后拷贝
      assert(dest && src);
      while (num--)
      {
        *(char*)dest = *(char*)src;
        dest = (char*)dest + 1;
        src = (char*)src + 1;
      }
  }
  else
  {
    //从后向前拷贝
    while (num--)
    {
      *((char*)dest + num) = *((char*)src + num);
    }
  }
  return ret;
}
#include<string.h>
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  //将1 2 3 4 5五个数据拷贝到 3 4 5 6 7的位置
  my_memmove(arr1, arr1 + 2, 20);
  int i = 0;
  for (i = 0; i < 10; i++)
  {
    printf("%d ", arr1[i]);
  }
  return 0;
}

三、memcmp函数

简要描述:与strcmp函数相比,memcmp函数功能则更强大,它能够与各种类型的数据进行比较。


1.函数声明

将 ptr1 指向的内存块的前 num 字节数与 ptr2 指向的内存块的前 num 字节数一一比较,如果它们都匹配,则返回零,如果不匹配,则返回一个不同于零的值,表示哪个值更大。它与strcmp不同,该函数在找到空字符后不会停止比较。

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

2.函数参数

ptr1 -- 指向内存块的指针。

ptr2 -- 指向内存块的指针。

num -- 要被比较的字节数。

3.函数返回值

如果返回值 < 0,则表示 str1 小于 str2。

如果返回值 > 0,则表示 str1 大于 str2。

如果返回值 = 0,则表示 str1 等于 str2。

3.用法

将arr1内存块的前28个字节与arr2内存块的前28个字节进行比较。

#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,5,6,6,6,6,6 };
  int ret = memcmp(arr1, arr2, 28);
  printf("%d", ret);
}

4.模拟实现

#include<stdio.h>
#include<string.h>
int my_memcmp(const void* e1, const void* e2, int num)
{
  int i = 0;
  for (i = 0; i < num; i++)
  {
    if (*((char*)e1 + i) > *((char*)e2 + i) || *((char*)e1 + i) < *((char*)e2 + i))
      return *((char*)e1 + i) - *((char*)e2 + i);  
  }
  return 0;
}
int main()
{
  int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
  int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
  int ret = my_memcmp(arr1,arr2,24);
  printf("%d", ret);
}

四、memset函数

简要描述:C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符

1.函数声明

void *memset(void *str, int c, size_t num);

2.函数参数

  • str -- 指向要填充的内存块。
  • c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
  • num -- 要被设置为该值的字符数。

3.函数返回值

该值返回一个指向存储区 str 的指针。

4.使用

#include<string.h>
#include<stdio.h>
int main()
{
  char arr1[] = "hello world!";
  memset(arr1, 'x', 5);
  printf("%s", arr1);
  return 0;
}

错误使用:

以下的代码可能认为是将arr2数组的所有元素都初始化为1,但是是错误的,打印%d,数据是十个很大的数字,以%p打印,我们发现其实是每个元素的4个字节都初始化为1。

故而不能使用这样的方式将数组所有元素初始化为1。

//error
#include<stdio.h>
int main()
{
  int arr2[10] = { 0 };
  //将数组的元素都初始化为1
  memset(arr2, 1, sizeof(arr2));
  int i;
  for (i = 0; i < 10; i++)
  {
    //printf("%d ", arr2[i]);
    printf("%p ", arr2[i]);
  }
  return 0;
}

以%d打印:

image.png

以%p打印:

image.png

目录
相关文章
|
2天前
|
C语言
C语言:内存函数(memcpy memmove memset memcmp使用)
C语言:内存函数(memcpy memmove memset memcmp使用)
|
1月前
|
编译器 C++
C++中memcpy函数的实现
C++中memcpy函数的实现
|
3月前
|
算法 编译器 C语言
memcpy内存拷贝函数
memcpy内存拷贝函数
37 0
|
9月前
|
存储 安全 编译器
【C语言】memcpy,memmove,memcmp,memset函数详解
【C语言】memcpy,memmove,memcmp,memset函数详解
【C语言】memcpy,memmove,memcmp,memset函数详解
|
4月前
|
存储 编译器 BI
内存函数​(memcpy、memmove、memset、memcmp)
内存函数​(memcpy、memmove、memset、memcmp)
|
4月前
|
存储 安全
内存函数(memcpy、memmove、memset、memcmp)你真的懂了吗?
内存函数(memcpy、memmove、memset、memcmp)你真的懂了吗?
52 0
内存函数(memcpy、memmove、memset、memcmp)你真的懂了吗?
|
8月前
|
编译器 C语言
你应该知道的C语言干货(5)(memset,memcpy,memmove,memcmp)
我们知道包含string.h头文件后,就可以使用memset,memcpy,memmove,memcmp这些库函数,接下来让我们了解他们。
46 0
|
9月前
|
编译器 C语言 C++
C进阶:内存函数memcpy, memmove, memcmp
C进阶:内存函数memcpy, memmove, memcmp
51 0
【memcpy和memmove函数的详解】
我们知道,strcpy函数是拷贝字符串的,但是它并不能拷贝例如整型,结构体之类的东西,strcpy有一定的局限性,memcpy函数可以说涵盖了所有类型数据的拷贝。