C:内存函数

JonlyMay 2024-10-03 10:35:02 阅读 80

目录

前言:

一、memcpy 函数的使用及实现

1、memcpy函数的介绍

 1.1 memcpy函数参数解读

 2、memcpy函数的使用

3、memcpy函数的模拟实现

二、memmove函数的使用及模拟

1、memmove函数的使用

2、memmove函数的模拟实现

三、memset 函数的使用

1、memset函数的介绍(cplusplus)

2、memset函数的使用

2.1 memset函数对数组的应用

2.2 memset函数对字符串的应用

​编辑

 四、memcmp函数的使用

1、memcmp函数的介绍

2、memcmp函数的使用


前言:

上篇文章介绍了C语言字符串函数,我们学会了一些对字符串的操作,比如说将字符串从一个字符数组拷贝到另一个字符数组中,我们可以通过使用strcpy函数实现。但是,如果我们想要拷贝一个整型数组到另一个整型数组中时,strcpy函数就失效了,那我们应该怎么才能实现这个操作呢?不要着急,本篇文章将带大家搞定这个问题。

一、memcpy函数的使用及实现

1、memcpy函数的介绍

cplusplus上的介绍:

作用介绍:

参数介绍:

返回值介绍:

 1.1 memcpy函数参数解读

(1)void* destination

该参数的作用是目标空间,用来存放将要拷贝的内存,为什么返回值是 void* 呢?这是因为这个函数的作用是内存拷贝,既然是内存拷贝,内存中又可能存放的是整型数组,也有可能存放的是字符数组……,因此我们不关心存放数据的类型,因此使用void*指针来接收任意类型的数据的地址。

(2)const void* source

source是源头,也就是要拷贝的内存数据,这里也是void*指针是因为我们不知道我们未来要拷贝的数据是什么类型的,可能是整型,可能是字符,也可能是结构体,因此我们也使用void*。

用const修饰是因为我们不希望要拷贝的数据被修改,因此使用const修饰会使得整个工程更加稳定。

(3)size_t num

num的作用是限定拷贝的字节数,比如说source中有十个字节的数据,我们可以通过修改num的值来拷贝我们想要的个数,num为5,我们就拷贝五个字节的数据到destination中;num的类型是size_t的原因是我们拷贝的个数最低都是0个,不会出现负数的情况,因此使用size_t类型最为合适。

 2、memcpy函数的使用

前面学习了memcpy函数,接下来我们将使用memcpy函数来实现一些操作

比如说我们打算将整型数组arr1[ ] = {1,2,3,4,5,6,7,8,9,10}拷贝到整型数组 arr2[10] = { 0 };

<code>#include <stdio.h>

int main()

{

    int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

    int arr2[10] = { 0 };

    memcpy(arr2, arr1, 5 * sizeof(int));

    return 0;

}

调试结果:

源头从 3 开始拷贝,比如说:

<code>#include <stdio.h>

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

int arr2[10] = { 0 };

memcpy(arr2, arr1+2, 5 * sizeof(int));

return 0;

}

 一些总结:

函数memcpy从source的位置开始向后赋值num个字节的数据到destination指向的内容;这个函数在遇到\0的时候并不会停下来,与字符串函数不同;如果source和destination有任何的重叠,赋值的结果都是未定义的。

3、memcpy函数的模拟实现

接下来我们尝试自己写一个函数来实现memcpy的功能

<code>void* my_memcpy(void* dest, const void* src, size_t num)

{

}

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

int arr2[10] = { 0 };

my_memcpy(arr2, arr1, 5 * sizeof(int));

return 0;

}

注意:在my_memcpy中,源头是void*指针类型的  

现在有一个问题,我们不知道我们要拷贝的是内容是什么类型的,我们只知道要拷贝的是20个字节,我们该怎么将这20个字节拿到arr2中呢?

在前面学习qsort函数的模拟实现中,我们用到了一个方法,我们可以一个字节一个字节的拷贝,那么就可以使用强制类型转换将void*指针转换为char*指针

<code>void* my_memcpy(void* dest, const void* src, size_t num)

{

while (num--)//num是字节总数,因此num每减1,就拷贝一个字节

{

*(char*)dest = *(char*)src;

dest = (char*)dest + 1;

src = (char*)src + 1;

}

}

调试结果: 

总代码:

<code>#include <stdio.h>

void* my_memcpy(void* dest, const void* src, size_t num)

{

// 保存目标地址,以便最后返回

void* ret = dest;

// 循环 num 次进行逐个字节的复制

while (num--)

{

// 将源地址指向的内容复制到目标地址指向的位置,并转换为 char* 类型进行操作,确保每次只复制一个字节

*(char*)dest = *(char*)src;

// 目标地址向后移动一个字节

dest = (char*)dest + 1;

// 源地址向后移动一个字节

src = (char*)src + 1;

}

// 返回复制后的目标地址

return ret;

}

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

int arr2[10] = { 0 };

my_memcpy(arr2, arr1, 5 * sizeof(int));

return 0;

}

如果我们向将int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };中(1,2,3,4,5)拷贝放到(3,4,5,6,7)的位置上,这样可以实现吗?

my_memcpy(arr1+2, arr1, 5 * sizeof(int));

调试结果:

为什么这里会是(1,2,1,2,1)呢?其实也很好理解

 因此最终结果是(1,2,1,2,1,2,1,8,9,10) 

关于重叠问题,我们一般使用后面的这个函数memmove函数

而memcpy函数一般用来处理不重叠情况。

在vs2022中,memcpy的能力是比较强的,也是可以用来处理重叠问题,但是对于memcpy函数,本来的作用是不包括处理重叠的问题的,这就像是老师说让你考到60分就行,但是你能考100分。但是不能保证在所有的编译器上memcpy都可以考到100分

也就是说无法确定其它编程环境是否可以实现,因此,如果要处理重叠问题,最好还是交给memmove函数.

二、memmove函数的使用及模拟

1、memmove函数的使用

memove函数的使用与memcpy函数是一样的,也是用来实现内存中数据的拷贝的,因此就不详细介绍了。不过前面也说了memmove函数可以实现重叠拷贝,来测试一下

<code>#include <stdio.h>

#include <string.h>

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

memmove(arr1+2, arr1, 5 * sizeof(int));

return 0;

}

调试结果:

 可以看到的确将(1,2,3,4,5)的内容拷贝到(3,4,5,6,7)的位置上了

那么memmove函数究竟是怎么实现这个操作的呢?我们来模拟了解一下

2、memmove函数的模拟实现

前面我们知道,如果拷贝1,会把3给覆盖,拷贝2,会把4给覆盖。

该怎么拷贝才能实现不被覆盖呢?

可以从后向前拷贝,先拷贝5,覆盖7,在拷贝4覆盖6,这时候在拷贝3覆盖5,拷贝2覆盖4,拷贝1覆盖3,由于3,4,5已经拷贝完成,不会出现还没有拷贝就被覆盖的情况。

那是不是从后向前拷贝就一定正确呢?

我们在换一种情况试试:

这时候如果还是从后向前拷贝的话会出现什么问题呢?

8拷贝到6,7拷贝到5,这时候向拷贝6的时候已经变成了8,因此从后向前失效了。

这时候我们在从前向后拷贝,3拷贝到1,4拷贝到2……恰好可以全部拷贝。

不知道大家有没有发现一个规律:

如果dest在src的后面,则从后向前拷贝;

如果dest在src的前面,则从前向后拷贝;

如果没有重叠,则随意。

如果是 后->前,该怎么拷贝呢?

比如说先拷贝5,我们只需要在起始位置跳过num个字节即可

比如说:*((char*)src + num)

代码实现:

<code>#include <stdio.h>

#include <string.h>

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;

}

}

else

{

while (num--)

{

//后->前

//num进来减1,变为19,src加上19后跳到最后一位上,也就是5,dest加上19跳到8的位置,然后将5赋值到8的位置

*((char*)dest + num) = *((char*)src + num);

}

}

return ret;

}

int main()

{

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };

my_memmove(arr+2, arr, 5 * sizeof(int));

return 0;

}

三、memset函数的使用

1、memset函数的介绍(cplusplus)

参数介绍:

注意:

memset是以字节为单位来设置内存的 ,而不是以一个元素为单位设置的。

 作用介绍:

返回值介绍:

2、memset函数的使用

2.1 memset函数对数组的应用

那么memset函数究竟有什么作用呢?

比如说:

<code>#include <stdio.h>

int main()

{

int arr[10] = { 0 };

int i = 0;

for (i = 0; i < 10; i++)

{

arr[i] = i + 1;

}

return 0;

}

我们想将arr数组全部初始化为0,我们该怎么做呢?

你可能会说这不简单?直接使用循环不就可以了吗?

for (i = 0; i < 10; i++)

{

arr[i] = 0;

}

这样的确可以,不过我们也可以使用库函数memset函数来实现这个操作。

我们要设置的这个空间整型数组arr[10]的地址交给ptr,而数组的地址就是数组名arr,我们需要将该数组的元素都变为0,也就是要设置的值value为0,由于是整型数组,有十个元素,所以num就等于40字节。

代码展示:

#include <stdio.h>

#include <string.h>

int main()

{

int arr[10] = { 0 };

int i = 0;

for (i = 0; i < 10; i++)

{

arr[i] = i + 1;

}

/*for (i = 0; i < 10; i++)

{

arr[i] = 0;

}*/

memset(arr, 0, 10 * sizeof(int));

for (i = 0; i < 10; i++)

{

printf("%d ", arr[i]);

}

return 0;

}

调试过程:

结果展示:

 如果memset(arr, 1, 10 * sizeof(int));这是否是将每一个元素都改为1了呢?

调试监控窗口

为什么没有达到想要的结果呢?

我们在来看一下内存窗口:

破案了,memset函数将每一个字节都设置为1,而不是把一个元素设置为1。

前面强调了memset是以字节为单位来设置内存的 ,而不是以一个元素为单位设置的。

2.2 memset函数对字符串的应用

代码:

<code>#include <stdio.h>

#include <string.h>

int main()

{

char arr[] = "hello world";

//如何将helo改为五个x

memset(arr, 'x', 5);

return 0;

}

调试结果:

如果我们想修改world呢?

<code>memset(arr+6, 'x', 5);

从前向后数hello五个字符,还有一个空格,共6个字符。

注意:这个函数比较常见,因此需要熟练掌握!!! 

 四、memcmp函数的使用

memcmp函数与之前学习的strcmp函数的功能是比较相似的,不过strcmp函数只能用来做字符串的比较,而memcmp函数是用来做内存块的比较,不论是什么类型。

1、memcmp函数的介绍

参数介绍:

 

返回值介绍:

2、memcmp函数的使用

直接上例题,比较arr1与arr2中前3个元素

<code>#include <stdio.h>

#include <string.h>

int main()

{

int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };

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

int ret = memcmp(arr1, arr2, 12);

printf("%d\n", ret);

return 0;

}

结果:

如果我们比较前4个元素呢?

int ret = memcmp(arr1, arr2, 16);、

这里返回的就是1了。


结语:本篇文章到这里就结束啦!期待下次的相遇!!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。