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

C语言中的内存函数(memcpy, memmove, memcmp, memset)

目录

一、内存拷贝函数——memcpy

✍️介绍

✍️模拟实现

二、内存拷贝函数——memmove

✍️介绍

✍️模拟实现

三、内存比较函数——memcmp

四、内存设置函数——memset


一、内存拷贝函数——memcpy

✍️介绍

这个函数是用来从一个内存位置拷贝一块内存到另一个内存位置的函数,使用的时候的头文件是string.h

函数声明:

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

参数说明:

  • 第一个参数:是目标位置的地址,因为我们不知道用户传入的参数类型是什么,所以我们一律使用了void*来作为参数类型。
  • 第二个参数:是源位置的地址,这里要注意的是这里的类型最好是加上const,这是因为用户不能修改源来位置的内容。
  • 第三个参数:要复制的字节个数。

举个栗子:

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

测试结果:

敲黑板:

我们将从src地址开始的n个字节的内容复制到了dest地址,如果src和dest区域出现了重叠的话,这个时候的结果就是未定义的,也就是无法保证正确的复制了。

✍️模拟实现

我们首先要做就是要保存住dest指针,这是为了方便我们后续的返回操作,然后就是遍历count次一个字节一个字节的将src位置开始的数据拷贝到dest,最后我们再返回原来我们保存的dest指针。

void* my_memcpy(void* dest, const void* src, size_t count) {assert(src != NULL); // 断言不要传入一个空的地址assert(dest != NULL); // 断言不要传入一个空的地址void* temp = dest; // 存一下dest的地址while(count--) // 遍历count次{*((char*)dest) = *((char*)src); // 这里重点解释一下:我们这里使用char*强转就是为了限制每一次都是一个字节的操作,前面的*是找到地址指向的数据(char*)dest++;(char*)src++;}return temp;
}

测试一下我仿写的效果:

二、内存拷贝函数——memmove

✍️介绍

其实这个函数和我们上面的memcpy函数的函数参数都是一样的,同时我上面也提及了memcpy函数无法解决我们的src和dest区域的重叠问题而这个函数就可以解决这个问题。

函数声明:

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

参数说明:

这里的参数和我们上面的一样,这里就不多说了。

我们这里首先来看看上面这个函数重叠的栗子:

我们给定一个从1到10的数组,然后我将1,2,3,4拷贝到3,4,5,6上,预期的结果是1 2 1 2 3 4 7 8 9 10

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

但是我们的运行结果就是预期的结果,这是因为我们的编译器发现了这个内存重叠问题,于是编译器便自作主张的使用了memmove函数。

但是如果不考虑编译器的优化,我的结果应该是1 2 1 2 1 2 7 8 9 10。

这里我们根据上面我们实现的原理来走一遍,dest和src同时往后走,3变成了1,4变成了2,这个时候我们的5就变成了1,7就是2了。

✍️模拟实现

我们这里可以首先发现一个规律,就是我们的拷贝操作实际上是有三类的:

我们通过上面的图,总结一下:

  • 第一类:dest指针在src内存块的左边,我们这里采用从前到后的拷贝方法,这样就可以避免重叠了。
  • 第二类:dest指针位于src内存块内,采用从后到前的拷贝方法,这样就可以避免重叠了。
  • 第三类:dest指针位于src内存块的右边,我们采用从后到前和从前到后都是可以的。

代码如下:

void* memmove(void* dest, const void* src, size_t count) {assert(src != NULL); // 断言不要传入一个空的地址assert(dest != NULL); // 断言不要传入一个空的地址void* temp = dest; // 存一下dest的地址if(dest < src) {while(count--) {*((char*)dest) = *((char*)src);(char*)dest++;(char*)src++;} }else {while(count--) {*((char*)dest + count) = *((char*)src + count); // 从后向前}}return temp;
}

测试效果如下:

三、内存比较函数——memcmp

这个函数是用来比较两个内存区域,也就是比较ptr1和ptr2指向的内存区域的前n个字节,当ptr1小于ptr2就返回负数,当ptr1大于ptr2就返回正数。

函数声明:

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

参数说明:

  • 第一个参数:是要比较的第一个内存空间的地址
  • 第二个参数:是要比较的第二个内存空间的地址
  • 第三个参数:是要比较的字节个数

我们这里可以写个代码来测试一下:

#include <stdio.h>
#include <string.h>int main() {int arr1[] = {1, 2, 3, 4};int arr2[] = {1, 2, 4, 5};int ret1 = memcmp(arr1, arr2, 8);int ret2 = memcmp(arr1, arr2, 9);printf("%d %d", ret1, ret2);return 0;
}

测试结果如图:

我们这里解释一下,我们在使用的编译器是小端模式,所以它的存储模式是从低地址到高地址的,比如这里的arr1和arr2:

所以我们比较前8个字节就会返回0,而比较到了第9个字节的时候就会返回-1了。

四、内存设置函数——memset

这个函数的主要目的就是将一块内存设置特定的值。

函数声明:

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

参数说明:

  • 第一个参数:就是要设置的内存的开始位置了。
  • 第二个参数:要设置的内容,这里虽然是int,但是后面会被转换成unsigned char类型的,也就说明了这个函数是一个字节一个字节设置的。
  • 第三个参数:是从起始位置开始需要设置的字节数了。

我们举个栗子:

比如将"hello world!"字符串的前5个字符设置成'#'。

代码如下:

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


效果如下图:


文章转载自:

http://PssDSN7p.kfbth.cn
http://gIpt7IHQ.kfbth.cn
http://6DsR1UMO.kfbth.cn
http://3wLaABSG.kfbth.cn
http://U2jlFpGL.kfbth.cn
http://3HKiYoY4.kfbth.cn
http://xGlZs0uN.kfbth.cn
http://cjEJtvOp.kfbth.cn
http://duBTjiWD.kfbth.cn
http://5Q6GKyyC.kfbth.cn
http://dzF9bX03.kfbth.cn
http://ehistZfo.kfbth.cn
http://r24UtL1h.kfbth.cn
http://ZQjcrfQE.kfbth.cn
http://7qGB6M6T.kfbth.cn
http://0bNXtkYL.kfbth.cn
http://wBMW4HEM.kfbth.cn
http://yHke93KX.kfbth.cn
http://ZI4eIQBt.kfbth.cn
http://RCd9yIL3.kfbth.cn
http://EgRDoEzi.kfbth.cn
http://5XyKKMbQ.kfbth.cn
http://AXXRb5XM.kfbth.cn
http://QfDTL0f2.kfbth.cn
http://yt7cTRHA.kfbth.cn
http://7dDp9sMK.kfbth.cn
http://hzJQQlz8.kfbth.cn
http://uYp8crEF.kfbth.cn
http://0QsdU9cW.kfbth.cn
http://5nqZEwOz.kfbth.cn
http://www.dtcms.com/a/380446.html

相关文章:

  • 自动化土壤称重分样系统
  • 太阳光模拟器 | 光辐射测量的基础知识
  • 手搓Tomcat
  • tuxedo11g-可执行文件
  • 全文 - Graphene -- An IR for Optimized Tensor Computations on GPUs
  • 3. 信息系统基础知识
  • 【开题答辩全过程】以 《黄帝内经》问答系统为例,包含答辩的问题和答案
  • Vmware 17 pro安装mac13
  • 【React】react 中如何实现像 vue 中的 keep-alive?
  • 《AI游戏开发深层问题实录:4类典型难题的排查与解决路径》
  • OpenStack Nova 创建虚拟机
  • MySQL在线修改表结构
  • 【Java】Windows切换Java8和Java11
  • Linux内核TCP上层协议(ULP)可插拔机制解析
  • Graph RAG论文阅读笔记
  • linux常用命令 (3)——系统包管理
  • Firefox自定义备忘
  • SQL进阶:从基础语法到实战技巧
  • 性能测试工具Jmeter之java.net.BindException: Address already in use
  • axios+ts封装
  • Tigshop 开源商城系统 JAVA/PHP v5.1.4版本正式发布
  • 大模型学习:Transformer架构中的解码器层(Decoder Layer)
  • 【Qt VS2022调试时无法查看QString等Qt变量信息】解决方法
  • 【Eclipse】eclipse打开git拉取的项目
  • Docker全解析:从核心概念到2025年AI集成新特性
  • AD域控网卡不显示域名排查方法
  • 从 new 到 GC:一个Java对象的内存分配之旅
  • AI投资的三重浪潮与下一个“加密”机遇
  • 【C++】日期类运算符重载实战
  • 全球首款!科聪控制器获德国 TÜV 莱茵功能安全认证