首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >内存内容操作函数详解[2]:memcpy()

内存内容操作函数详解[2]:memcpy()

作者头像
用户12001910
发布2026-01-20 16:43:38
发布2026-01-20 16:43:38
1230
举报

博主简介:byte轻骑兵,现就职于国内知名科技企业,专注于嵌入式系统研发。深耕 Android、Linux、RTOS、通信协议、AIoT、物联网及 C/C++ 等领域,乐于技术交流与分享。欢迎技术交流。 主页地址byte轻骑兵-CSDN博客 微信公众号:「嵌入式硬核研究所」 邮箱:byteqqb@163.com 声明:本文为「byte轻骑兵」原创文章,未经授权禁止任何形式转载。商业合作请联系作者授权。


在 C 语言的内存操作函数中,memcpy () 是使用频率极高的一个。它如同一位精准的内存搬运工,能够高效地将指定长度的数据从源内存区域复制到目标内存区域。无论是在系统编程、应用开发还是嵌入式领域,memcpy () 都扮演着不可或缺的角色。



一、函数简介

memcpy () 函数的核心功能是实现内存块之间的复制。它可以将源内存地址起始的连续 n 个字节的数据,完整地复制到目标内存地址起始的连续 n 个字节的空间中。与 strcpy () 等字符串复制函数不同,memcpy () 并不关心复制的数据内容,无论是字符、整数、结构体还是其他类型的数据,它都一视同仁,纯粹按照字节进行复制操作。​

这一特性使得 memcpy () 具有广泛的适用性,不仅可以用于复制字符串,还能用于复制数组、结构体等各种数据类型。在数据传输、缓存操作、对象复制等场景中,memcpy () 都能发挥重要作用。例如,在网络编程中,当需要将接收到的数据包从缓冲区复制到应用程序的数据结构中时,memcpy () 就能高效地完成这一任务。

​核心特性​:

  • ​不检查内存重叠​​:如果源地址(src)和目标地址(dest)重叠,行为未定义(应改用 memmove)。
  • ​不添加终止符​​:与 strcpy不同,memcpy不会自动添加 \0,仅按字节复制。
  • ​高性能​​:现代编译器可能内联优化,甚至使用 SIMD 指令(如 SSE、AVX)加速。

二、函数原型

memcpy () 函数的原型定义在 < string.h> 头文件中,具体如下:

代码语言:javascript
复制
void *memcpy(void *dest, const void *src, size_t n);

  • 参数说明:
    • dest:指向目标内存区域的指针,即复制操作的目的地。这是一个void*类型,意味着它可以接受任何类型的指针。
    • src:指向源内存区域的指针,即复制操作的来源。同样是一个void*类型,但使用const修饰符表明函数不会修改源数据。
    • n:需要复制的字节数,类型为size_t(一种无符号整数类型,通常用于表示大小和计数)。
  • 返回值:函数返回指向目标内存区域的指针,即dest的值。这种设计允许函数调用被嵌套在其他表达式中,提高了代码的灵活性。

三、函数实现(伪代码)

虽然不同编译器和标准库对memcpy()的具体实现可能有所不同,但基本思路是一致的。以下是一个简化的伪代码实现,帮助我们理解memcpy()的工作原理:

代码语言:javascript
复制
void *memcpy(void *dest, const void *src, size_t n) {
    // 将void指针转换为char指针,以便按字节操作
    char *d = (char *)dest;
    const char *s = (const char *)src;
    
    // 循环复制每个字节
    while (n-- > 0) {
        *d++ = *s++;
    }
    
    // 返回目标内存区域的起始地址
    return dest;
}

从伪代码可以看出,函数首先将 void 类型的指针转换为 char 类型的指针,因为 char 类型的长度为 1 字节,这样可以保证按照字节粒度进行数据复制。然后通过一个循环,每次复制一个字节的数据,并将源指针和目标指针分别向后移动一位,直到完成 n 个字节的复制。最后返回目标内存区域的起始地址。

在实际实现中,为了提高性能,库开发者通常会使用更高效的方法:

  • 字长优化:如果硬件平台支持,会使用更大的数据宽度(如4字节或8字节)进行复制,减少循环次数
  • 对齐处理:检查内存地址是否对齐,对齐的内存访问通常更快
  • 硬件指令:某些平台可能有专门的内存复制指令,库函数会利用这些指令
  • 向量化:使用SIMD指令集(如SSE、AVX)一次处理多个字节

四、使用场景

memcpy () 函数由于其按字节复制的特性,在很多场景下都能派上用场,以下是一些常见的使用场景:​

1. 数组复制​

当需要将一个数组中的部分或全部元素复制到另一个数组时,memcpy () 是一个很好的选择。例如,对于整型数组:

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    int src_arr[5] = {1, 2, 3, 4, 5};
    int dest_arr[5];
    
    // 复制整个数组,每个int占4字节,5个元素共20字节
    memcpy(dest_arr, src_arr, sizeof(src_arr));
    
    for (int i = 0; i < 5; i++) {
        printf("%d ", dest_arr[i]);
    }
    // 输出:1 2 3 4 5
    return 0;
}

2. 结构体复制​

在处理结构体时,使用 memcpy () 可以方便地将一个结构体变量的数据复制到另一个结构体变量中。需要注意的是,如果结构体中包含指针成员,复制的只是指针的值,而不是指针所指向的内存区域,这种情况称为浅拷贝

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

struct Student {
    char name[20];
    int age;
};

int main() {
    struct Student src_stu = {"Tom", 18};
    struct Student dest_stu;
    
    memcpy(&dest_stu, &src_stu, sizeof(struct Student));
    
    printf("Name: %s, Age: %d\n", dest_stu.name, dest_stu.age);
    // 输出:Name: Tom, Age: 18
    return 0;
}

3. 内存块复制​

在动态内存管理中,当需要对动态分配的内存块进行复制时,memcpy () 可以发挥作用。例如,将一块动态分配的内存中的数据复制到另一块动态分配的内存中:

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char *src = (char *)malloc(10 * sizeof(char));
    strcpy(src, "hello");
    
    char *dest = (char *)malloc(10 * sizeof(char));
    
    memcpy(dest, src, 10);
    
    printf("dest: %s\n", dest);
    // 输出:hello
    
    free(src);
    free(dest);
    return 0;
}

五、注意事项

虽然 memcpy () 函数使用起来方便高效,但在使用过程中如果不注意一些细节,可能会导致程序出现错误甚至崩溃,以下是一些需要特别注意的事项:​

1. 内存重叠问题​

memcpy () 函数不处理源内存区域和目标内存区域重叠的情况。如果 src 和 dest 所指向的内存区域有重叠,使用 memcpy () 可能会导致复制结果出错。例如:

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "abcdef";
    // 试图将"bcdef"复制到"abcdef"的前5个位置,内存重叠
    memcpy(str, str + 1, 5);
    printf("%s\n", str);
    // 可能输出"bcdef",也可能输出其他错误结果,具体取决于编译器实现
    return 0;
}

在这种情况下,应该使用 memmove () 函数,它能够正确处理内存重叠的情况。​

2. 内存边界问题​

在使用 memcpy () 时,必须确保目标内存区域有足够的空间来容纳要复制的数据,同时源内存区域的数据长度不能小于要复制的字节数,否则会导致内存越界访问,引发不可预测的错误,如程序崩溃、数据损坏等。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    char src[3] = "abc";
    char dest[2];
    
    // 源内存有3字节,目标内存只有2字节,复制3字节会导致目标内存越界
    memcpy(dest, src, 3);
    return 0;
}

目标数组 dest 的大小为 2 字节,而要复制 3 字节的数据,会超出 dest 的内存边界,导致错误。​

3. 指针有效性问题​

src 和 dest 指针必须是有效的,不能为 NULL,否则在解引用指针时会导致程序崩溃。在使用 memcpy () 之前,应该对指针的有效性进行检查。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    char *src = NULL;
    char dest[5];
    
    // src为NULL,使用memcpy()会导致程序崩溃
    memcpy(dest, src, 3);
    return 0;
}

六、示例代码

以下是一些更具代表性的 memcpy () 函数使用示例,涵盖不同的数据类型和场景。​

1. 复制字符串​

虽然 strcpy () 专门用于字符串复制,但 memcpy () 也可以实现相同的功能,只需指定字符串的长度(包括终止符 '\0')。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    char src_str[] = "Hello, World!";
    char dest_str[20];
    
    // 计算源字符串的长度(包括'\0')
    size_t len = strlen(src_str) + 1;
    memcpy(dest_str, src_str, len);
    
    printf("dest_str: %s\n", dest_str);
    // 输出:dest_str: Hello, World!
    return 0;
}

2. 复制二维数组​

对于二维数组,memcpy () 同样可以进行复制操作,只需计算好总的字节数。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

int main() {
    int src_arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
    int dest_arr[2][3];
    
    // 计算二维数组的总字节数
    size_t total_bytes = sizeof(src_arr);
    memcpy(dest_arr, src_arr, total_bytes);
    
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", dest_arr[i][j]);
        }
        printf("\n");
    }
    // 输出:
    // 1 2 3 
    // 4 5 6 
    return 0;
}

3. 复制结构体数组​

当需要复制结构体数组时,memcpy () 可以一次性复制整个数组,提高效率。

代码语言:javascript
复制
#include <stdio.h>
#include <string.h>

struct Product {
    int id;
    float price;
};

int main() {
    struct Product src_products[3] = {{1, 10.5f}, {2, 20.3f}, {3, 15.8f}};
    struct Product dest_products[3];
    
    // 复制结构体数组
    memcpy(dest_products, src_products, sizeof(src_products));
    
    for (int i = 0; i < 3; i++) {
        printf("ID: %d, Price: %.1f\n", dest_products[i].id, dest_products[i].price);
    }
    // 输出:
    // ID: 1, Price: 10.5
    // ID: 2, Price: 20.3
    // ID: 3, Price: 15.8
    return 0;
}

4. 结合动态内存使用​

在动态内存分配中,memcpy () 可以方便地复制动态分配的内存块。

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    int n = 5;
    int *src = (int *)malloc(n * sizeof(int));
    for (int i = 0; i < n; i++) {
        src[i] = i + 1;
    }
    
    int *dest = (int *)malloc(n * sizeof(int));
    memcpy(dest, src, n * sizeof(int));
    
    for (int i = 0; i < n; i++) {
        printf("%d ", dest[i]);
    }
    // 输出:1 2 3 4 5
    
    free(src);
    free(dest);
    return 0;
}

memcpy () 函数作为 C 语言中重要的内存复制工具,以其高效、通用的特性被广泛应用于各种编程场景。在使用 memcpy () 时,一定要注意内存重叠、内存边界和指针有效性等问题,以确保程序的正确性和稳定性。只有正确掌握和使用 memcpy () 函数,才能在内存操作中发挥其最大效能,提升程序的质量和效率。


本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-09-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、函数简介
  • 二、函数原型
  • 三、函数实现(伪代码)
  • 四、使用场景
  • 五、注意事项
  • 六、示例代码
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档