1.memcpy
void * memcpy ( void * destination, const void * source, size_t num );
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
这个函数在遇到 '\0' 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
接下来我们使用一下这个函数:
1. #include<string.h> 2. int main() 3. { 4. int arr1[10] = { 0 }; 5. int arr2[] = { 1,2,3,4,5 }; 6. memcpy(arr1, arr2, 20); 7. return 0; 8. }
经过调试之后可以发现arr1里面存放了1,2,3,4,5,这个就是memcpy的基本用法。
2.模拟实现memcpy
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<assert.h> #include<string.h> void* my_memcpy(void* dest, const void* src, size_t num) { assert(dest && src); while (num) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; num--; } } int main() { int arr1[10] = { 0 }; int arr2[] = { 1,2,3,4,5 }; my_memcpy(arr1, arr2, 20); return 0; }
src里面的内容是不会被改变的,所以使用const限制一下。num是拷贝的字节数,我们每次拷贝一个字节。先使用while循环,循环num次,每次num--,dest和src是void*的指针,我们强制类型转换为char*的指针,然后解引用,将src赋给dest,dest和src这时就应该往后走一个字节,因为强制类型转换是临时的,所以还是需要强制类型转换为char*,然后+1。
当我们想把1,2,3,4,5放到3,4,5,6,7的位置上时就会出现问题,当空间不重叠内存的时候使用memcpy是可以的,像这种不重叠内存的拷贝需要使用memmove。
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; memcpy(arr + 2, arr, 20); return 0; }
3.memmove
void * memmove ( void * destination, const void * source, size_t num );
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。
int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; memcpy(arr + 2, arr, 20); return 0; }
使用memmove的话就能很好的解决上面的问题。
4.模拟实现memmove
我们先来想一下,如果有一个数组:
int arr[]={1,2,3,4,5,6,7,8,9,10};
假如我们要把1,2,3,4,5拷贝到3,4,5,6,7这个位置上去,使得数组变成:
int arr[]={1,2,1,2,3,4,5,8,9,10};
那我们就需要先将5放在7的位置上,4放在6的位置上,以此类推,才能达到效果,也就是从后向前拷贝。
如果我们要把3,4,5,6,7拷贝到1,2,3,4,5这个位置上,就要先将3放到1的位置上,以此类推,也就是从前往后拷贝。
所以说拷贝的方式会根据需求而改变。
那么什么时候从前往后拷贝,什么时候从后往前拷贝呢?
还是用一个数组举例:
int arr[]={1,2,3,4,5,6,7,8,9,10};
如果dest这块空间在src这块空间的左边且重叠,也就是要把高地址的数据拷贝到低地址处,就需要从前往后拷贝。
如果dest这块空间在src这块空间的右边且重叠,也就是要把低地址的数据拷贝到高地址处,就需要从后往前拷贝。
如果dest这块空间和src这块空间不重叠,不管从前往后还是从后往前都是可以的,当然是在空间充足的情况下,不能越界。
一开始我们还是需要保存起始位置的地址,用作返回值。使用if判断dest是否小于src,如果小于则进入for循环,循环sz次,这里的方法是跟上面的memcpy一样的。当dest>src时,我们使用while循环比较方便,因为我们从后向前拷贝是先将后面的数据开始,所以当我们将条件设置成sz--时,由于是后置--,也是循环sz次,但是*((char*)src + sz)就刚好能够拿出最后一个数据拷贝到*((char*)dest + sz),当然使用for循环也是可以的。
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> void* my_memmove(void* dest,const void* src,size_t sz) { assert(dest && src); void* ret = dest; if (dest < src)//从前向后 { for (int i = 0; i < sz; i++) { *(char*)dest = *(char*)src; dest = (char*)dest+1; src = (char*)src + 1; } } else//从后向前 { while (sz--) { *((char*)dest + sz) = *((char*)src + sz); } } return ret; } int main() { int arr[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr, arr + 2, 20); for (int i = 0; i < 10; i++) { printf("%d ", arr[i]); } return 0; }
5.memset
void * memset ( void * ptr, int value, size_t num );
这个函数就是将ptr指向的前num个字节的数据设置成指定的值(value),再把起始位置返回。
下面是memset的使用:
int main() { char arr[] = "hello world"; memset(arr + 6, 'x', 3); printf("%s\n", arr); return 0; }
但是要注意的是num的单位是字节。如果我们想要把这个数组的内容设置成10个1,像下面这样就已经出错了,这样是把每个字节都设置成1,而不是整型。
1. int main() 2. { 3. int arr[10] = { 0 }; 4. memset(arr, 1, 40); 5. return 0; 6. }
6.memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
memcmp这个函数就是比较两块空间的前num个字节的内容,如果一样则返回0,小于则返回<0,大于则返回>0。需要注意的是在比较时也是一对字节进行比较,和strcmp的区别就是strcmp是进行字符的比较,而memcmp是进行内存块的比较。
#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[] = { 1,2,3,4 }; int ret=memcmp(arr1, arr2, 16); printf("%d ", ret); return 0; }