C函数之memcpy()函数用法

本文详细介绍了C语言中的memcpy函数,包括其函数原型、功能、头文件和返回值。memcpy用于将源内存区域的数据复制到目标内存区域,不检查两者是否重叠,且会完整复制指定字节数,不同于strcpy仅适用于字符串复制。文中通过实例展示了如何使用memcpy复制字符串、特定位置的字符序列以及覆盖原有数据。同时,文章还对比了memcpy和strcpy的区别,强调了在使用memcpy时注意的参数设置,例如在整型数组复制时需考虑sizeof运算。

1 基本介绍

1.1 函数原型
void *memcpy(void*dest, const void *src, size_t n);
函数原型
void *memcpy(void *dest, const void *src, int count)
{
    void *ptr = dest;
    if (NULL == dest || NULL == src || count <= 0)
    {
        return NULL;
    }
    while (count--)
    *(char *)dest++ = *(char *)src++;
    return ptr;
}
1.2 功能

由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内,是用指针进行操作的

1.3头文件

#include<string.h>

1.4 返回值

函数返回一个指向dest的指针。

2 说明

  1. source和destin所指内存区域不能重叠,函数返回指向destin的指针。
  2. 与strcpy相比,memcpy并不是遇到’\0’就结束,而是一定会拷贝完n个字节。
  3. strcpy提供了字符串的复制。即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符。
  4. memcpy提供了一般内存的复制。即memcpy对于需要复制的内容没有限制,因此用途更广。
  5. 整型数组复制的时候,第三个参数应该为sizeof(int) * size (同理会有sizeof(long int) * size, sizeof(Node) * size…)
  6. 在memcpy的源码中可以看到,复制是以char为地址单元进行的,所以第三个参数只传进size的话,那么将会只复制前size个char地址单元的数据。
int main()
{
    int a[4] = {1, 2, 3, 100};
    int b[3] = {4, 5, 6};
    memcpy(b, a, 4); // 通常int = 4 * char, 所以复制的结果为b = {1, 5, 6}, 只复制了前4个char地址的内容
    memcpy(b, a, sizeof(int) * 3); // 这里的sizeof(int) * 3 == 12, 所以复制结果为b = {1, 2, 3};
    for (int i = 0; i < 3; i++) {
        printf("%d ", b[i]);
    }
    return 0;
}

3 使用例子

3.1 作用:将s中的字符串复制到字符数组d中。
  #include<stdio.h>

  #include<string.h>

  intmain()

  {
	  char*s="Golden Global View";
	  chard[20];
	  clrscr();
	  memcpy(d,s,strlen(s));
	  d[strlen(s)]='\0';//因为从d[0]开始复制,总长度为strlen(s),d[strlen(s)]置为结束符
	  printf("%s",d);
	  getchar();
  return0;

  }
  输出结果:GoldenGlobal View

3.2 作用:将s中第14个字符开始的4个连续字符复制到d中。(从0开始)
  #include<string.h>
  int main()
  {
	  char*s="Golden Global View";
	  chard[20];
	  memcpy(d,s+14,4);//从第14个字符(V)开始复制,连续复制4个字符(View)
	  //memcpy(d,s+14*sizeof(char),4*sizeof(char));也可
	  d[4]='\0';
	  printf("%s",d);
	  getchar();
	  return0;
  }
3.3 作用:复制后覆盖原有部分数据
  #include<stdio.h>
  #include<string.h>
  int main(void)
  {
	  charsrc[] = "******************************";
	  chardest[] = "abcdefghijlkmnopqrstuvwxyz0123as6";
	  printf("destinationbefore memcpy: %s\n", dest);
	  memcpy(dest,src, strlen(src));
	  printf("destinationafter memcpy: %s\n", dest);
	  return0;
  }

  输出结果:

  destinationbefore memcpy:abcdefghijlkmnopqrstuvwxyz0123as6

  destinationafter memcpy: ******************************as6

输出结果: View

参考链接:

https://blog.csdn.net/frank_jb/article/details/51698948?hmsr=toutiao.io&utm_campaign=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
<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` 实现深拷贝链表结构?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值