【C语言进阶篇】C语言内存函数
目录
1.memcpy函数及其模拟实现
1.1 memcpy函数的使用
1.2 memcpy函数的模拟实现
2.memmove函数及其模拟实现
2.1 memmove函数的使用
2.2 memmove函数的模拟实现
3.memset函数
4.memcmp函数
1.memcpy函数及其模拟实现
1.1 memcpy函数的使用
memcpy函数是用来拷贝内存的函数,其拷贝的方式为一个字节一个字节地拷贝,使用时需要包含 的头文件。上面的 size_t num 参数的作用就是接收我们输入的想要拷贝的字节数;destination的意思是目的地,void* destination 参数是拷贝存放的目的地;source是起源的意思,const void* source就是我们所要拷贝的数据的起始地。两个接收地址的变量用 void* 类型来定义,说明可以接收不同类型的地址。来看看具体使用:
这里我们将 arr 数组里20个字节的内存拷贝到 arr1 数组中去,因为arr数组的类型为int型,每个元素的大小为4个字节,因此,拷贝20个字节的内容相当于拷贝了 arr 中5个元素进 arr1 中。
需要注意的是,如果 arr1 数组中本身有元素,在拷贝的时候,拷贝过去的元素会覆盖掉 arr1 中原有的元素:
操作字符串:
1.2 memcpy函数的模拟实现
下面我们来自己动手模拟实现一下memcpy函数:
//模拟实现memcpy函数 void* my_memcpy(void* dest, const void* src, size_t num) { void* ret = dest; //这一步是为了保存dest最开始指向的地址,因为在后续循环里的操作里, //dest的地址已经跑到后面去了,不指向最开始的地址 while (num--)//这里是总共循环的次数,需要拷贝多少个字节就循环多少次 { *(char*)dest = *(char*)src; // 因为dest与src都是void*类型,没法直接解引用操作,又因为 // memcpy函数是一个字节一个字节拷贝的,因此我们在这进行拷贝操作的时候 //将dest与src都强制类型转换为char*类型,这样就可以达到一个字节一个字节拷贝的目的了 dest = (char*)dest + 1; src = (char*)src + 1; //这里的操作是在拷贝完一个字节以后,dest与src的地址都+1 //去找下一个字节地址,进行下一个字节的拷贝 } return ret; } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; int arr1[10] = { 0 }; my_memcpy(arr1, arr, 20); int i = 0; for (i = 0; i2.memmove函数及其模拟实现
2.1 memmove函数的使用
memmove函数的作用和memcpy函数几乎一样,使用时也需要包含的头文件但是memcpy函数相比于memmove函数来讲有一个缺陷,下面来看一段代码:
这里我们想要将 arr 数组中 1,2,3,4,5 的数据拷贝到 arr 数组中 3,4,5,6,7 的位置,那么结果按理来说应该最后输出打印的是 1,2,1,2,3,4,5,8,9,10。但实际上跟我们想的一样吗?
没错,想的和我们就是一样的,可能这个时候就有人想说我在水字数了。哎!此言差矣。虽然在这里最后输出的结果和我们设想的是一样的,但是,这仅是在VS编译器环境下一样而已,换做别的编译器,可能最后输出的就是下列结果:
为什么可能输出这个结果呢?我们来画图理解:
那么我们如果想要在一个数组里实现我们想要的效果,该怎么办呢?
这就需要用到memmove函数了,memmove函数就可以专门用来处理这种拷贝内存与存放内存有重叠的情况:
在VS编译器中,memcpy函数也能够完成memmove的作用,我们不妨大胆猜测,在VS中memcpy函数的实现是和memmove函数一样的。
2.2 memmove函数的模拟实现
下面我们来手动模拟实现一下memmove函数。
在模拟实现memmove函数之前,我们需要搞清楚memmove函数的原理,memmove函数的模拟实现相较于memcpy函数更为复杂,我们来逐个讨论:
①
如何避免在拷贝内存的时候将后面需要拷贝的内存给改变呢?就像上面所说的改为 1,2,1,2,1,2,1,8,9,10 的情况。这时候我们可以想到,将内存从后往前拷贝不就行了,就像这样:
②
用从前往后的方法就可以达到我们上面的目的,但是,这里又会出现一个新的问题,如果我需要拷贝的内存在存放的内存后面呢,就像下面这种情况:
这种情况从前往后存还适用吗?我们画图来探究一下:
显然,这个时候再采用从后往前的方法就不行了。既然从前往后不行,那我们从前往后可行吗?画图探究下:
可以发现,这种情况从前往后拷贝存储是可行的,那么除了这两种情况还有别的情况吗?没有了,需要拷贝的内存和拷贝内存目的地重复的情况一共就三种: ① 需要拷贝的内存在拷贝内存目的地前并且两者有重复部分 ② 需要拷贝的内存在拷贝内存目的地后并且两者有重复部分 ③ 两者完全重复 。而第三种情况不管怎么拷贝都能达到我们的目的,因此,在模拟实现memmove函数时,我们只需要考虑前两种情况即可。下面来看代码:
void* my_memmove(void* dest, const void* src, size_t num) { if (dest > src)//拷贝内存在拷贝存放的内存的前面,并且有内存叠加 { dest = (char*)dest + num - 1;//将dest和src分别指向各自的最后一个字节 src = (char*)src + num - 1; while (num--)//将内容一个字节一个字节进行拷贝 { *(char*)dest = *(char*)src; dest = (char*)dest - 1; src = (char*)src - 1; } } else { while (num--)//拷贝内存在拷贝存放的内存的后面,并且有内存叠加 { *(char*)dest = *(char*)src;//直接从第一个字节拷贝 dest = (char*)dest + 1; src = (char*)src + 1; } } } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr+2, arr, 20); int i = 0; for (i = 0; i3.memset函数
memset函数顾名思义就是用来设置内存,将需要设置的内存中的值按照字节来设置成想要的内容,使用时也需要包含的头文件。
这里我们用memset函数将字符串s的前三个字节的内容改为了字符 ' x '。
4.memcmp函数
memcmp函数是用来比较内存的,用法与strcmp函数十分相似,不同的是,strcmp函数是专门用来比较字符串的函数,而memcmp函数则可以用来比较各种类型。
这里的意思就是:当所比较的两个字符串,前一个大于后一个则返回一个大于0的数,反之则返回小于0的数,相等则返回0。
这里我们调用的memcmp的时候,将 s 与 s1 进行比较,如果 s > s1 ,则返回一个大于0的数,反之,则返回小于0的数,如果相等,则返回0。这里 s 的第一个字符是a,而 s1 的第一个字符是b,在第一个字符就判断出了大小,后面也不用判断了,直接返回了小于0的数。
创作不易,点个赞再走呗,求求啦~😊