memcopy在C语言中如何正确使用?

99ANYc3cd6
预计阅读时长 18 分钟
位置: 首页 C语言 正文

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

memcopy c语言
(图片来源网络,侵删)

memcpy 是什么?

memcpy 是 "memory copy"(内存拷贝)的缩写,它的核心功能是从一个内存位置(源地址)复制一块连续的内存数据到另一个内存位置(目标地址)。

关键特性:

  • 按字节拷贝:它以字节为单位进行复制,不关心数据的具体类型(是 intfloat 还是 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);

参数详解:

memcopy c语言
(图片来源网络,侵删)
  1. void *dest (Destination - 目标)

    • 这是一个指向目标内存块的 void 指针。
    • void* 是一种通用指针,可以指向任何类型的数据。
    • memcpy 会将 src 指向的 n 个字节的数据复制到 dest 指向的内存区域。
    • 调用者必须确保 dest 指向的内存区域足够大,至少能容纳 n 个字节,否则会导致缓冲区溢出,这是非常危险的。
  2. const void *src (Source - 源)

    • 这是一个指向源内存块的 const void 指针。
    • const 关键字表示 memcpy 不会修改 src 指向的内存中的内容,这是一个很好的编程实践,防止意外修改源数据。
    • 它也是一个通用指针,可以指向任何类型的数据。
  3. 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 内部会判断源地址和目标地址的大小关系:

  1. src < dest(源在目标的前面),它会从后向前拷贝,以避免覆盖还未拷贝的数据。
  2. 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 会使用更底层的、更快的汇编指令来优化拷贝过程,特别是处理大量数据时。


  1. 用途memcpy 是 C 语言中用于高效、快速拷贝内存块的标准函数。
  2. 原型void *memcpy(void *dest, const void *src, size_t n);
  3. 关键点
    • 按字节拷贝。
    • 绝不用于重叠内存,否则是未定义行为。
    • 调用者必须确保目标内存足够大。
  4. 替代品:当源和目标内存可能重叠时,必须使用 memmove
  5. 包含头文件#include <string.h>

掌握 memcpy 是 C 语言编程的一项基本功,理解它与 memmove 的区别,是写出健壮、高效代码的关键。

-- 展开阅读全文 --
头像
dmatrix在C语言中该如何实现与使用?
« 上一篇 04-23
dede如何去掉标题分页?
下一篇 » 04-23

相关文章

取消
微信二维码
支付宝二维码

目录[+]