在此之间,我们学过许多的字符函数,例如strcpy,strstr,strcmp等等,这里我们以strcpy函数进行举例:
//实现将arr1拷贝到arr2中 int main() { int arr1[5] = { 1,2,3,4,5 }; int arr2[5] = { 0 }; strcpy(arr2, arr1); return 0; }
程序并没有正确运行,编译器报错:
错误分析如下:
上述代码是无法实现将arr1拷贝到arr2中,由此可见,像strcmp,strcpy,strstr这种字符串函数,它虽然能够实现字符串拷贝,比较等功能,但是由于它们的操作对象是字符串,因此对于整形数组,浮点型数组等并不适用。
那么对于整形数组,浮点型数组等其他的数据,我们该如何进行操作呢?
c语言给出了另一类函数-------内存函数(memcpy/memmove/memcmp)
memcpy:
//void*-----通用类型指针:可以接受各种类型的参数 void*memcpy(void*destination,const void*source,size_num)//size_num的单位是字节
还是选用上述实例:
#include<stdio.h> #include<string.h> //实现将arr1拷贝到arr2中 int main() { int arr1[5] = { 1,2,3,4,5 }; int arr2[5] = { 0 }; memcpy(arr2, arr1,sizeof(arr1)); return 0; }
此时打开监视窗口,我们不难发现,数组之间完美的实现了拷贝。
结构体类型实现拷贝:
举例:
struct person { int age; char name[20]; char phone[13]; }; int main() { struct person arr3[] = { {20,"张三","20031319"},{19,"lisa","193684"} }; struct person arr4[3] = {0}; memcpy(arr4, arr3, sizeof(arr3)); }
此时打开监视窗口,我们不难发现,结构体之间完美的实现了拷贝。
模拟实现memcpy:
以整形进行举例:
#include<stdio.h> #include<assert.h> void* my_memcpy(void* dest, const void* scr,size_t num) { void* ret = dest; //断言:为避免是空指针 assert(dest != NULL); assert(scr != NULL); while (num--)//num为要拷贝的长度 { //不能直接解引用操作的原因:void*类型不能直接进行++/--操作 *(char*)dest = *(char*)scr;//先强制类型转换在解引用 dest=(char*)dest+1; scr=(char*)scr+1; } return ret;//不能直接返回dest,因为此时的dest并不是首地址 } int main() { int arr1[] = { 1,2,3,4,5 }; int arr2[5] = { 0 }; my_memcpy(arr2, arr1,sizeof(arr1)); for (int i = 0; i < 5; i++) { printf("%d ", arr2[i]); } return 0; }
1 2 3 4 5
上述实例,我们是实现的功能是将一个数组中的元素拷贝到另一个数组中,如果现在我们实现将数组中的前几个元素拷贝到后面的几个呢?
举例:
//实现将1 2 3 4 5拷贝到3 4 5 6 7的位置上 #include<stdio.h> #include<assert.h> void* my_memcpy(void* dest, const void* scr,size_t num) { void* ret = dest; assert(dest != NULL); assert(scr != NULL); while (num--) { *(char*)dest = *(char*)scr; dest=(char*)dest+1; scr=(char*)scr+1; } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memcpy(arr1+2, arr1,20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
预期输出结果为:
1 2 1 2 3 4 5 8 9 10
实际输出结果:
1 2 1 2 1 2 1 8 9 10
预期和实际不相符分析如下:
由此可得出:我们所编写的my_memcpy函数并不能实现在同一个数组中的拷贝,会出现数的覆盖现象。
那库函数memcpy能否实现呢?
#include<stdio.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; memcpy(arr1+2, arr1,20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
输出结果如下:
1 2 1 2 3 4 5 8 9 10
看到这里,相信很多人都会产生疑惑,为什么库函数memcpy可以实现,而我们编写的my_memcpy不能实现呢?难道是我们写错了?
事实并非如此,memcpy虽然也实现了这样的功能,但C语言标准规定:memcpy:用来处理不重叠的内存拷贝。memmove:处理重叠的内存拷贝
我们所编写的my_memcpy是因为严格按照C语言的标准所编写,而在VS编译器上memcpy超额完成了任务,相当于抢了memmove的饭碗。
下面我们就来学习memmove函数!
memmove:
依然是上述实例:
#include<stdio.h> int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; memmove(arr1+2, arr1,20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
输出结果如下:
1 2 1 2 3 4 5 8 9 10
现在得出的结果正是我们预期的效果!
模拟实现memmove:
目的地的地址低于源头地址:
正序进行拷贝:
#include<stdio.h> #include<assert.h> my_memmove(void* dest, const void* scr, size_t count) { assert(dest != NULL); assert(scr != NULL); void* ret = dest; while (count--) //直接按照源头一个个进行打印,不存在数的覆盖 { *(char*) dest= *(char*) scr; dest = (char*)dest + 1; scr = (char*)scr + 1; } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1, arr1+2,20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
这种比较简单,这里就不赘述分析过程了
输出结果为:
3 4 5 6 7 6 7 8 9 10
目的地址高于源头地址:
倒序进行拷贝:
#include<stdio.h> #include<assert.h> my_memmove(void* dest, const void* scr, size_t count) { assert(dest != NULL); assert(scr != NULL); void* ret = dest; while (count--) { *((char*)dest + count) = *((char*)scr + count); } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1+2, arr1,20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
输出结果为:
1 2 1 2 3 4 5 8 9 10
对于该行代码的实现过程,我们分析如下:
*((char*)dest + count) = *((char*)scr + count);
对于上述两种情况,我们可用一个程序进行表示:
#include<stdio.h> #include<assert.h> my_memmove(void* dest, const void* scr, size_t count) { assert(dest != NULL); assert(scr != NULL); void* ret = dest; if(dest<scr)//正序进行拷贝 while (count--) { *(char*)dest = *(char*)scr; dest = (char*)dest + 1; scr = (char*)scr + 1; } else//倒序进行拷贝 { while(count--) { *((char*)dest + count) = *((char*)scr + count); } } return ret; } int main() { int arr1[] = { 1,2,3,4,5,6,7,8,9,10 }; my_memmove(arr1+2, arr1,20); for (int i = 0; i < 10; i++) { printf("%d ", arr1[i]); } return 0; }
输出结果如下:
1 2 1 2 3 4 5 8 9 10
memcmp:
将两个存储区的前n个字节进行比较。
举例:
#include<stdio.h> int main() { int arr[] = { 1,2,3,4,5 }; int arr2[] = { 1,2,5,4,3 }; int ret=memcmp(arr, arr2, 9);//arr>arr2,返回大于零的数字,arr<arr2,返回小于零的数字,二者相等,返回0 //注:VS编译器返回-1/0/1,虽然不严谨,但是并不违背C语言的标准规定 printf("%d\n", ret); return 0; }
输出结果如下:
-1
分析如下:
memset:内存设置函数
举例:
字符型:
#include<stdio.h> int main() { char arr[10] = ""; memset(arr, '#', 10);//10代表更改10个字节 return 0; }
整形:
#include<stdio.h> int main() { int arr[10] = {0}; memset(arr, 1, 10); return 0; }
输出如下:
让不少人产生疑惑的是:为什么此时并没有实现将数组中的10个元素都修改为1呢?
原因是:该函数的操作单位是字节,而数组是一个整形数组,其中的元素都为整形,每个元素为4个字节。
具体分析如下:
因此,在使用该函数的时候,一定要注意类型。