函数】"/>
【内存操作函数】
学习了字符串函数之后,紧接着向大家分享内存操作函数。也希望大家能够指出文章中不足的地方。
目录
1 memcpy
2 memmove
3 memset
4 memcmp
1 memcpy
void * memcpy ( void * destination, const void * source, size_t num );
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。
memcpy的使用:
#define _CRT_SECURE_NO_WARNINGS 1
#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, 40);return 0;
}
通过调试我们可以很直观的看出来arr2的改变:
memcpy不仅可以拷贝整形数组,浮点型,字符型,结构体等等类型都可以拷贝,具体的使用方法与上面类似,大家可以自己去试试。
memcpy的自己实现:
通过上面的演示,我们已经知道memcpy可以拷贝多种类型的数据,那么究竟如何定义memcpy函数才能让其接受多种数据类型呢?
在以前学习回调函数以及自己实现快速排序的时候我们已经知道可以用void*接受任意地址,这里也同样不例外,我们仍然需要用void*来接受所传入的地址。
具体代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>
void MyMemcpy(void* dest, const void* src, size_t count)
{assert(dest);assert(src);void* ret = dest;while (count--){*(char*)dest = *(char*)src;++(char*)dest;++(char*)src;}return ret;
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };int arr2[10] = { 0 };//memcpy(arr2, arr1, 40);MyMemcpy(arr2, arr1, 40);return 0;
}
由于count代表的是字节数,每次-1只能减去一个字节,所以为了方便计算,我们将dest和src强制转换成char*类型,每当count-1时会将src一个字节的内容拷贝到dest中,直至拷贝完成。
这里又有了一个疑问:能自己拷贝自己吗?
例如:arr1[]={1,2,3,4,5,6,7,8,9,10},将1,2,3,4,5的内容拷贝到6,7,8,9,10中
在我们自己实现的函数中,好像可以实现:
那如果将arr1中1,2,3,4,5的内容拷贝到3,4,5,6,7中是否可行呢?
但是答案好像与我们预期结果不太吻合呀,按照我们的预期的预期得到的应该是{1,2,1,2,3,4,5,8,9,10} 但是实际结果却是{1,2,1,2,1,2,1,8,9,10},究竟是为什么呢?
由于我们是从前向后一个字节一个字节进行拷贝的,我们首先将1赋给3,然后将2赋给4,我们将3赋给5的时候3其实在之前已经被改成了1,我们就将1赋给了5。同理,又将2赋给了6,1赋给了7.
那我们可以知道我们自己定义的memcpy好像能够拷贝内存不重叠的情况,不能拷贝内存重叠的情况。
那我们使用vs自己本身用的memcpy来测试一下:
好像这个结果与我们自己定义的结果不一样,是我们自己定义错了吗?
c语言标准规定:
memcpy只要处理不重置的内存拷贝就行
memmove是处理重置内存拷贝的
我们定义的memcpy其实是没有问题的,只不过不同编译器对memcpy的定义不同,但都遵循c标准,在vs中我们能够用memcpy拷贝内存重置的情况,但是在其他编译器下就不一定能够成功。
2 memmove
有了上面的了解我们知道了,memmove是专门用来拷贝重置字符串的,它的使用相信大家都已经有了了解,那我们应该如何来实现它呢?
arr1={1,2,3,4,5,6,7,8,9,10} ,我们依旧是将1,2,3,4,5的内容拷贝到3,4,5,6,7中,我们如果依旧是从前向后一个字节一个字节拷贝的话,那么结果肯定是不对的,但是如果我们从后向前一个字节一个字节进行拷贝的话:将5赋给7,4赋给6,3赋给5,2赋给4,1赋给3,这样我们的结果是正确的。那么如果将3,4,5,6,7拷贝到1,2,3,4,5中如果从后向前拷贝的话好像又会出问题,从前向后才是正解。
那么我们如何划分界限呢?
是这样的:我们发现,如果dest>src && dest<src+count,从后向前比较合理.如果dest<src,从前向后比较合理.如果dest>src+count(表示不重叠),两种方法都行,有了这个理解之后我们就可以写代码了:
void MyMemmove(void* dest, const void* src, size_t count)
{assert(dest);assert(src);void* ret = dest;if (dest < src){while (count--){*(char*)dest = *(char*)src;++(char*)dest;++(char*)src;}}elsewhile (count--){*((char*)dest + count) = *((char*)src + count);}return ret;
}
int main()
{int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };//int arr2[10] = { 0 };//memcpy(arr2, arr1, 40);//MyMemcpy(arr1+2, arr1, 20);//memcpy(arr1 + 2, arr1, 20);MyMemmove(arr1, arr1+2, 20);return 0;
}
其中if else语句中,if的条件还可以换成(dest<src ||(char*)dest>(char*)src+count) ,只是选定的条件不同,结果都是正确的。
3 memset
void *memset( void *dest, int c, size_t count );
- dest: Pointer to destination
- c: Character to set
- count: Number of characters
其中c表示设置的字符
我们可以用代码来更好的理解memset的用法。
通过调试我们可以知道arr中每一个元素都被改成了'#',我们也可以试试整形数组:
为什么数组中会出现那么奇怪的数字呢?
我们知道该整形数组大小为40个字节,现在我们只改变了前10个字节的内容,将每个字节的内容都改成了1,也就是前10个字节的内容变成了01010101 01010101 0101 .将其提取出来时arr[0]
中存放的数字16进制为01010101 ,arr[1]中存放的数字16进制为01010101,arr[2]中存放的数字16进制为00000101,将其转化成10进制就是上面的数字。
4 memcmp
int memcmp ( const void * ptr1,const void * ptr2, size_t num );
比较从ptr1和ptr2指针开始的num个字节
#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;
}
这个函数与strncmp有些类似,只是memcmp可以比较各种类型的变量,而strncmp只是针对字符串比较大小。
好了,今天的分享就到这里了,如果觉得该文章对你有帮助的话,希望多多支持一下,一起共同进步!
更多推荐
【内存操作函数】
发布评论