文章目录
- 📋前言
- 一、memcpy函数基础
-
- [1.1 函数原型](#1.1 函数原型)
- [1.2 参数说明](#1.2 参数说明)
- [2.3 返回值](#2.3 返回值)
- [2.4 核心作用](#2.4 核心作用)
- 二、基础使用示例
- [三、memcpy vs strcpy:核心区别](#三、memcpy vs strcpy:核心区别)
- 四、memcpy底层原理(简易实现)
-
- [4.1 朴素实现(字节拷贝)](#4.1 朴素实现(字节拷贝))
- [4.2 优化版(考虑对齐)](#4.2 优化版(考虑对齐))
- 五、重要注意事项(避坑指南)
-
- [5.1 memcpy的致命坑点:内存重叠](#5.1 memcpy的致命坑点:内存重叠)
-
- 错误示例(内存重叠)
- [解决方案:使用 memmove](#解决方案:使用 memmove)
- [5.2 长度参数的单位是字节](#5.2 长度参数的单位是字节)
- [5.3 目标内存必须足够大](#5.3 目标内存必须足够大)
- [5.4 memcpy注意事项汇总](#5.4 memcpy注意事项汇总)
- 📝全文总结
📋前言
在C语言开发中,内存操作是核心技能之一,而memcpy作为最常用的内存拷贝函数,几乎是每个开发者的必备工具。它不仅能拷贝普通数组,还能处理任意类型的数据,相比字符串拷贝函数strcpy,通用性更强。
今天我们就从函数原型、核心用法、底层原理、与strcpy的区别、常见坑点 五个维度,彻底吃透memcpy。
一、memcpy函数基础
1.1 函数原型
memcpy是C标准库<string.h>中的函数,原型如下:
c
void *memcpy(void *dest, const void *src, size_t n);
1.2 参数说明
| 参数 | 含义 |
|---|---|
dest |
目标内存地址(拷贝数据的目的地) |
src |
源内存地址(要拷贝的数据来源) |
n |
要拷贝的字节数 (类型为size_t,无符号整数) |
2.3 返回值
返回目标内存地址dest的起始指针,方便链式调用。
2.4 核心作用
从源内存地址拷贝n个字节到目标内存地址,不限制数据类型(int、char、结构体、数组等都支持)。
二、基础使用示例
先看几个最常用的场景,快速上手memcpy。
示例1:拷贝整型数组
c
#include <stdio.h>
#include <string.h> // 必须包含头文件
int main()
{
// 源数组
int src[] = { 10, 20, 30, 40, 50 };
// 目标数组(提前分配足够空间)
int dest[5] = { 0 };
// 拷贝全部元素:数组总字节数 = 元素个数 * 单个元素字节数
memcpy(dest, src, sizeof(src));
// 打印验证
for (int i = 0; i < 5; i++)
{
printf("%d ", dest[i]);
}
// 输出:10 20 30 40 50
return 0;
}
示例2:拷贝结构体
memcpy支持自定义类型,这是strcpy做不到的:
c
#include <stdio.h>
#include <string.h> // 必须包含头文件
// 自定义结构体
struct Student
{
char name[20];
int age;
float score;
};
int main()
{
struct Student stu1 = { "张三", 18, 95.5 };
struct Student stu2;
// 直接拷贝整个结构体
memcpy(&stu2, &stu1, sizeof(struct Student));
printf("姓名:%s\n", stu2.name);
printf("年龄:%d\n", stu2.age);
printf("分数:%.1f\n", stu2.score);
// 输出:张三 18 95.5
return 0;
}
示例3:拷贝部分数据
c
#include <stdio.h>
#include <string.h> // 必须包含头文件
int main()
{
char buffer[100] = { 0 };
char data[] = "Important Data";
// 只拷贝前 9 个字符
memcpy(buffer, data, 9);
printf("%s\n", buffer);
// 输出:Important
return 0;
}
三、memcpy vs strcpy:核心区别
很多人会混淆memcpy和strcpy,它们有本质区别:
| 特性 | memcpy | strcpy |
|---|---|---|
| 拷贝对象 | 任意类型数据(字节级拷贝) | 仅字符串 (以\0结尾) |
| 拷贝长度 | 手动指定n个字节 |
自动拷贝到\0为止 |
| 结束标志 | 不关心\0,严格拷贝n个字节 |
遇到\0停止 |
| 安全性 | 需手动控制长度,避免越界 | 目标空间不足会溢出 |
重点: memcpy不会关心\0,它只是机械地复制字节。如果你用memcpy复制字符串但不复制结尾的\0,则目标字符串没有终止符。
一句话总结:
- 拷贝字符串 用
strcpy;- 拷贝非字符串/任意内存数据 用
memcpy。
四、memcpy底层原理(简易实现)
理解memcpy的实现有助于更好地使用它。一个简单但高效的实现通常采用字节拷贝 + 字对齐优化。
4.1 朴素实现(字节拷贝)
c
// 简易版memcpy实现
void* my_memcpy(void* dest, const void* src, size_t n)
{
// 空指针校验
if (dest == NULL || src == NULL)
{
return NULL;
}
// 转为char*,逐字节拷贝(char占1字节,最适合字节操作)
char* p_dest = (char*)dest;
const char* p_src = (const char*)src;
// 逐字节拷贝n次
while (n--)
{
*p_dest++ = *p_src++;
}
return dest;
}
核心原理 :
memcpy是逐字节拷贝,这也是它能支持任意数据类型的原因------不关心数据类型,只操作底层字节。
4.2 优化版(考虑对齐)
现代C库的memcpy实现会:
-
先按字节拷贝,直到目标地址对齐到字边界(如4字节或8字节)
-
然后按字(
int或long)批量拷贝 -
最后处理剩余字节
这种优化能显著提升拷贝大块内存的性能。
五、重要注意事项(避坑指南)
5.1 memcpy的致命坑点:内存重叠
这是memcpy最容易踩的坑!标准库的memcpy不处理内存重叠问题,如果源地址和目标地址有重叠,会导致数据错乱。
错误示例(内存重叠)
c
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
// 目标地址 = arr+1,源地址 = arr,内存重叠!
memcpy(arr + 1, arr, 3);
printf("%s\n", arr);
// 预期输出:aabcef
// 实际输出:aaaaef(数据被覆盖,结果异常)
return 0;
}
解决方案:使用 memmove
C语言提供了memmove函数,专门处理内存重叠场景 ,用法和memcpy完全一致:
c
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
// 替换为memmove,自动处理内存重叠
memmove(arr + 1, arr, 3);
printf("%s\n", arr);
// 正确输出:aabcef
return 0;
}
开发建议 :
不确定内存是否重叠时,直接用memmove,安全性更高;确定无重叠时,用memcpy(性能略优)。
5.2 长度参数的单位是字节
c
#include <stdio.h>
#include <string.h>
void Print(int* dest, int size)
{
for (int i = 0; i < size; i++)
{
printf("%d ", dest[i]);
}
printf("\n");
}
int main()
{
int arr[] = { 10, 20, 30, 40, 50 };
int dest[5] = { 0 };
// 错误:这里只拷贝了 5 字节,而不是 5 个整数
memcpy(dest, arr, 5);
// 打印验证
Print(dest, 5);
// 输出:10 20 0 0 0
// 正确:拷贝整个数组
memcpy(dest, arr, sizeof(arr));
// 打印验证
Print(dest, 5);
// 输出:10 20 30 40 50
return 0;
}
5.3 目标内存必须足够大
c
#include <stdio.h>
#include <string.h>
int main()
{
char small[5];
char big[] = "This is a long string";
// 报错:严重溢出!small 只有 5 字节
memcpy(small, big, sizeof(big));
return 0;
}
5.4 memcpy注意事项汇总
- 必须包含头文件 :
<string.h>,否则会报警告。 - 目标空间必须足够大:否则会造成内存越界,导致程序崩溃。
- 源地址加const修饰 :标准库中
src是const void*,防止源数据被修改。 - 空指针校验 :实际开发中,建议先判断
dest和src是否为NULL。 - 长度单位是字节 :拷贝数组时,用
sizeof(数组名)获取总字节数,不要手动写死。
📝全文总结
memcpy是字节级内存拷贝函数,支持任意数据类型,是C语言内存操作的核心工具。- 用法:
memcpy(目标地址, 源地址, 拷贝字节数)。 - 与
strcpy的区别:memcpy通用,strcpy仅用于字符串。 - 核心坑点:内存重叠 ,此时用
memmove替代。 - 开发原则:无重叠用
memcpy,有重叠用memmove,永远保证目标空间足够。
掌握memcpy,你的C语言内存操作能力会提升一个档次,快去动手试试吧!