C函数之memcpy()函数用法

本文详细介绍了memcpy函数的功能和用法,包括其头文件引入方式、参数含义及返回值等。此外,还提供了具体的代码示例来展示如何使用memcpy进行内存复制,并强调了与strcpy的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

头文件:#include <string.h>

memcpy() 用来复制内存,其原型为:
    void * memcpy ( void * dest, const void * src, size_t num );

memcpy() 会复制 src 所指的内存内容的前 num 个字节到 dest 所指的内存地址上。

memcpy() 并不关心被复制的数据类型,只是逐字节地进行复制,这给函数的使用带来了很大的灵活性,可以面向任何数据类型进行复制。

需要注意的是:
  • dest 指针要分配足够的空间,也即大于等于 num 字节的空间。如果没有分配空间,会出现断错误。
  • dest 和 src 所指的内存空间不能重叠(如果发生了重叠,使用 memmove() 会更加安全)。

与 strcpy() 不同的是,memcpy() 会完整的复制 num 个字节,不会因为遇到“\0”而结束。

【返回值】返回指向 dest 的指针。注意返回的指针类型是 void,使用时一般要进行强制类型转换。


代码示例:
 
   
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #define N (10)
  5. int main()
  6. {
  7. char* p1 = "abcde";
  8. char* p2 = (char*)malloc(sizeof(char) * N);
  9. char* p3 = (char*)memcpy(p2, p1, N);
  10. printf("p2 = %s\np3 = %s\n", p2, p3);
  11. free(p2);
  12. p2 = NULL;
  13. p3 = NULL;
  14. system("pause");
  15. return 0;
  16. }
运行结果:
p2 = abcde
p3 = abcde

代码说明:
1) 代码首先定义p1,p2,p3三个指针,但略有不同,p1指向一个字符串字面值,给p2分配了10个字节的内存空间。

2) 指针p3通过函数memcpy直接指向了指针p2所指向的内存,也就是说指针p2、p3指向了同一块内存。然后打印p2,p3指向的内存值,结果是相同的。

3) 最后按照好的习惯释放p2,并把p3也置为NULL是为了防止再次访问p3指向的内存,导致野指针的发生。
<think>我们正在讨论C语言中的memcpy函数。根据引用[1],memcpy用于将一块内存区域的内容复制到另一块内存区域。其函数原型为: `void* memcpy(void *dest, const void *src, size_t count);` 参数说明: - dest:目标内存地址的指针。 - src:源内存地址的指针。 - count:要复制的字节数。 引用[2]指出,对于二进制数据(如结构体、数组等)必须使用memcpy(或memmove处理重叠内存区域)。而字符串处理则推荐使用strncpy或strcpy_s等安全函数。 引用[3]补充了strcpy和memcpy的区别:strcpy专门用于字符串复制,它会复制字符串内容直到遇到结束符'\0';而memcpy则按指定字节数复制内存,不关心内容。 下面我将详细解析memcpy的使用方法,包括参数说明、使用步骤、注意事项和代码示例。 ### 1. memcpy函数详解 #### 1.1 函数原型 ```c #include <string.h> void *memcpy(void *dest, const void *src, size_t n); ``` - **功能**:从源内存地址`src`复制`n`个字节到目标内存地址`dest`。 - **返回值**:目标内存地址`dest`的指针(可用于链式操作,但通常忽略)。 #### 1.2 参数说明 | 参数 | 类型 | 说明 | |--------|--------------|--------------------------| | `dest` | `void*` | 目标内存起始地址(需可写) | | `src` | `const void*`| 源内存起始地址(只读) | | `n` | `size_t` | 复制的字节数 | ### 2. 使用步骤 1. **包含头文件**:`#include <string.h>` 2. **检查内存重叠**:若`src`和`dest`内存重叠,应改用`memmove`(见注意事项)。 3. **计算字节数**:确定需复制的字节数(如结构体用`sizeof(struct)`,数组用`sizeof(array)`)。 4. **调用函数**:传递目标地址、源地址和字节数。 ### 3. 代码示例 #### 3.1 复制基本类型数组 ```c #include <stdio.h> #include <string.h> int main() { int src_arr[5] = {1, 2, 3, 4, 5}; int dest_arr[5]; size_t bytes_to_copy = 5 * sizeof(int); // 计算总字节数 memcpy(dest_arr, src_arr, bytes_to_copy); // 验证复制结果 for (int i = 0; i < 5; i++) { printf("dest_arr[%d] = %d\n", i, dest_arr[i]); } return 0; } ``` **输出**: ``` dest_arr[0] = 1 dest_arr[1] = 2 ... dest_arr[4] = 5 ``` #### 3.2 复制结构体 ```c #include <stdio.h> #include <string.h> typedef struct { int id; char name[20]; float score; } Student; int main() { Student stu1 = {101, "Alice", 95.5}; Student stu2; memcpy(&stu2, &stu1, sizeof(Student)); // 复制整个结构体 printf("stu2: %d, %s, %.1f\n", stu2.id, stu2.name, stu2.score); return 0; } ``` **输出**:`stu2: 101, Alice, 95.5` ### 4. 关键注意事项 1. **内存重叠问题** `memcpy`不处理源和目标内存重叠的情况(如`dest`在`src`和`src+n`之间)。此时应改用`memmove`(其内部会检测重叠并正确处理)[^2]。 **错误示例**: ```c char str[] = "Hello, World!"; memcpy(str + 5, str, 7); // 未定义行为!可能覆盖源数据 ``` **修正**: ```c memmove(str + 5, str, 7); // 安全处理重叠 ``` 2. **字节数计算** - 结构体:`sizeof(struct StructName)` - 数组:`sizeof(element_type) * element_count` 或 `sizeof(array_name)` - 避免硬编码字节数(如`memcpy(dest, src, 4)`),应使用`sizeof`确保可移植性。 3. **目标缓冲区大小** 目标内存必须足够容纳`n`字节,否则导致缓冲区溢出(可能崩溃或安全漏洞)。 **错误示例**: ```c char dest[5]; char src[] = "Hello, World!"; memcpy(dest, src, strlen(src) + 1); // dest溢出! ``` 4. **与`strcpy`的区别** | 特性 | `memcpy` | `strcpy` | |--------------|------------------------|------------------------| | **复制内容** | 任意二进制数据 | 字符串(以'\0'结尾) | | **结束符** | 不依赖'\0',按字节复制 | 遇到'\0'停止 | | **安全性** | 需手动控制长度 | 易溢出(应用`strncpy`)| ### 5. 应用场景总结 | 场景 | 推荐函数 | 原因 | |----------------------|----------------|--------------------------| | 复制数组/结构体 | `memcpy` | 精确控制字节数 | | 字符串复制(安全) | `strncpy` | 避免缓冲区溢出 | | 源和目标内存重叠 | `memmove` | 安全处理重叠区域 | | 需要自动追加'\0' | `strcpy_s` | 更安全的字符串操作 | ### 6. 常见问题解答 **Q1: 为什么`memcpy`比循环赋值更快?** A1: `memcpy`通常由编译器优化为底层指令(如x86的`REP MOVS`),可高效复制大块内存[^1]。 **Q2: 如何实现自定义`memcpy`?** A2: 基础实现(不考虑性能优化): ```c void* my_memcpy(void* dest, const void* src, size_t n) { char* d = (char*)dest; const char* s = (const char*)src; for (size_t i = 0; i < n; i++) { d[i] = s[i]; } return dest; } ``` **Q3: `memcpy`能复制动态分配的内存吗?** A3: 可以,只要目标内存足够: ```c int* src = malloc(100 * sizeof(int)); int* dest = malloc(100 * sizeof(int)); memcpy(dest, src, 100 * sizeof(int)); ``` --- ### 相关问题 1. 如何检测内存重叠? 2. `memcpy`和`memmove`在性能上有何差异? 3. 在Linux内核中`memcpy`是如何优化的? 4. 为什么`memcpy`复制结构体时可能引发字节对齐问题? 5. 如何用`memcpy`实现深拷贝链表?</think>### C语言 `memcpy` 函数使用指南 `memcpy` 是 C 语言标准库中用于**内存块复制**的核心函数,特别适合处理二进制数据(如数组、结构体等)。以下是详细使用指南: --- #### 一、函数原型与参数 ```c #include <string.h> // 必需头文件 void* memcpy(void *dest, const void *src, size_t count); ``` | 参数 | 类型 | 说明 | |----------|--------------|----------------------------------------------------------------------| | `dest` | `void*` | **目标内存地址**(复制的数据将存储在此) | | `src` | `const void*`| **源内存地址**(从此处复制数据,内容不会被修改) | | `count` | `size_t` | **复制的字节数**(需精确计算) | | **返回值** | `void*` | 返回目标地址 `dest`(可用于链式操作) | --- #### 二、核心特性 1. **二进制安全** 直接复制内存内容,不检查终止符(如 `\0`),适用于任意数据类型[^3]。 2. **高性能** 底层通常使用处理器指令优化(如 x86 的 `REP MOVSB`),效率高于手动循环复制[^1]。 3. **不处理内存重叠** 源地址 (`src`) 和目标地址 (`dest`) **内存区域不能重叠**,否则行为未定义(需重叠时改用 `memmove`)[^2]。 --- #### 三、使用步骤与示例 ##### 1. 复制基本类型数组 ```c int src_arr[5] = {10, 20, 30, 40, 50}; int dest_arr[5]; size_t bytes_to_copy = 5 * sizeof(int); // 计算总字节数 memcpy(dest_arr, src_arr, bytes_to_copy); ``` - **关键点**:通过 `sizeof(element_type) * element_count` 精确计算字节数。 ##### 2. 复制结构体 ```c typedef struct { int id; char name[20]; float score; } Student; Student stu1 = {101, "Alice", 95.5}; Student stu2; memcpy(&stu2, &stu1, sizeof(Student)); // 复制整个结构体 ``` - **关键点**:使用 `sizeof(struct_name)` 获取结构体完整大小。 ##### 3. 复制部分数据 ```c char src_str[] = "Hello, World!"; char dest_str[10]; // 复制前5个字符(不包括'\0') memcpy(dest_str, src_str, 5); dest_str[5] = '\0'; // 手动添加终止符 printf("%s", dest_str); // 输出 "Hello" ``` - **关键点**:非字符串数据需自行管理终止符。 --- #### 四、关键注意事项 1. **缓冲区溢出风险** ```c // 危险示例:目标缓冲区过小 char dest[5]; char src[] = "This is too long!"; memcpy(dest, src, sizeof(src)); // 缓冲区溢出! ``` **解决方案**:始终确保目标缓冲区足够容纳 `count` 字节。 2. **内存重叠问题** ```c char data[20] = "ABCDEFG"; // 错误:内存重叠(目标地址在源地址范围内) memcpy(data + 2, data, 5); // 未定义行为! // 正确:改用 memmove memmove(data + 2, data, 5); // 结果:ABABCDE ``` 3. **与字符串函数的区别** | 函数 | 适用场景 | 是否检查 `\0` | 是否安全 | |-----------|-------------------|---------------|----------| | `memcpy` | 二进制数据 | ❌ | 需手动控制长度 | | `strcpy` | 字符串 | ✅(遇 `\0` 停止) | 易溢出 | | `strncpy` | 字符串(安全版) | ✅ | 相对安全 | --- #### 五、典型应用场景 1. **网络数据传输** 序列化结构体后通过 `memcpy` 复制到发送缓冲区: ```c struct Packet packet = {...}; char buffer[sizeof(struct Packet)]; memcpy(buffer, &packet, sizeof(packet)); send(socket, buffer, sizeof(buffer), 0); ``` 2. **硬件寄存器操作** 复制寄存器配置块: ```c uint32_t reg_config[4] = {0xA1, 0xB2, 0xC3, 0xD4}; volatile uint32_t* hw_reg = (uint32_t*)0x40000000; memcpy((void*)hw_reg, reg_config, 4 * sizeof(uint32_t)); ``` 3. **图像处理** 复制图像像素数据: ```c uint8_t src_pixels[1024*768*3]; uint8_t dest_pixels[1024*768*3]; memcpy(dest_pixels, src_pixels, sizeof(src_pixels)); ``` --- #### 六、常见错误与调试 | 错误类型 | 现象 | 解决方法 | |------------------|--------------------|------------------------------| | 缓冲区不足 | 程序崩溃/数据损坏 | 检查 `sizeof(dest) >= count` | | 内存重叠 | 数据异常 | 改用 `memmove` | | 错误计算字节数 | 数据截断或多余复制 | 使用 `sizeof()` 精确计算 | | 未初始化目标内存 | 随机值污染 | 初始化目标缓冲区 | --- ### 相关问题 1. 如何安全地使用 `memcpy` 复制动态分配的内存? 2. `memcpy` 和 `memmove` 在性能上有何差异? 3. 在嵌入式系统中如何优化 `memcpy` 的性能? 4. 为什么复制结构体时可能需要考虑字节对齐问题? 5. 如何用 `memcpy` 实现深拷贝链表结构?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值