memcpy 是 C 标准库中一个非常重要且常用的函数,用于内存块的拷贝。

(图片来源网络,侵删)
memcpy 是什么?
memcpy 是 "memory copy"(内存拷贝)的缩写,它的核心功能是从一个内存位置(源地址)复制一块连续的内存数据到另一个内存位置(目标地址)。
关键特性:
- 按字节拷贝:它以字节为单位进行复制,不关心数据的具体类型(是
int、float还是struct)。 - 高效:编译器会为
memcpy生成高度优化的机器码,可能使用 SIMD(如 SSE, AVX)指令集来实现快速复制。 - 不重叠:
memcpy的一个重要前提是源内存块和目标内存块不能重叠,如果重叠,拷贝的结果将是未定义的(Undefined Behavior, UB),对于可能重叠的内存拷贝,应该使用memmove函数。
函数原型
memcpy 函数在头文件 <string.h> 或 <cstring> (在 C++ 中) 中声明。
#include <string.h> // C // #include <cstring> // C++ void *memcpy(void *dest, const void *src, size_t n);
参数详解:

(图片来源网络,侵删)
-
void *dest(Destination - 目标)- 这是一个指向目标内存块的
void指针。 void*是一种通用指针,可以指向任何类型的数据。memcpy会将src指向的n个字节的数据复制到dest指向的内存区域。- 调用者必须确保
dest指向的内存区域足够大,至少能容纳n个字节,否则会导致缓冲区溢出,这是非常危险的。
- 这是一个指向目标内存块的
-
const void *src(Source - 源)- 这是一个指向源内存块的
const void指针。 const关键字表示memcpy不会修改src指向的内存中的内容,这是一个很好的编程实践,防止意外修改源数据。- 它也是一个通用指针,可以指向任何类型的数据。
- 这是一个指向源内存块的
-
size_t n(Number of bytes - 要拷贝的字节数)- 这是一个
size_t类型的无符号整数,表示要从src复制到dest的字节数。 - 使用
sizeof操作符通常是计算n的最佳方式。
- 这是一个
返回值:
void *:函数返回一个指向目标内存块dest的指针,这个返回值通常很有用,因为它可以让你链式调用函数,或者在复制后立即使用目标指针。
使用示例
下面通过几个例子来展示 memcpy 的用法。
示例 1:基本用法(拷贝整数数组)
#include <stdio.h>
#include <string.h>
int main() {
int src_arr[] = {10, 20, 30, 40, 50};
int dest_arr[5];
// 计算要拷贝的字节数:数组元素个数 * 每个元素的大小
size_t n = sizeof(src_arr);
// 将 src_arr 的内容拷贝到 dest_arr
memcpy(dest_arr, src_arr, n);
// 打印 dest_arr 的内容以验证
printf("Source array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", src_arr[i]);
}
printf("\n");
printf("Destination array after memcpy: ");
for (int i = 0; i < 5; i++) {
printf("%d ", dest_arr[i]);
}
printf("\n");
return 0;
}
输出:
Source array: 10 20 30 40 50
Destination array after memcpy: 10 20 30 40 50
示例 2:拷贝结构体
memcpy 对于拷贝复杂的数据结构(如结构体)非常方便,比逐个成员赋值要简洁高效。
#include <stdio.h>
#include <string.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
struct Person p1 = {"Alice", 30, 1.68f};
struct Person p2;
// 拷贝整个结构体
memcpy(&p2, &p1, sizeof(struct Person));
printf("Person 1: Name: %s, Age: %d, Height: %.2f\n", p1.name, p1.age, p1.height);
printf("Person 2: Name: %s, Age: %d, Height: %.2f\n", p2.name, p2.age, p2.height);
return 0;
}
输出:
Person 1: Name: Alice, Age: 30, Height: 1.68
Person 2: Name: Alice, Age: 30, Height: 1.68
示例 3:使用返回值
#include <stdio.h>
#include <string.h>
int main() {
char buffer[100] = "This is a test buffer.";
char *p;
// 将 "test" 复制到 buffer 的开头,并让 p 指向新的起始位置
p = (char *)memcpy(buffer, "test", 4);
// p 现在指向 buffer 的第一个字符 't'
printf("New buffer content: %s\n", p); // 输出: test buffer.
printf("Original buffer content: %s\n", buffer); // 输出相同
return 0;
}
memcpy vs. memmove
这是初学者最容易混淆的地方,两者功能相似,但在处理内存重叠时有本质区别。
| 特性 | memcpy |
memmove |
|---|---|---|
| 功能 | 拷贝不重叠的内存块 | 拷贝内存块(可以重叠) |
| 重叠处理 | 未定义行为 | 正确处理 |
| 性能 | 通常更快(因为它不需要处理重叠的复杂逻辑) | 通常稍慢(因为它需要额外的检查和逻辑来安全地处理重叠) |
| 使用场景 | 当你确定源和目标内存不重叠时 | 当源和目标内存可能重叠时,或者你不确定时 |
何时会发生内存重叠? 最常见的场景是在同一个数组内移动元素。
// 错误示例:使用 memcpy 处理重叠内存
char str[] = "Hello, world!";
// 想要把 "world!" 移动到开头
// memcpy(str, str + 7, 6); // 危险!未定义行为
// 结果可能是 "world! world!" 或 "Hello, world!" 或其他乱码,取决于编译器和优化
// 正确示例:使用 memmove 处理重叠内存
char str[] = "Hello, world!";
memmove(str, str + 7, 6); // 安全
printf("After memmove: %s\n", str); // 输出: world! world!
memmove 如何工作?
memmove 内部会判断源地址和目标地址的大小关系:
src<dest(源在目标的前面),它会从后向前拷贝,以避免覆盖还未拷贝的数据。src>=dest(源在目标的后面或相同位置),它和memcpy一样,从前向后拷贝。
手动实现 memcpy
为了更好地理解 memcpy 的工作原理,我们可以自己实现一个简化版本。
#include <stdio.h>
// 一个简单的 memcpy 实现
void* my_memcpy(void* dest, const void* src, size_t n) {
// 将 void* 指针转换为 char* 指针,因为 char 的大小是 1 字节,便于逐字节操作
char* d = (char*)dest;
const char* s = (const char*)src;
// 逐字节拷贝
for (size_t i = 0; i < n; i++) {
d[i] = s[i];
}
return dest; // 返回目标指针
}
int main() {
int src[] = {100, 200, 300};
int dest[3];
my_memcpy(dest, src, sizeof(src));
for (int i = 0; i < 3; i++) {
printf("%d ", dest[i]); // 输出: 100 200 300
}
printf("\n");
return 0;
}
这个实现虽然清晰,但效率不高,实际的 memcpy 会使用更底层的、更快的汇编指令来优化拷贝过程,特别是处理大量数据时。
- 用途:
memcpy是 C 语言中用于高效、快速拷贝内存块的标准函数。 - 原型:
void *memcpy(void *dest, const void *src, size_t n); - 关键点:
- 按字节拷贝。
- 绝不用于重叠内存,否则是未定义行为。
- 调用者必须确保目标内存足够大。
- 替代品:当源和目标内存可能重叠时,必须使用
memmove。 - 包含头文件:
#include <string.h>。
掌握 memcpy 是 C 语言编程的一项基本功,理解它与 memmove 的区别,是写出健壮、高效代码的关键。
