赞
踩
在C语言的内存管理领域,四大秘境之一的内存操作函数无疑为程序员提供了强大的工具。这些函数——memcpy、memmove、memset、memcmp——各自拥有独特的用途和特性,它们在内存操控中扮演着至关重要的角色。
目录
在前面我们讲了许多字符串函数:
其中我们发现strcpy和memcpy非常相似,我们了解了str代表字符串,cpy代表copy,也就是拷贝,那mem是什么呢?其实它是memory的简写,memory我们都知道是记忆的意思,而它还有一个意思——内存。可以简单理解为计算机的记忆,那不就是内存嘛。那我们可以推断出来,memcpy,它的作用是用来拷贝内存的。
它的原型如下:
void* memcpy(void* destination, const void* source, size_t num);
不难看出,它和strncpy的参数是一模一样的,没错,正是如此,只不过它可以copy任意类型的,而strncpy只能拷贝字符串。
1.原理:memcpy从source的位置开始,向后复制num个字节,到destination指向的内存的位置。
2.与strncpy不同的是,mem在遇到'\0'时不会停止。
3.使用需要包含头文件<string.h>。
- #include <stdio.h>
- #include <string.h>
- int main()
- {
- int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
- int arr2[10] = { 0 };
- memcpy(arr2, arr1, 20);
- int i = 0;
- for (i = 0; i < 10; i++)
- {
- printf("%d ", arr2[i]);
- }
- return 0;
- }
运行结果:
聊完它的基本原理以及使用后,我们再来看看它的模拟实现。
首先通过参数来看,我们传进去一个目的地,一个源头,还有字节数,那我可以这么写:
void* memcpy(void* dst, const void* src, size_t count)
接下来我们可以使用循环,次数为count,由于count是字节数,所以我们这里一个字节一个字节进行拷贝会比较容易操作。而void*类型的指针不可以解引用,我们需要进行强制类型转换,将其转换成char*,代码如下:
- void* memcpy(void* dst, const void* src, size_t count)
- {
- void* ret = dst;
- assert(dst);
- assert(src);
- /*
- * copy from lower addresses to higher addresses
- */
- while (count--) {
- *(char*)dst = *(char*)src;
- dst = (char*)dst + 1;
- src = (char*)src + 1;
- }
- return(ret);
- }
这里返回ret(目标空间的首元素地址)是因为在memcpy中返回的也是这个:
但是问题出现了,如果目标空间和源头重叠了怎么办?
我们来看下面一段代码:
- #include <stdio.h>
- #include <string.h>
- #include<assert.h>
- void* my_memcpy(void* dst, const void* src, size_t count)
- {
- void* ret = dst;
- assert(dst);
- assert(src);
- /*
- * copy from lower addresses to higher addresses
- */
- while (count--) {
- *(char*)dst = *(char*)src;
- dst = (char*)dst + 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(arr1+2, arr1, 20);
- int i = 0;
- for (i = 0; i < 10; i++)
- {
- printf("%d ", arr1[i]);
- }
- return 0;
- }
我们可以看到目标空间与源头有重叠的部分,本来我们想把1,2,3,4,5拷贝到3,4,5,6,7的位置。
预期结果:1,2,1,2,3,4,5,8,9,10
实际结果:
这是为什么呢?这是因为在拷贝3的时候,3的位置已经是1了,3被覆盖掉了,其他的同理。这可怎么办呢?其实memcpy它被设计出来就是为了拷贝不重叠的情况的,如果它拷贝到重叠的部分,结果都是未定义的。那我们就没办法完成拷贝重叠部分了嘛?别着急,下面这个函数就可以解决这个问题。
memmove和memcpy几乎是一模一样的,唯一的差别就是它可以处理重叠的源内存块和目标内存块。
void* memmove(void* destination, const void* source, size_t num);
来看刚刚的问题的代码解决:
- #include <stdio.h>
- #include <string.h>
- int main()
- {
- int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
- memmove(arr1 + 2, arr1, 20);
- int i = 0;
- for (i = 0; i < 10; i++)
- {
- printf("%d ", arr1[i]);
- }
- return 0;
- }
运行结果:
它是怎么实现的呢?我们试着来模拟一下。
我们先来画图解析一下:
我们正常来讲是将1拷贝到3,2拷贝到4,是正着拷的。那如果我们反过来,先把5拷过去,倒着拷是不是就避免了被重叠覆盖的情况了。
这里有的人就耍小聪明了,就喜欢死记硬背了,正着拷不行,那就都倒着拷。
那再来看这张图:
如果我还是倒着拷的话,还是会覆盖。
所以我们得到了一个结论:当dest在src左面时,从前向后拷(正着拷);当dest在src右面时,从后向前拷(倒着拷)。
当你不理解的时候,画个图自己捋一下就好了。
思路有了,我们来看代码:
- void* memmove(void* dst, const void* src, size_t count)
- {
- void* ret = dst;
- if (dst <= src || (char*)dst >= ((char*)src + count)) {
-
- //正着拷形式如同memcpy
-
- while (count--) {
- *(char*)dst = *(char*)src;
- dst = (char*)dst + 1;
- src = (char*)src + 1;
- }
- }
- else {
-
- //倒着拷将首元素地址+字节数再-1即可指向最后一个字节。
-
- dst = (char*)dst + count - 1;
- src = (char*)src + count - 1;
- while (count--) {
- *(char*)dst = *(char*)src;
- dst = (char*)dst - 1;
- src = (char*)src - 1;
- }
- }
- return(ret);
- }
从名字上来看,set译为设置,那这个函数的作用大概率是设置内存的。
void* memset(void* ptr, int value, size_t num);
首先来看它的三个参数:
1.ptr:一个void*类型的指针,指向要被填充的内存块。
2.value:要设置的值。
3.num:字节的个数(设置多少个value的值,以字节为单位)
也就是说memset是⽤来设置内存的,将内存中的值以字节为单位设置成想要的内容。
它包含在头文件<string.h>中。
因此memset经常被用来写游戏外挂。
我们来看代码实例:
- #include <stdio.h>
- #include <string.h>
- int main ()
- {
- char str[] = "hello world";
- memset (str,'x',6);
- printf(str);
- return 0;
- }
运行结果:
切记:memset以字节为单位设置,若用于数组中,绝不是以元素为单位!!
经过这么多次的学习,我们已经对mem和cmp非常熟悉了,一个是内存,一个是比较。显然这个函数是用来比较两个内存块的大小的。
int memcmp(const void* ptr1, const void* ptr2, size_t num);
原理:⽐较从ptr1和ptr2指针指向的位置开始,向后的num个字节
返回值如下:
其实和strcmp相类似。
代码示例:
- #include <stdio.h>
- #include <string.h>
- int main()
- {
- char buffer1[] = "DWgaOtP12df0";
- char buffer2[] = "DWGAOTP12DF0";
- int n;
- n = memcmp(buffer1, buffer2, sizeof(buffer1));
- if (n > 0)
- printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
- else if (n < 0)
- printf("'%s' is less than '%s'.\n", buffer1, buffer2);
- else
- printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
- return 0;
- }
来看运行结果:
完
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。