1. 主页
  2. 文档
  3. C语言教程
  4. C语言案例实战
  5. C语言编写自己的 memcpy() 和 memmove()

C语言编写自己的 memcpy() 和 memmove()

memcpy函数用于将数据块从源地址复制到目标地址。下面是它的原型。

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

这个想法是简单地将给定的地址类型转换为 char *(char 需要 1 个字节)。然后将数据从源一一复制到目标。下面是这个想法的实现。

// A C implementation of memcpy()
#include<stdio.h>
#include<string.h>

void myMemCpy(void *dest, void *src, size_t n)
{
// Typecast src and dest addresses to (char *)
char *csrc = (char *)src;
char *cdest = (char *)dest;

// Copy contents of src[] to dest[]
for (int i=0; i<n; i++)
cdest[i] = csrc[i];
}

// Driver program
int main()
{
char csrc[] = "GeeksforGeeks";
char cdest[100];
myMemCpy(cdest, csrc, strlen(csrc)+1);
printf("Copied string is %s", cdest);

int isrc[] = {10, 20, 30, 40, 50};
int n = sizeof(isrc)/sizeof(isrc[0]);
int idest[n], i;
myMemCpy(idest, isrc, sizeof(isrc));
printf("\nCopied array is ");
for (i=0; i<n; i++)
printf("%d ", idest[i]);
return 0;
}

输出:

Copied string is GeeksforGeeks
Copied array is 10 20 30 40 50

什么是memmove()

memmove() 与 memcpy() 类似,因为它也将数据从源复制到目标。当源地址和目标地址重叠时, memcpy() 会导致问题,因为 memcpy() 只是将数据从一个位置一个接一个地复制到另一个位置。例如考虑下面的程序。

// Sample program to show that memcpy() can lose data.
#include <stdio.h>
#include <string.h>
int main()
{
char csrc[100] = "52cxydh";
memcpy(csrc+5, csrc, strlen(csrc)+1);
printf("%s", csrc);
return 0;
}

输出:

52cxy52cxydh

由于输入地址重叠,上述程序会覆盖原始字符串,导致数据丢失。

// Sample program to show that memmove() is better than memcpy()
// when addresses overlap.
#include <stdio.h>
#include <string.h>
int main()
{
char csrc[100] = "52cxydh";
memmove(csrc+5, csrc, strlen(csrc)+1);
printf("%s", csrc);
return 0;
}

输出:

52cxy52cxydh

如何实现 memmove()?

这里的技巧是使用临时数组而不是直接从 src 复制到 dest。临时数组的使用对于处理源地址和目标地址重叠的情况很重要。

//C++ program to demonstrate implementation of memmove()
#include<stdio.h>
#include<string.h>

// A function to copy block of 'n' bytes from source
// address 'src' to destination address 'dest'.
void myMemMove(void *dest, void *src, size_t n)
{
// Typecast src and dest addresses to (char *)
char *csrc = (char *)src;
char *cdest = (char *)dest;

// Create a temporary array to hold data of src
char *temp = new char[n];

// Copy data from csrc[] to temp[]
for (int i=0; i<n; i++)
temp[i] = csrc[i];

// Copy data from temp[] to cdest[]
for (int i=0; i<n; i++)
cdest[i] = temp[i];

delete [] temp;
}

// Driver program
int main()
{
char csrc[100] = "52cxydh";
myMemMove(csrc+5, csrc, strlen(csrc)+1);
printf("%s", csrc);
return 0;
}

输出:

52cxy52cxydh

优化:
该算法效率低下(如果您使用临时数组,老实说时间会加倍)。除非确实不可能,否则应避免重复复制。

在这种情况下,虽然很容易通过选择复印方向来避免重复复印。事实上,这就是 memmove() 库函数的作用。

通过比较 src 和 dst 地址,您应该能够找到它们是否重叠。

– 如果它们不重叠,您可以向任何方向复制
– 如果它们重叠,找到 dest 的哪一端与源重叠,并相应地选择复制方向。
– 如果 dest 的开头重叠,则从头到尾复制
– 如果 dest 的结尾重叠,则从头到尾复制
– 另一种优化是按字长复制。只是要小心处理边界条件。
– 进一步的优化是使用向量指令进行复制,因为它们是连续的。

这篇文章对您有用吗?