详细介绍:C 语言内存操作函数:memcpy、memmove、memset、memcmp 详解

news/2025/9/22 22:29:32/文章来源:https://www.cnblogs.com/yxysuanfa/p/19106243

核心环节之一,memcpy、memmove、memset和memcmp这四个函数直接作用于内存字节,不依赖数据类型,灵活性极高,但也因执行底层内存,若使用不当易引发内存越界、数据污染等疑问。本文将从函数定义、使用场景、实现原理到避坑指南,全方位解析这四个函数,帮你彻底掌握内存操作的精髓。就是在 C 语言编程中,内存处理

一、内存拷贝:memcpy

memcpy的核心功能是从源内存地址拷贝指定字节数的素材到目标内存地址,是最常用的内存拷贝函数,头文件为<string.h>。

1.1 函数原型与参数解析

void *memcpy(void *dest, const void *src, size_t n);

  • dest:目标内存地址(需确保可写,且足够大以容纳n字节内容)。
  • src:源内存地址(用const修饰,表明函数不会修改源数据,提高安全性)。
  • 无符号整数类型,避免负数传入)。就是n:要拷贝的字节数(size_t
  • 返回值:返回dest的地址(方便链式调用,如printf("%s", memcpy(buf, "hello", 5)))。

1.2 核心特性与运用场景

  • 特性:按字节拷贝,不关心数据类型(无论是int、float还是自定义结构体,都能直接拷贝);但不处理源地址和目标地址重叠的情况(若重叠,可能导致资料拷贝错误)。
  • 使用场景:非重叠内存区域的数据拷贝,如数组拷贝、结构体赋值、内存块备份等。

1.3 实例演示:用 memcpy 拷贝数组与结构体

示例 1:拷贝 int 数组

#include <stdio.h>

#include <string.h>

int main() {

int src_arr[5] = {1, 2, 3, 4, 5};

int dest_arr[5] = {0}; // 目标数组初始化为0

// 拷贝5个int元素,每个int占4字节,共5×4=20字节

memcpy(dest_arr, src_arr, sizeof(src_arr));

printf("目标数组:");

for (int i = 0; i < 5; i++) {

printf("%d ", dest_arr[i]); // 输出:1 2 3 4 5

}

return 0;

}

示例 2:拷贝结构体

#include <stdio.h>

#include <string.h>

struct Student {

char name[20];

int age;

float score;

};

int main() {

struct Student src_stu = {"Zhang San", 20, 95.5};

struct Student dest_stu;

// 拷贝整个结构体,字节数为结构体大小

memcpy(&dest_stu, &src_stu, sizeof(struct Student));

printf("目标结构体:\n");

printf("姓名:%s\n", dest_stu.name); // 输出:Zhang San

printf("年龄:%d\n", dest_stu.age); // 输出:20

printf("成绩:%.1f\n", dest_stu.score); // 输出:95.5

return 0;

}

1.4 避坑指南

  • 坑点 1:目标内存不足:若dest指向的内存空间小于n字节,会导致内存越界,破坏其他内容。解决方法:拷贝前确保dest容量 ≥ n字节(可通过sizeof或自定义长度检查)。
  • 坑点 2:地址重叠:若src和dest指向的内存区域有重叠(如src = arr+1,dest = arr),memcpy可能先覆盖源数据再拷贝,导致结果错误。此时需改用memmove(下文讲解)。
  • 坑点 3:传入 NULL 指针:若dest或src为NULL,调用memcpy会触发空指针异常,程序崩溃。解决方法:拷贝前添加指针非空检查(如if (dest == NULL || src == NULL) return;)。

二、重叠内存拷贝:memmove

memmove与memcpy效果相似,也是拷贝指定字节数的内存数据,但核心优势是支持源地址和目标地址重叠的场景,头文件同样为<string.h>。

2.1 函数原型与参数解析

void *memmove(void *dest, const void *src, size_t n);

参数和返回值与memcpy完全一致,唯一区别是内部实现逻辑(处理重叠的机制)。

2.2 核心特性与使用场景

  • 特性:按字节拷贝,帮助地址重叠(经过 “先拷贝到临时缓冲区” 或 “根据地址位置决定拷贝方向” 完成);效率略低于memcpy(因多了重叠判断或临时拷贝步骤),但安全性更高。
  • 使用场景:重叠内存区域的数据拷贝,如数组内部元素移动(将arr[2]~arr[4]移动到arr[0]~arr[2])、环形缓冲区数据更新等。

2.3 实现原理:如何处理重叠?

memmove的核心是根据dest和src的地址关系,选择不同的拷贝方向:

dest < src(目标地址在源地址左侧,且无重叠或重叠区域在右侧):从低地址到高地址拷贝(与memcpy一致),避免覆盖未拷贝的源数据。

dest > src(目标地址在源地址右侧,且存在重叠):从高地址到低地址拷贝(先拷贝最右侧的字节,再依次向左拷贝),确保源数据在被覆盖前已拷贝。

2.4 实例演示:处理重叠内存拷贝

#include <stdio.h>

#include <string.h>

int main() {

int arr[5] = {1, 2, 3, 4, 5};

// 需求:将arr[2]~arr[4](3,4,5)移动到arr[0]~arr[2],内存重叠

// 若用memcpy,结果可能错误;用memmove则正确

memmove(arr, arr + 2, 3 * sizeof(int)); // 拷贝3个int,共12字节

printf("移动后数组:");

for (int i = 0; i < 5; i++) {

printf("%d ", arr[i]); // 输出:3 4 5 4 5(正确,仅前3个元素更新)

}

return 0;

}

若将上述代码中的memmove改为memcpy,在部分编译器(如 GCC)中可能输出3 4 3 4 5(错误,因先覆盖了arr[0]和arr[1],再拷贝时源资料已被修改),印证了memmove处理重叠的优势。

2.5 避坑指南

  • 无需过度依赖:若明确内存区域无重叠,优先使用memcpy(效率更高);仅当存在重叠风险或确认重叠时,才使用memmove。
  • 其他坑点与 memcpy 一致:仍需检查目标内存容量、避免 NULL 指针,因memmove仅解决重叠问题,不处理内存越界和空指针。

三、内存初始化:memset

memset的核心特性是将指定内存区域的每个字节都设置为指定的值,常用于内存初始化(如将数组清零、结构体置空),头文件为<string.h>。

3.1 函数原型与参数解析

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

  • ptr:要初始化的内存起始地址(需可写)。
  • value:要设置的字节值(虽为int类型,但实际仅采用低 8 位,即取值范围为0x00~0xFF,超出部分会被截断)。
  • n:要初始化的字节数(size_t类型,无符号)。
  • 返回值:返回ptr的地址,方便链式调用。

3.2 核心特性与使用场景

  • 特性:按字节初始化(每个字节都设置为value的低 8 位),不关心数据类型;初始化效率高(底层多为汇编优化实现)。
  • 使用场景:内存区域清零(value=0)、设置特定标记(如将数组所有字节设为0xFF表示 “未使用”)、结构体初始化等。

3.3 实例演示:用 memset 初始化内存

示例 1:数组清零

#include <stdio.h>

#include <string.h>

int main() {

int arr[5] = {1, 2, 3, 4, 5};

// 将数组所有字节设为0,共5×4=20字节

memset(arr, 0, sizeof(arr));

printf("清零后数组:");

for (int i = 0; i < 5; i++) {

printf("%d ", arr[i]); // 输出:0 0 0 0 0

}

return 0;

}

示例 2:结构体初始化(注意局限性)

#include <stdio.h>

#include <string.h>

struct Student {

char name[20]; // 字符数组,按字节初始化有效

int age; // int类型,按字节初始化需注意

float score; // float类型,不建议用memset初始化(除非设为0)

};

int main() {

struct Student stu;

// 将结构体所有字节设为0,初始化字符数组和数值类型

memset(&stu, 0, sizeof(struct Student));

printf("初始化后结构体:\n");

printf("姓名:%s(空字符串)\n", stu.name); // 正确,字符数组全0即空串

printf("年龄:%d(0)\n", stu.age); // 正确,int全字节为0即0

printf("成绩:%.1f(0.0)\n", stu.score); // 正确,float全字节为0即0.0

return 0;

}

3.4 避坑指南

  • 坑点 1:误解 value 参数:value是 “字节值”,不是 “材料值”。例如memset(arr, 1, sizeof(arr)),不是将数组每个int设为 1,而是每个字节设为 1,最终每个int值为0x01010101(十进制 16843009),这是初学者最易犯的错误!
  • 坑点 2:初始化非 0 数值类型:除了0,不建议用memset初始化int、float、double等数值类型。例如想将int a设为10,用memset(&a, 10, sizeof(a))会导致a的值为0x0A0A0A0A(十进制 168430090),完全不符合预期。
  • 坑点 3:初始化指针成员:若结构体包含指针成员(如char *name),memset会将指针设为0(即NULL),但不会初始化指针指向的内存(需手动malloc分配内存后再赋值)。

四、内存比较工具:memcmp

memcmp的核心功能是按字节比较两个内存区域的前n个字节,返回比较结果,常用于底层内存数据的比较(如比较两个数组、结构体是否完全相同),头文件为<string.h>。

4.1 函数原型与参数解析

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

  • ptr1、ptr2:要比较的两个内存起始地址(均用const修饰,不修改原资料)。
  • n:要比较的字节数(size_t类型)。
  • 返回值:
    • 若ptr1 < ptr2(按字节 ASCII 码或二进制值比较):返回负数(通常为-1,具体值因编译器而异)。
    • 若ptr1 == ptr2(前n字节完全相同):返回0。
    • 若ptr1 > ptr2:返回正数(通常为1)。

4.2 核心特性与应用场景

  • 特性:按字节逐次比较(从低地址到高地址),不关心数据类型;比较的是 “原始字节值”(而非数据的逻辑值,如float 1.0和int 1的字节值不同,比较结果不相等)。
  • 使用场景否被篡改等。就是:比较两个相同类型的数组(如int数组、char数组)、判断两个结构体是否完全一致、验证内存数据

4.3 实例演示:用 memcmp 比较内存

示例 1:比较 int 数组

#include <stdio.h>

#include <string.h>

int main() {

int arr1[3] = {1, 2, 3};

int arr2[3] = {1, 2, 4};

int arr3[3] = {1, 2, 3};

// 比较arr1和arr2的前3×4=12字节

int cmp1 = memcmp(arr1, arr2, sizeof(arr1));

// 比较arr1和arr3的前12字节

int cmp2 = memcmp(arr1, arr3, sizeof(arr1));

printf("arr1与arr2比较结果:%d(arr1 < arr2)\n", cmp1); // 输出:-1

printf("arr1与arr3比较结果:%d(arr1 == arr3)\n", cmp2); // 输出:0

return 0;

}

示例 2:比较结构体(注意陷阱)

#include <stdio.h>

#include <string.h>

struct Student {

char name[20];

int age;

float score;

};

int main() {

struct Student stu1 = {"Li Si", 20, 90.0};

struct Student stu2 = {"Li Si", 20, 90.0};

struct Student stu3 = {"Li Si", 20, 90.1};

// 比较stu1和stu2:因内存对齐可能存在填充字节,若填充字节值不同,结果可能不相等!

int cmp1 = memcmp(&stu1, &stu2, sizeof(struct Student));

// 比较stu1和stu3:score不同,字节值不同,结果不相等

int cmp2 = memcmp(&stu1, &stu3, sizeof(struct Student));

printf("stu1与stu2比较结果:%d\n", cmp1); // 可能为0(填充字节相同)或非0(填充字节不同)

printf("stu1与stu3比较结果:%d\n", cmp2); // 非0(score字节值不同)

return 0;

}

4.4 避坑指南

  • 坑点 1:结构体比较的陷阱:因结构体存在内存对齐的 “填充字节”(如前文struct Student中的填充字节),即使结构体成员值完全相同,填充字节的随机值也可能导致memcmp返回非 0。消除方法:若需比较结构体,建议手动比较每个成员(而非直接比较整个结构体)。
  • 坑点 2:浮点型比较的误区:memcmp比较浮点型(float、double)时,比较的是原始字节值,而非逻辑值。例如float a = 1.0和float b = 1.0000001,逻辑上接近,但字节值不同,memcmp会返回非 0;甚至float a = 1.0和double a = 1.0(类型不同,字节数不同),比较结果也一定非 0。
  • 坑点 3:比较字节数不足:若n小于两个内存区域的实际大小,memcmp仅比较前n字节,可能导致 “误判”(如两个数组前 5 字节相同,第 6 字节不同,若n=5,会判定为相等)。解决手段:确保n等于要比较的完整内存区域大小(如 `sizeof

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/910588.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网站做优化的必要性wordpress用户角色管理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 因为做牛客的题目…

小学校园门户网站建设做情网站

Scrapy核心组件与运行机制 引言 这一章开始讲解Scrapy核心组件的功能与作用&#xff0c;通过流程图了解整体的运行机制&#xff0c;然后了解它的安装与项目创建&#xff0c;为后续实战做好准备。 Scrapy定义 Scrapy是一个为了爬取网站数据、提取结构性数据而编写的应用框架…

安徽元鼎建设工程网站自助建站系统官方版

什么是Elastic AMPElastic APM 是一个应用程序性能监控系统。它可以请求的响应时间、数据库查询、对缓存的调用、外部 HTTP 请求等的详细性能信息&#xff0c;可以实时监控软件服务和应用程序。这可以帮助我们快速查明和修复性能问题。Elastic APM 还会自动收集未处理的错误和异…

汉川做网站网站策划搭建方案

问题描述&#xff1a; 今天在写csdn动态的时候&#xff0c;发了五个动态&#xff0c;但是主页面的“最近”看不到我发的动态&#xff0c;我还以为是csdn动态每天的发送量有数量限制。去这个地方点我的发现 右上角全是“审核中”的字样 按理说是不可能审核这么久的&#xff08…

如何制作自己的网站 可放广告西安seo关键词排名优化

一&#xff1a;背景 1. 讲故事这个月初&#xff0c;星球里的一位朋友找到我&#xff0c;说他的程序出现了死锁&#xff0c;怀疑是自己的某些写法导致mongodb出现了如此尴尬的情况&#xff0c;截图如下&#xff1a;说实话&#xff0c;看过这么多dump&#xff0c;还是第一次遇到真…

怎么直接用代码做网站手机 pc网站模板

左右指针 前言一、双指针算法二、左右指针1.用于在已排序数组中找到两个数使其和为特定值2.在字符串中判断是否为回文 总结 前言 今天在刷Leetcode的时候觉得自己双指针掌握的还是不错的记录一下,写个学习笔记,也方便以后翻阅,如果也帮助到你了,那真是太好啦! 本篇介绍的是左右…

滁州网站建设czesou网站群建设情况

一、什么是Redis Redis是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;用于存储键值对、缓存、消息队列、分布式锁等。 二、Redis和mencached的区别 相同&#xff1a;都是基于内存的数据库&#xff0c;读写都…

网站建设有关的软件重庆微信网站制作价格

目录 gdb工具的使用 代码调试相关指令 运行程序指令 r 显示代码的指令 l 给代码打断点 b 查看断点位置 info b 执行代码到断点处停止 关闭断点 d断点编号 关闭某个断点&#xff0c;但不删除 disable编号 打开某个断点 enable断点编号 逐过程调试代码 n 逐语句调试代码 s 查看…

湖南网站营销推广设计滨州做网站优化

1 python新式类变化统一类和类型 python新式类从2.2版本引入。 &#xff08;1&#xff09;新式类继承内置类型 (比如object&#xff0c;list&#xff0c;dict等)&#xff0c;经典类不继承任何类。 &#xff08;2&#xff09;python2&#xff0c;定义类时&#xff0c;显式继承…

今天做什么

练习java的项目分解

多模态算法QwenVL、KimiVL等算法原理 - Big-Yellow

最新内容:https://www.big-yellow-j.top/posts/2025/08/28/MultiModal2.html 对于多模态系列模型大致的多模态大语言模型的通用模型框架和每个模块的一些实现方法[1]:基本上就是对于图片/视频等通过不同的视觉编码器…

多模态模型——QwenVL2.5的微调以及强化学习代码操作 - Big-Yellow

本文详细解析QwenVL2.5模型的处理流程及微调方法,包括模板化输入(通过processor.apply_chat_template处理对话messages,含<|im_start|>等标记模拟用户/assistant对话)、编码输入(图像处理采用smart_resize动…

从用户态到内核态:Windows CC 技术深度解析(第一篇:DNS隧道)

本文是Windows命令与控制系列的开篇,重点解析DNS隧道技术如何通过编码数据绕过网络安全检测,涵盖从用户态到内核态的完整攻击链。文章将分三部分深入探讨DNS隧道、QUIC协议C2及内核级隐蔽通信的实现原理。从用户态到…

网站开发的关键计算机资源计划宝塔wordpress动静分离

使用swtichHost工具切换开发环境时候提示没有权限问题&#xff0c;如下图。。 解决方案有两点 1、进入 C:\Windows\System32\drivers\etc右键点击hosts的属性查看 属性的只读是否被勾选了&#xff0c;如果被勾选了将勾选勾去掉 上述完成后以管理员身份运行&#xff08;管理员…

网站建设是怎么赚钱上海网站制作哪家奿

文章目录 前言是什么&#xff1f;如何使用适用场景优点和缺点兼容性后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;前端系列文章 &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技…

科技网站设计欣赏建俄语网站

书友阅读↓ Morii 5阶学习法 1.测试学习法&#xff08;先测试再学习&#xff0c;课前测试&#xff09; 【4问】 标题是什么&#xff1f; 我的观点是什么?主题是什么? 想解决的问题是什么? 【看前言➕结尾——初步看本书的逻辑?好书?】 2.指读法~细节【逻辑】 手指指着文字…

电影网站源码怎么做的如何设计网站导航

series[i]-map用于控制 ECharts 中的地图。地图主要用于地理区域数据的可视化&#xff0c;配合 visualMap 组件用于展示不同区域的人口分布密度等数据。多个地图类型相同的系列会在同一地图上显示&#xff0c;这时候使用第一个系列的配置项作为地图绘制的配置。Tip: 在 ECharts…

哪个网站可以接加工单绵阳 网站开发

一、概述 无线局域网是指无线通信技术与上位机设备互相连接&#xff0c;最初推出的版本为IEEE802.11和IEEE802.11b&#xff0c;虽然传输距离和蓝牙一样&#xff0c;属于短距离传输&#xff0c;但是其传输速率最高可以达到11Mb/s&#xff0c;并且其覆盖率也相当高。目前WIFI技术…

广西智能网站建设哪家好做网站的保证承诺

目录 1、Windows系统自带截图工具 2、截屏软件 3、聊天软件 4、Windows系统自带有截屏的快捷键 5、浏览器截屏 6、手机拍照 今天小编给大家介绍几个常用截屏的方法&#xff0c;希望对大家的日常办公能有所帮助&#xff01; 1、Windows系统自带截图工具 点击左下角开始菜单在“…

网页制作专业怎么选wordpress 访问优化

文章目录 一、文件流打开方式参数1、文件流打开方式参数2、文件指针3、组合打开方式4、文件打开失败 一、文件流打开方式参数 1、文件流打开方式参数 文件流打开方式参数 : ios::in : 以只读方式打开文件 ;ios::out : 以只写方式打开文件 , 默认打开方式 , 如果文件已存在则清…