C语言-内存操作函数

embedded/2025/3/26 1:42:55/

C语言有一类内存函数,他们可以以字节为单位进行数据的拷贝、追加,甚至可以替代部分字符串函数。于是让我们来狠狠地学习它一百万遍吧~

目录

1.memcpy函数的使用和模拟实现

1.1memcpy函数的使用 

1.2memcpy函数的模拟实现

2.memmove函数的使用和模拟实现

2.1memmove函数的使用

2.2memmove函数的模拟实现

3.memset函数的使用

4.memcmp函数的使用


1.memcpy函数的使用和模拟实现

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

1.1memcpy函数的使用 

1.memcpy函数从source的位置开始向后赋值num个字节的数据到destination指向的内存位置。由于是以字节为单位进行拷贝,所以无关数据类型。

2.memcpy函数在遇到'\0'时并不会停下来。

3.我们不希望source和destination有重叠的部分。如果它们有任何重叠的部分,复制的结果都是未定义的。 

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

1.2memcpy函数的模拟实现

由于char类型的数据为1个字节,所以我们可以将数据类型华为char再赋值,直到num个为止。

void my_memcpy(void* dest, const void* src, size_t num)
{assert(dest && src);void* ret = dest;while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;//((char*)dest)++;src = (char*)src + 1;//((char*)src)++;}return ret;
}

我们从我们自己模拟实现的代码来看,为什么说不能有重复的地方呢?我们来看下面的图解就明白了

 假设我们有一数组,里面元素为1 2 3 4 5 6 7 8 9 10,现在我们想将2 3 4 5这20个字节的数据赋值到4 5 6 7中。那么我们希望得到的应该是1 2 3 2 3 4 5 8 9 10,而按照我们代码的逻辑,原本的4和5的位置已经被2和3覆盖了,所以赋值给原本6和7的位置就是2和3而不是4和5了,这样我们得到的结果就是1 2 3 2 3 2 3 8 9 10,不符合我们的预期。

虽然说在VS中memcpy有时候确实可以在有重叠的情况下赋值,但是标准中那么说,我们还是不要这样使用为好,万一有问题呢?所以memcpy还是只适用于dest和src无重叠部分的情况。那有重叠的情况该怎么办呢?我们接下来马上介绍。

2.memmove函数的使用和模拟实现

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

2.1memmove函数的使用

1.memmove函数与memcpy函数的区别就是memmove函数处理的源内存块和目标内存块可以有重叠部分。

2.如果源空间和目标空间出现重叠,我们就是用memmove函数来处理。

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

2.2memmove函数的模拟实现

memmove的实现要比memcpy要复杂,因为要考虑到区域块重叠的情况,所以我们也要分情况讨论。

第一种情况,也就是上面memcpy函数举例的情况

我们能看到,下面左边的就是按照我们memcpy模拟实现的代码的逻辑,这显然是不行的。但是我们发现,左边的逻辑是将src的内容从左边开始向dest复制,那如果我们从右边开始,即先把src最后一个元素先给到dest最后一个元素,然后依次往前呢?显然是可以的,这样就不会导致4和5还没赋值过去就被2和3覆盖,结果显然就是正确的。所以这种情况我们可以用从后往前赋值的逻辑处理

第二种情况,我们将src和dest换个位置,希望将4 5 6 7赋值到2 3 4 5的内存块中,那我们期望的结果应该是1 4 5 6 7 6 7 8 9 10。

 我们也向上次那样分析,可以发现从前往后传值是正确的,而从后往前传就会导致重叠部分还没赋值它原本的值就已经被覆盖了,所以这种情况我们可以用从前往后赋值的逻辑处理

 现在理解了上面两种情况,我们来尝试总结一下

 

当目标内存块dest在源内存块src左边或在src左边且与src有重叠时(dest在D1区域或D1+D2区域),我们采用从前向后赋值的方法;而当目标内存块dest在源内存块src右边或在src右边且与src有重叠时(dest在D3区域或D3+D2区域),我们采用从后向前赋值的方法。

那我们现在就可以开始写代码了。

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;
}

其实只要好好画图分析,分类讨论,其实也不难。

3.memset函数的使用

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

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

2.ptr是指向被填充的内存块,value是要把原数据修改成什么,num是你要设置多大的字节的内存。

#include <stdio.h>
#include <assert.h>int main()
{char arr[] = "hello world";memset(arr + 2, 'y', 7);printf("%s\n", arr);//heyyyyyyyldreturn 0;
}

特别注意当我们向修改整型数组的值的时候,出了0之外的值我们是没办法设置的 

#include <stdio.h>int main()
{int arr[5] = { 0 };memset(arr, 1, 5*sizeof(int));for (int i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;
}

向上面这样想把数组中的每个值都修改为1是做不到的,我们来看看代码运行的结果 :

其原因是,memset是以字节为单位来设置的,我们来看看数组元素的内存:

如我们所见,memset将我们每个字节都设置成了1,那么一个整型为4个字节,即每个字节为01 01 01 01,换算成十进制这是个很大的数字,所以我们的结果才会都为16843009。

4.memcmp函数的使用

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

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

#include <stdio.h>int main()
{int arr1[] = { 1,2,3,4,5,6,7 };int arr2[] = { 1,2,3,4,8,8,8 };printf("%d\n", memcmp(arr1, arr2, 16));//0printf("%d\n", memcmp(arr1, arr2, 17));//-1return 0;
}

 其实和strncmp函数是比较相似的,只不过memcmp函数是以字节为单位的,可以比较不限于char*类型的数据。


那么到这里内存函数相关的内容就结束了,其实还是很简单的吧~


http://www.ppmy.cn/embedded/11862.html

相关文章

LabVIEW仪器信息管理系统

LabVIEW仪器信息管理系统 在计量检测实验室的日常工作中&#xff0c;仪器检定校准是一项基础而重要的任务。随着科技的进步和实验室工作量的增加&#xff0c;传统的人工管理方式已经难以满足现代实验室对效率和准确性的要求。开发一套基于LabVIEW的仪器信息管理系统显得尤为必…

实现动态组件的方式

一、<component :is"放置组件名"></component>&#xff08;常用&#xff09; 我们可以定义一个数据并将该数据作为 component 元素的 is 属性值&#xff0c;这个数据保存了组件的名称&#xff0c;当我们需要切换组件时&#xff0c;只需要改变数据所保存…

2232: 【数学】因子个数

题目描述 对于任意给定的一个正整数&#xff0c;计算其因数个数。 输入 输入正整数N。 输出 输出N的因子个数。 样例输入 6 样例输出 4 Code: #include<bits/stdc.h> using namespace std; int c(int n){int cu2;double ksqrt(n);for(int i2;i<k;i){if(n%i…

Linux 第一章

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

Json三方库介绍

目录 Json是干什么的Json序列化代码Json反序列化代码 Json是干什么的 Json是一种轻量级的数据交换格式&#xff0c;也叫做数据序列化方式。Json完全独立于编程语言的文本格式来存储和表述数据。易于人阅读和编写&#xff0c;同时也易于机器解析和生成&#xff0c;并有效地提升…

贪心算法练习day.1

理论基础 贪心算法是一种常见的解决优化问题的方法&#xff0c;其基本思想就是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部的最优决策&#xff0c;以此得到全局的最优解&#xff0c;例如在十张面额不同的钞票&#xff0c;让我们…

Debian 12.5(代号 “Bookworm“)中安装中文支持

在 Debian 12.5&#xff08;代号 "Bookworm"&#xff09;中安装中文支持通常涉及以下几个步骤&#xff1a; 1. **选择语言**&#xff1a; 在安装过程中&#xff0c;安装程序会询问您选择界面语言&#xff0c;您可以选择中文。 2. **安装中文语言包**&#xff1a…

MapReduce案例-电影网站数据统计分析

本文适合大数据初学者学习MapReduce统计分析业务问题的步骤和基础的MapReduce编程方法&#xff0c;初步掌握Hadoop对计算任务的管理。 本文末尾有全部数据集和完整代码连接。 1.准备工作 安装Hadoop:Hadoop 3.3.2 离线安装-CSDN博客 按照好Hadoop之后要检查一下datanode运行情况…