目录
🍕注意:内存操作函数隶属于头文件,因此在使用任何内存操作函数之前都必须引用
🥞memcpy函数
🍞memcpy函数的初步认识及使用
🍳样例示范:
🧈代码呈现:
🧀my_memcpy函数的实现
🥗memcpy函数的分析
🥙代码呈现
🥪运行效果:
🌮memmove函数
🌯memmove函数的认识
🍖代码呈现:
🍗运行效果
🍠现象背后的本质
🥠my_memmove的实现
🥡代码呈现:
🍱运行效果:
编辑
🍘memcmp 和 memset 函数的使用介绍
🦪代码呈现:
🍣运行结果:
🍿还记得我们之前所认识的 strcpy 函数吗?这个函数的作用是将一个字符数组中的内容拷贝到另一个字符数组当中。但是在我们了解并实现 strcpy 函数的时候就会很容易发现一个弊端,要是我们想要复制的内容不是一个字符数组该怎么办呀?要是一个整型数组或者是一个浮点数数组呢?那应该用什么函数呢?今天我们就来认识可以解决以上疑问的函数,内存操作函数。
🍕注意:内存操作函数隶属于<string.h>头文件,因此在使用任何内存操作函数之前都必须引用<string.h>
🥞memcpy函数
🍞memcpy函数的初步认识及使用
🍿这个函数就是和我们 strcpy 函数相对应存在的,用于处理各种数据类型进行复制。我们先通过函数原型进行 memcpy 函数的基本了解与认识。
🍿 由上图可以了解到我们的 memcpy 函数在使用的时候需要向函数中传递三个参数,第一个参数为目的地数组的指针,第二个参数为源头数组的指针(源数组->目的地数组 进行复制),第三个参数为一个无符号整型也就是说我们需要向函数说明本次复制的字节的个数。
🍿由上面的函数接口我们可以了解到,memcpy 函数实质上是按照字节个数进行一个字节一个字节进行复制操作的。因此 memcpy 函数不仅可以实现数组的整体复制而且可以实行数组部分的复制。更大程度上增加了我们使用函数的灵活性。那么接下来我们就来通过一个使用样例感受一下 memcpy 函数使用的奥妙之处吧!
🍳样例示范:
🧈代码呈现:
#include<string.h>
#include<stdio.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,0 };
int arr2[] = { 12,13,14,15,16,17 };
memcpy(arr1, arr2, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr1 + i));
}
return 0;
}
🍿如同上图所示,我们将数组 arr2中的前五个元素(20个字节)复制进入数组 arr1 当中。 之后我们将 arr1 通过打印的形式展示出来之后发现数组 arr1 改变的只有前五个元素,其他元素均不变。经过上面的示例之后我相信大家已经明白该怎使用 memcpy 函数了,那么接下来就让我们来亲自实现以下这个 memcpy 函数进而加深我们的学习体会吧!
🧀my_memcpy函数的实现
🍿首先我们需要做的第一步就是逐步分析 memcpy 函数的操作原理,只有在了解了 memcpy 函数是如何执行复制操作之后才可以真正意义实现我们的 memcpy 函数。
🥗memcpy函数的分析
🍿对于 memcpy 函数,我们已经了解到了他其实是逐个字节依次进行赋值操作的。为了使得我们设计的函数同样可以适应多种数据的复制,所以我们的函数接口同样应该设计成 void * 类型。借鉴并仿照上面的函数原型我们同样可以将我们的函数接口设计成 void *类型,实质上是返回我们目的地数组首元素的指针。
🍿有了以上的思路我们就可以正是着手实现我们的memecpy函数了。
🥙代码呈现
#include<stdio.h>
//模拟实现memcpy
void* my_memcpy(void* dest, const void* sour, size_t size)
{
void* ret = dest;
while (size--)
{
*(char*)dest = *(char*)sour;
dest = (char*)dest + 1;
sour = (char*)sour + 1;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,0 };
int arr2[] = { 11,22,33,44,55,66,77,88,99,100 };
my_memcpy(arr2, arr1, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr2 + i));
}
return 0;
}
🥪运行效果:
🍿由上面的代码我们可以看出,我们 my_memcpy 函数的实现的难点就在于类型的强转操作。为什么我们再赋值的时候要先进行强转操作才可以进行赋值呢?
🍿这是因为:我们为了适应所有类型的数据的复制操作,所以我们需要将函数的类型定义成 void *类型的数据,但是为什么又要将 void *强转成为(char *)类型而不是其他的类型呢?这与我们函数接口中的传参有关,我们向函数中上传的第三个参数是一个以字节为单位的无符号整型,因此我们想要按照字节,一个字节一个字节的复制的话就只有 char 类型的数据符合条件。(同样的是为了满足我们使用者各种各样的需求,想象一下,假如将函数设计成强转为(int *)类型的数据的时候使用者想要复制四个字节的数据也是不可能的了,不是吗?)
🍿上面我们的 my_memcpy 也就设计完成了。但是肯定会有小伙伴会突发奇想:要是将一个数组中的数据复制进入本数组中会出现什么呢?
🍿假如我们想要将 1-5 这五个数据复制到 3-7 这五个数据的位置,按照我们的想象复制完成的数据本来应该是:1 2 1 2 3 4 5 8 9 0 。但是我们事实上的运行效果却是:1 2 1 2 1 2 1 8 9 0 。这是什么原因呢?
🍿相信小伙伴们很容易就想到:元素覆盖 1->3 的位置的时候将 3 覆盖了。依次再次复制到 3 的位置的时候只能复制出 1 ,而不是 3 。那么又该怎么解决这种现象呢?这时候我就需要来向大家介绍下一个 memmove 函数了。
🌮memmove函数
🌯memmove函数的认识
🍟我们要想在同一个数组中进行复制操作的时候就需要使用到一个新的函数—— memmove 函数了。同样的,我们先来分析函数原型,认识一下这个函数。
🍟通过图片我们可以知道,memcpy 函数和 memmove 的函数不管是函数接口还是函数的返回值都是一样的,事实上,这两个函数的使用方法也确实是一模一样的。我们对其用法直接通过一段代码来体会:
🍖代码呈现:
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
memmove(arr + 2, arr, 5 * sizeof(int));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
🍗运行效果
🍟我们发现,使用我们的 memmove 函数并不会出现这个问题,这是什么原因呢?为了向大家解释着其中的奥秘,我们得先从出现这种现象的的原因进行讲起。
🍠现象背后的本质
🍟如图我们的 memcpy 函数从前向后一个字节一个字节的进行复制,3 和 4 等元素都被覆盖成了 1 和 2 这是我们运行结果出错的原因。但是有没有办法避免这种不必要的覆盖呢?或者说先覆盖我们不需要的元素呢?
🍟我们只需要略微调换一下位置之后就会很容易就发现原来这么简单!我们可以先从我们最后一个元素进行覆盖,一直向前,那么即使我们源数组中的元素被覆盖了也没有任何影响,反正我已将用完了。不是吗?这样就我们的复制操作也就可以正常进行了。但是你又会发现,并不是所有单个数组中的情况都能用从后向前复制。
🍟假如我们想将 3-7 复制到我们的 1-5 的位置的时候从后向前复制的话反而会覆盖,从前向后复制则不会。那么我们就需要仔细观察了,那么什么时候才可以用从前向后复制呢?什么时候从后向前复制呢?经过我们仔细观察发现,我们可以将我们的源数组和我们的目的地数组的指针进行比较,如果源数组的指针大于目的地数组那么就是用从后向前的复制顺序,反之则使用从前向后的复制顺序。有了这一点经验之后我们就可以自主设计一个 memmove 函数了。
🥠my_memmove的实现
🍟经过上面的分析之后我们就已经了解过了 memmove 和 memcpy 之间的主要差别。我们就可以抓住这点差别来进行程序的编写。闲话少说,直接来看代码。
🥡代码呈现:
#include<stdio.h>
//自主实现memmove
void* my_memmove(void* dest, void* sour, size_t size)
{
void* ret = dest;
if (sour>dest)
{
while (size--)
{
*(char*)dest = *(char*)sour;
dest = (char*)dest + 1;
sour = (char*)sour + 1;
}
}
else
{
while (size--)
{
*((char*)dest + size) = *((char*)sour + size);
}
}
return ret;
}
int main()
{
int i = 0;
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i));
}
printf("\n");
//my_memmove(arr, arr + 2, 5 * sizeof(int));
my_memmove(arr + 2, arr, 5 * sizeof(int));
for (i = 0; i < 10; i++)
{
printf("%d ", *(arr + i));
}
return 0;
}
🍱运行效果:
🍟值得我们注意的同样是和 memcpy 函数中提到的一样:注意强制类型转换。那么之后让我们再来认识一下其他的内存操作函数吧!
🍘memcmp 和 memset 函数的使用介绍
🍤接下来就轮到介绍我们的 memcmp 函数和 memset 函数了。对于这两个函数均是和 memcpy 和 memmove 函数类似,对单个字节进行操作的函数。
🍤首先,memcmp是将两个不同类型的元素一个字节一个字节的进行比较。如果相同则返回0,反之按照情况返回一个大于或小于0 的数字。(返回方案和strcmp类似)
🍤就像是上图中显示的一样。只需要在最后加上一个参数(所要比较的字节的个数)即可。
🍤之后对于我们的 memset 同样具有很重要的作用,该函数是对单个字节进行设置指定的值。
🍤由上图我们可以了解到 memset 函数的第一个参数是一个 void*类型的指针,也就是我们需要设置的元素的地址,第二个元素为我们所要设置的元素的值,第三个元素是我们索要设置的字节的个数。
🍤经过建议的了解之后我们来通过实例来体会这两个函数的使用方法。
🦪代码呈现:
#include<stdio.h>
#include<string.h>
int main()
{
float arr1[] = { 12.1,13.42 };
float arr2[] = { 12.1,14.6 };
int ret1 = memcmp(arr1, arr2, 4);
int ret2 = memcmp(arr1, arr2, 5);
printf("%d %d", ret1, ret2);
return 0;
}
#include<stdio.h>
#include<string.h>
int main()
{
int arr[5] = { 0 };
memset(arr, 1, 4);
int i = 0;
printf("%x\n", arr[0]);
return 0;
}
🍣运行结果:
🍤由上图我们的运行展示也就结束了。由于后两个函数的自定义并不难所以就叫给大家自己来尝试啦。那么本次博客的内容到此结束,感谢大家的观看。祝大家天天开心。