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

《C 语言内存函数深度剖析:从原理到实战(memcpy/memmove/memset/memcmp 全解析)》

在C语言中,内存操作是程序设计的核心环节之一。无论是数据拷贝、内存初始化还是内存比较,都离不开专门的内存函数。本文将详细解析C语言中最常用的4个内存函数:memcpymemmovememsetmemcmp,包括它们的使用方法、模拟实现及注意事项,帮助你彻底掌握内存操作的核心技巧。

1. memcpy:内存拷贝函数

memcpy是C语言中最基础的内存拷贝函数,用于将一块内存的数据复制到另一块内存。

1.1 函数原型

void * memcpy ( void * destination, const void * source, size_t num );

1.2 功能说明

  • source指向的内存位置开始,向后复制num个字节的数据到destination指向的内存位置。
  • 核心特点
    • 按字节拷贝,不依赖数据类型(因此参数用void*)。
    • 遇到'\0'不会停止(区别于字符串函数strcpy)。
    • 不处理重叠内存:如果sourcedestination的内存区域有重叠,复制结果是未定义的。

1.3 示例代码及解析

#include <stdio.h>
#include <string.h>int main()
{int arrx[] = { 1,2,3,4,5,6,7,8,9,10 };int arry[10] = { 0 };// 复制arr1的前20个字节到arr2(int占4字节,即复制前5个元素)memcpy(arrx, arry, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arry[i]); // 输出:1 2 3 4 5 0 0 0 0 0}return 0;
}

解析
int类型占4字节,20字节 = 5个int元素,因此memcpy(arry, arrx, 20)会将arr1的前5个元素(1-5)复制到arry,剩余元素保持初始值0。

1.4 模拟实现及解析

#include <assert.h> // 用于断言void * memcpy ( void * dst, const void * src, size_t count)
{void * ret = dst; // 保存目标地址,用于返回assert(dst && src); // 断言确保指针非空,增强安全性// 按字节拷贝(char*每次移动1字节)while (count--) {*(char *)dst = *(char *)src; // 强制转换为char*,逐个字节复制dst = (char *)dst + 1; // 目标指针后移1字节src = (char *)src + 1; // 源指针后移1字节}return ret; // 返回目标地址
}

实现要点

  • 使用char*强制转换:保证每次只操作1字节,适配任意数据类型。
  • 断言assert(dst && src):避免空指针传入导致的运行时错误。
  • 返回dst原始地址:方便链式调用(如memcpy(arr3, memcpy(arr2, arr1, 20), 20))。

1.5 注意事项

  • 禁止重叠内存:如果sourcedestination内存重叠(如memcpy(arr+2, arr, 20)),结果不可预测,此时需使用memmove
  • 拷贝长度单位:num的单位是字节,而非元素个数,需根据数据类型计算(如n个int元素n*sizeof(int)字节)。

2. memmove:处理重叠内存的拷贝函数

memmovememcpy的增强版,专门用于处理源内存和目标内存重叠的场景。

2.1 函数原型

void * memmove ( void * destination, const void * source, size_t num );

2.2 功能说明

  • memcpy功能类似,均用于复制num字节数据。
  • 核心区别:支持源内存和目标内存重叠,复制结果可预测。

2.3 示例代码及解析

#include <stdio.h>
#include <string.h>int main()
{int arrx[] = { 1,2,3,4,5,6,7,8,9,10 };// 将arr1的前20字节(1-5)复制到arr1+2位置(从索引2开始)memmove(arrx+2, arrx, 20); int i = 0;for (i = 0; i < 10; i++){printf("%d ", arrx[i]); // 输出:1 2 1 2 3 4 5 8 9 10}return 0;
}

解析
复制后,原索引0-4的元素(1-5)被拷贝到索引2-6的位置,因memmove处理重叠逻辑,避免了数据覆盖问题,最终结果正确。

2.4 模拟实现及解析


void * my_memmove(void * dest, void * src, size_t num)
{assert(dest && src);void * tmp = dest;while(num--){if(src < dest){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}else{*((char*)dest + num) = *((char*)src + num);}}return tmp;
}

实现核心逻辑

  • 正向拷贝:当目标内存在源内存左侧,或两者无重叠时,从起始位置依次向后复制(同memcpy)。
  • 反向拷贝:当目标内存在源内存右侧且重叠时,从末尾位置依次向前复制,避免覆盖尚未复制的源数据。

2.5 注意事项

  • 适用场景:所有内存拷贝场景(包括重叠和非重叠),建议优先使用memmove替代memcpy,避免重叠问题。
  • 性能差异:memmove因需判断重叠逻辑,性能略低于memcpy,但在现代编译器优化下差异可忽略。

3. memset:内存设置函数

memset用于将一块内存的每个字节设置为指定值,常用于内存初始化。

3.1 函数原型

void * memset ( void * ptr, int value, size_t num );

3.2 功能说明

  • ptr指向的内存中,前num个字节逐个设置为value(低8位有效)
  • 按字节操作,无论原始数据类型如何。

3.3 示例代码及解析

#include <stdio.h>
#include <string.h>int main ()
{char str[] = "hello world";// 将str的前6个字节设置为'x'memset(str, 'x', 6); printf(str); // 输出:xxxxxxworldreturn 0;
}

解析
'x'的ASCII值为120,memsetstr前6个字节均设置为120,因此"hello “被替换为"xxxxxx”。

3.4 常见误区及补充

  • 按字节设置的局限性
    memset按字节操作,因此对非char类型数组设置时需注意。例如:

    int arr[5] = {0};
    memset(arr, 1, sizeof(arr)); // 错误:每个字节被设为1,而非整个int为1
    

    上述代码中,每个int(4字节)会被设置为0x01010101(十进制16843009),而非预期的1。
    正确用法:仅用于设置0(memset(arr, 0, sizeof(arr)))或 -1memset(arr, -1, sizeof(arr)),因-1的补码为全1)。

  • 适用场景:初始化字符串、清空数组、设置标记位等(如将缓冲区填充为0)。

3.5 注意事项

  • value参数:实际使用的是value的低8位(即value & 0xFF),因此传入120120+256效果相同。
  • 长度计算:num为字节数,需用sizeof获取完整内存长度(如sizeof(arr))。

4. memcmp:内存比较函数

memcmp用于比较两块内存的前num个字节,返回比较结果。

4.1 函数原型

int memcmp ( const void * ptr1, const void * ptr2, size_t num );

4.2 功能说明

  • 比较ptr1ptr2指向的内存中,前num个字节的大小(按无符号字符比较)。
  • 返回值规则:
返回值含义
<0ptr1中第一个不匹配字节 < ptr2中对应字节(无符号值)
=0num个字节完全相同
>0ptr1中第一个不匹配字节 > ptr2中对应字节(无符号值)

4.3 示例代码及解析

#include <stdio.h>
#include <string.h>int main()
{char buffer1[] = "DWgaOtP12df0";char buffer2[] = "DWGAOTP12DF0";int 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);elseprintf("'%s' is the same as '%s'.\n", buffer1, buffer2);// 输出:'DWgaOtP12df0' is greater than 'DWGAOTP12DF0'.return 0;
}

解析
比较到第3个字符时,buffer1'g'(ASCII 103),buffer2'G'(ASCII 71),因此103>71,返回值>0。

4.4 与strcmp的区别

特性memcmpstrcmp
终止条件比较完num字节后停止遇到'\0'停止
比较范围精确控制比较字节数仅比较到字符串结束
适用场景任意内存块(包括二进制数据)仅字符串

4.5 注意事项

  • 无符号比较:即使比较有符号数据,memcmp也会按无符号字节值比较(如-1的无符号值为255)。
  • 完整比较:需确保num不超过两块内存的实际大小,避免越界访问。

总结

C语言的内存函数是数据操作的基础工具,掌握它们的特性和差异能显著提升程序的可靠性和效率:

  • memcpy:基础内存拷贝,不支持重叠。
  • memmove:增强版拷贝,支持重叠内存,建议优先使用。
  • memset:按字节设置内存,适合初始化(注意非字符类型的局限性)。
  • memcmp:比较指定长度的内存,适用于任意数据类型的比较。

合理使用这些函数,能让内存操作更简洁、安全,避免手动字节操作带来的错误。实际开发中,需根据场景选择合适的函数,并始终注意内存边界和重叠问题。

http://www.lryc.cn/news/609203.html

相关文章:

  • 使用ACK Serverless容器化部署大语言模型FastChat
  • 【十九、Javaweb-day19-Linux概述】
  • 我的世界模组进阶教程——伤害(1)
  • 每日面试题20:spring和spring boot的区别
  • Linux 文件与目录操作命令宝典
  • Unity_数据持久化_IXmlSerializable接口
  • 【视频内容创作】PR的关键帧动画
  • SQL157 更新记录(一)
  • linux下jvm之jstack的使用
  • 代码随想录day53图论4
  • Java 大视界 -- Java 大数据在智能教育学习资源个性化推荐与学习路径动态调整中的深度应用(378)
  • 【LLM】 BaseModel的作用
  • 【0基础PS】PS工具详解--文字工具
  • Shell脚本-变量是什么
  • 思途JSP学习 0802(项目完整流程)
  • Linux网络编程 --- 多路转接select
  • Unity JobSystem 与 BurstCompiler 资料
  • 2025.8.3
  • webrtv弱网-QualityScalerResource 源码分析及算法原理
  • 【大模型实战】向量数据库实战 - Chroma Milvus
  • Linux mount挂载选项详解(重点关注nosuid)
  • ESP32开发问题汇总
  • ZStack Cloud 5.3.40正式发布
  • 第15届蓝桥杯Scratch图形化国赛初/中级组2024年9月7日真题
  • Product Hunt 每日热榜 | 2025-08-02
  • 01数据结构-时间复杂度和空间复杂度
  • Petalinux 23.2 构建过程中常见下载错误及解决方法总结
  • ORA-12514:TNS: 监听程序当前无法识别连接描述符中请求的服务
  • 小白学OpenCV系列2-理解图像
  • 使用纯Docker命令搭建多服务环境(Linux版)