在上篇博客中,我们学习了字符串函数,针对一些字符串我们可以做出一系列操作。接下来我们将学习一些内存修改函数(#inlcude),让我们一起走进mempy、memmove、memcmp函数中。


目录

mempcy函数​编辑

memmove函数

memcmp函数


mempcy函数

通过函数原型,我们可以看出返回值和一些指针参数类型都是void*类型,这就说明此函数不仅仅局限于字符串,针对整型数组、结构体数组等等都可以实现,size_t num参数单位是字节,因为前面指针的类型为void* ,我们并不知道传入数组的元素类型,所以第三个参数的单位为字节就能对很多数据类型进行拷贝。

memcpy函数是复制内存块的函数,函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

1.参数:destianation指针是接收一个任何类型的数组首元素地址,用作目标。

source指针也是接收同种类型的指针变量,用作拷贝内容。

size_t num是要拷贝的字节数。

2.返回值:返回目标的指针。

3.这个函数在遇到 ‘\0’ 的时候并不会停下来该函数不检查中的任何终止空字符 – 它总是准确地复制字节

4.如果source和destination有任何的重叠,复制的结果都是未定义的。

接下来对memcpy函数的引用:

int main(void){int arr1[] = {1,2,3,4,5,6,7,8,9,10};int arr2[20] = { 0 };memcpy(arr2, arr1, 40);int i = 0;for (i = 0; i < 20; i++){printf("%d ", arr2[i]);}return 0;}

将arr1中的40个字节内存内容拷贝到arr2中去。arr2中有20个0,但是只拷贝10个arr1中的元素,所以arr2中有前10个元素将被arr1中的内容覆盖,结果如下:接下来我们进行对memcpy函数的模拟实现:

模拟实现void* my_memcpy(void* dest, const void* src, size_t num){void* ret = dest;assert(src && dest);while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;}int main(void){int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[20] = { 0 };my_mempy(arr2, arr1, 40);for (int i = 0; i < 20; i++){printf("%d ", arr2[i]);}return 0;}

我们创建一个my_memcpy函数,仿照memcpy函数原型来建立自定义函数的原型。因为返回值为目标数组的首元素指针,所以我们先创建一个void*指针对目标数组进行标记。然后使用while循环,进行逐字节赋值。因为memcoy函数可以针对任何种类的数组,所以我们应该将void*类型指针全部强制类型转换为最小单位char*类型的指针,这样就可以兼容所有种类的数组。当赋值完成后进行指针加1操作(对于void*类型的指针,我们不能对其进行++操作,所以我们只能写成dest = (char*)dest + 1;的形式)。当num–到0时,证明已经全部赋值完毕,跳出循环返回最初指针即可。

下面是我们运行的结果:

与我们最初使用memcpy函数所得到的结果相同。

接下来我们又有一个问题,我们可以使用memcpy函数将一个数组中的内容,拷贝到自己中吗?说更通俗点:有一个整型数组int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };将arr1中的1,2,3,4,5放到3,4,5,6,7的位置上面吗?

int main(void){int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };memcpy(arr1 + 2,arr1, 20);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;}

我们想的到最终arr1中的内容:1,2,1,2,3,4,5,8,9,10但结果如何呢?

那为什么会是这样呢?

那我们应该怎么做才能完成刚才的内容呢?这就要使用memmove函数来完成了!!


memmove函数

memmove函数是将移动内存块的函数,将字节的值从指向的位置复制到目标指向的内存块。复制就像使用了中间缓冲区一样,允许目标重叠。

1.返回值和参数与mempy函数相同,我们可以参照上面进行理解。

2.指针和目标指针指向的对象的基础类型与此函数无关;结果是数据的二进制副本。
该函数不检查中的任何终止空字符 – 它总是准确地复制字节
为避免溢出,目标参数和参数指向的数组的大小应至少为字节

3.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

接下来让我们使用memmove函数完成上面的问题:

int main(void){int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };memmove(arr1 + 2, arr1, 20);for (int i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;}

很丝滑的完成了刚才的问题。

现在让我们对memmove函数进行模拟实现:

void* my_memmove(void* dest,const void* src, size_t num){void* ret = dest;if (dest < src){while (num--){*(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;}else{while (num--){*((char*)dest + num) = *((char*)src + num);}}}int main(void){int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };my_memmove(arr + 2, arr, 20);int i = 0;for (i = 0; i < 10; i++){printf("%d ", arr1[i]);}return 0;}

创建一个my_memmove函数,仿照memmove原型完成自定义函数的参数。但是我们应该怎么完成函数体呢?

刚才使用最基本的从前向后复制,就得不到想要的结果,如果从后向前复制则可以得到想要数组。有写情况我们又需要从前向后复制,所以我为大家总结一下:

所以我们得分情况来完成自定义函数体,当dest指针src指针时,我们继续利用while循环,利用从后向前复制才能完成。在每次解引用前加上nun字节数即可指针从后往前访问,while判断条件为num–,在判断是否结束时也可以挪动指针,一举两得。最后返回目标数组的首元素地址即可。唯一与my_memcpy函数不同的时必须分类讨论。

运行结果如下:

此函数模拟完成!

针对以上两个函数,看到这里都会有个疑问,是不是memcpy函数可以干的事memmove函数都可以办到。那肯定的。所以memmove函数的作用肯定是高于mempy函数的!!!


memcmp函数

memcmp函数是比较两个内存块,这里与strcmp函数有一些相似,都是比较两个数组是否相同,返回值的情况相同。但不同的是memcmp可以比较的类型更多,该函数在找到空字符后不会停止比较,而strcmp会停止。

PTR1

指向内存块的指针。

PTR2

指向内存块的指针。

数字

要比较的字节数。

它是通过比较内存中存储的数据进行一个字节一个字节的比较。

举个使用memcmp函数的例子:

int main(void){int arr1[10] = { 1,2,1,4,5,6 };int arr2[10] = { 1,2,257 };int ret = memcmp(arr1, arr2, 9);printf("%d\n", ret);return 0;}

比较arr1与arr2中前9个字节是否相同?

arr1中前10个字节的存储内容

arr2中前10个字节存储内容

因为我们比较的是前9个字节的内容,所以是全部相同的,应该返回输出的值为0;如果我们比较前10个字节,应该输出的就为-1;

以上就是我对内存修改函数的全部认识,希望各位大佬在评论区给予我宝贵意见!