当前位置: 首页 > news >正文

C 语言内存函数全解析:从 memcpy 到 memcmp 的使用与模拟实现

目录:

  1. memcpy的使用和模拟实现
  2. memmove的使用和模拟实现
  3. memset函数的使用
  4. memcpm函数的使用

1.memcpy的使用和模拟实现

1.1memcpy函数的使用:

//函数原型 void* memcpy(void* destination, const void* source, size_t num);

👉void *是“万能入口”,char *才是“字节搬运工”——按字节逐个拷贝

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。

memcpy函数不支持内存重叠拷贝

使用举例:

#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[10] = { 0 }; memcpy(arr2, arr1, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr2[i]); } return 0; }

输出结果:

1 2 3 4 5 0 0 0 0 0

注意:对于重叠的内存,交给memmove来处理。

1.2memcpy函数的模拟实现:

#include <assert.h> void* my_memcpy(void* dest, void* src, size_t num) { assert(dest && src); void* ret = dest; while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return ret; }

memcpy之所以要把 void *转换成 char *,是因为

✅ void *不能做指针运算

✅ 字节拷贝必须以「1 字节」为单位

✅ 只有 char *能保证跨平台、标准、安全

加入主函数测试:

#include <stdio.h> #include <assert.h> void* my_memcpy(void* dest, void* src, size_t num) { assert(dest && src); void* ret = dest; while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return ret; } int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; my_memcpy(arr1, arr1 + 1, 5 * sizeof(int)); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }

测试结果:

2 3 4 5 6 6 7 8 9 10

2.memmove的使用和模拟实现

2.1memmove的使用

//函数原型 void * memmove ( void * destination, const void * source, size_t num );

👉void *是“万能入口”,char *才是“字节搬运工”——按字节逐个拷贝

  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的
  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

使用举例:

#include <stdio.h> #include <string.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr1 + 2, arr1, 20); int i = 0; for (i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }

输出结果:

1 2 1 2 3 4 5 8 9 10

2.2memmove的模拟实现

#include <assert.h> void* my_memmove(void* dest,const void* src, size_t num) { assert(dest && src); void* ret = dest; if (dest < src) { //前往后 while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { //后往前 while (num--) { *((char*)dest + num) = *((char*)src + num); } } return ret; }

加入主函数测试:

#include <stdio.h> #include <assert.h> void* my_memmove(void* dest,const void* src, size_t num) { assert(dest && src); void* ret = dest; if (dest < src) { //前往后 while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { //后往前 while (num--) { *((char*)dest + num) = *((char*)src + num); } } return ret; } int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1, arr1 + 1, 5 * sizeof(int)); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }

测试结果:

2 3 4 5 6 6 7 8 9 10

常见误区:

  • ❌ 误区 1:直接强转为 int*
  • ❌ 误区 2:认为 void *可以直接用

总结:

✅memmove 的核心不是“拷贝”,而是“安全地按字节拷贝

✅void* 只负责“接收”,不负责“运算”

唯一正确的中间类型是 char*


3.memset函数的使用

//函数原型 void * memset ( void * ptr, int value, size_t num );

memset是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。

使用举例:

#include <stdio.h> #include <string.h> int main() { char str[] = "hello world"; memset(str, 'x', 6); printf("%s\n", str); return 0; }

输出结果:

xxxxxxworld

4.memcmp函数的使用

//函数原型 int memcmp ( const void * ptr1, const void * ptr2, size_t num );

比较从ptr1和ptr2指针指向的位置开始,向后的num个字节

返回值如下:

使用举例:

#include <stdio.h> #include <string.h> int main() { char buffer1[] = "DWgaOtP12df0"; char buffer2[] = "DWGAOTP12DF0"; int n; n = memcmp(buffer1, buffer2, sizeof(buffer1)); if (n > 0) printf("'%s' is greater than '%s'.\n", buffer1, buffer2); else if (n < 0) printf("'%s' is less than '%s'.\n", buffer1, buffer2); else printf("'%s' is the same as '%s'.\n", buffer1, buffer2); return 0; }

本专栏C语言持续更新中,欢迎关注!

http://www.jsqmd.com/news/516825/

相关文章:

  • Qwen3-32B开源大模型教程:百度开发者关注的transformers模型加载最佳实践
  • Texlive新手避坑指南:如何彻底解决xelatex编译中的字体缺失问题(以AdobeSongStd-Light为例)
  • 联邦学习实战:如何用语义通信解决自动驾驶中的非IID数据问题?
  • 你以为在靠理财逆袭,其实在被“盯盘”榨干时薪
  • 2026哈尔滨考研培训公司课程费用,哪家性价比高呢 - 工业推荐榜
  • antv x6实战:基于类型校验的自定义连接桩与智能连线规则设计
  • 【LoRA实战】精准定位MoE模型Router层的target_modules配置指南
  • Python虚拟环境里pip总出问题?可能是你的包路径没配好(附完整排查流程)
  • FineReport报表设计器与服务器详解:如何高效搭建本地开发环境
  • 保姆级避坑指南:Windows/Mac双平台搞定GraphRAG 2.0.0本地部署(附Ollama模型选择建议)
  • 新书上市 | 陶哲轩强推!这可能是今年最值得读的一本数学科普书!
  • VSCode配置PyTorch开发环境:从CUDA版本检查到镜像源加速(避坑指南)
  • 2026年济宁泥层界面仪性价比排名,探讨价格、可信度及适用场景 - myqiye
  • Apache DolphinScheduler 3.1.8 从入门到精通:部署、核心功能与实战告警配置全解析
  • QGC源码编译避坑指南:从git submodule卡死到QT工程配置
  • 用Python手把手实现矩阵分解推荐算法(附完整代码与数据集)
  • 2026 NMN靠谱品牌推荐,十大热门牌子测评,安全有效才是真抗衰 - 速递信息
  • Android Banner库避坑指南:网络图片加载、内存泄漏与生命周期那些事儿
  • 大屏iframe通信避坑指南:Vue3中如何确保postMessage100%送达
  • 灵感画廊部署教程:Ubuntu 22.04 LTS + NVIDIA 535驱动 + SDXL 1.0全兼容
  • 独立按键硬件设计与软件消抖全栈实现
  • RAGFlow本地开发避坑指南:解决PyCharm中常见安装错误
  • PTE成为留学英国新选择,英国高校对PTE认可度如何?
  • 2026年车位代理销售服务选哪家,成都这些公司值得关注 - 工业品牌热点
  • 嵌入式DMA原理与工程实践:从硬件机制到串口/ADC应用
  • 聊聊2026年常州办公家具选购,欧圣办公家具稳定性好吗 - 工业设备
  • 3分钟解锁付费内容:Bypass Paywalls Clean浏览器扩展使用全攻略
  • JavaScript代码保护实战:5款加密混淆工具横向评测(附真实案例对比)
  • 从休闲爆款到技术实现:拆解水排序游戏背后的 CocosCreator + Spine 动画系统设计
  • 解锁Matlab Online:两种主流认证路径详解与实战体验