本章概述
本章函数概述
我们在本章的博客中讲的内容是有关内存的操作,我们 直接通过内存块对数据进行操作。 因为我们是直接对内存块操作,所以可以对任意类型数据进行操作(我们没必要管它是什么类型的数据,我们只对这个内存块操作)。接下来所讲的这些内存函数的头文件为:<string.h>,这些内存函数是对每个字节进行操作。
memcpy使用和模拟
- memcpy的功能:和
strcpy
函数的功能类似,strcpy
函数是对元素进行拷贝,而memcpy
函数是对内存块进行拷贝,所以内存块里面的数据类型没意义。比如,前面咱们讲过了strcpy和strncpy函数,它们只能对字符串进行拷贝。但是,当我们想进行两个整形数据的拷贝时候,就不能用strcpy和strncpy函数了,就只能用memcpy函数了。memcpy
函数能拷贝的数据类型比strcpy和strncpy函数拷贝的数据库类型要广泛些。 - memcpy函数的结构:
// void * memcpy ( void * destination, const void * source, size_t num );
// 因为我们进行内存块的拷贝,所以里面是什么类型的数据咱们是不知道的,所以当你传给我地址的时候我只能用void*指针来接收。
// 因为咱们是进行内存块的拷贝,所以要知道具体拷贝多少个字节的空间,size_t num 就是要拷贝的字节数。
// 返回的是传给des的首元素地址,因为是什么类型咱不知道,所以返回void*指针数据类型。
// des和source的地址是可以被我们指定的。
由于size_t num是表示要拷贝的字节数,咱们是第一次见到它的这种意义,所以我给大家放个它的截图,给大家看一下。如图:
- memcpy函数的使用:----->对字符串的拷贝。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdefghu";
char arr2[30] = {0};
void* p = memcpy(arr2, arr1, 4 * sizeof(char));
printf("%s\n", (char*)p);
return 0;
}
结果运行图:
------->对整形数据进行拷贝
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr3[] = { 1,2,3,4,5,6 };
int arr4[10] = { 0 };
int sz = sizeof(arr4) / sizeof(arr4[0]);
memcpy(arr4, arr3, 4 * sizeof(int));
printf("arr4: ");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr4[i]);
}
return 0;
}
结果运行图:
- memcpy函数的模拟:因为这个函数对内存进行操作,所以它能够拷贝多种数据类型。模拟的核心思想:我们要对单个内存进行操作,也就是把各种指针类型全部转换为
char *
(因为char *
能够访问单个字节),进行代码展示:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* des, const void* str, size_t num)
{
void* p = des;
assert(des && str);
while (num--)
{
*(char*)des = *(char*)str;
des = (char*)des + 1;
str = (char*)str + 1;
}
return p;
}
int main()
{
int arr3[] = { 1,2,3,4,5,6 };
int arr4[10] = { 0 };
int sz = sizeof(arr4) / sizeof(arr4[0]);
my_memcpy(arr4, arr3, 4 * sizeof(int));
printf("arr4: ");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr4[i]);
}
return 0;
}
结果运行图:
- 拓展使用:------->当我们用memcpy函数进行自己给自己拷贝数据(重叠数据拷贝)。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr3) / sizeof(arr3[0]);
memcpy(arr3+2, arr3, 4 * sizeof(int));
printf("arr4: ");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr3[i]);
}
return 0;
}
结果运行图:
--------->当我们用my_memcpy函数进行自己给自己拷贝数据(重叠数据拷贝)。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* des, const void* str, size_t num)
{
void* p = des;
assert(des && str);
while (num--)
{
*(char*)des = *(char*)str;
des = (char*)des + 1;
str = (char*)str + 1;
}
return p;
}
int main()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr3) / sizeof(arr3[0]);
my_memcpy(arr3+2, arr3, 4 * sizeof(int));
printf("arr4: ");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr3[i]);
}
return 0;
}
结果运行图:
当自己给自己进行重复数据拷贝的时,memcpy函数能达到我们的预想值。但是,我们自己模拟的my_memcpy函数就无法达到预想值,难道是我们的模拟函数有问题?答案是否。我们来举个生活中的例子,100分的试卷。考到60分就欧克了,但是你却偏偏考到80多分,说明有能考到60分的能力,但是你还能超越自己。memcpy
函数就是这个道理 ,它有拷贝数据的能力(不重叠数据),但是,它还能超越自己(拷贝重叠数据)。而我们自己模拟的my_memcpy函数完成了它的基本功能。memcpy
函数不太常用于拷贝重叠数据,我们常用memmove
函数拷贝重叠数据。
memmove使用和模拟
- memmove函数的功能:它的功能比
memcpy
函数要广泛些,它既能拷贝不重叠的数据,有能拷贝重叠的数据。 - memmove函数的结构:
// void * memmove ( void * destination, const void * source, size_t num );
memmove函数的结构与memcpy函数的结构相同,但是它的功能比memcpy函数要广泛些。
- memmove函数的使用:进行代码展示---------->数据不重叠。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9 };
int arr4[10] = { 0 };
int sz = sizeof(arr3) / sizeof(arr3[0]);
memmove(arr4, arr3, 4 * sizeof(int));
printf("arr4: ");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr4[i]);
}
return 0;
}
结果运行图:
进行代码展示---------->数据重叠。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9 };
int sz = sizeof(arr3) / sizeof(arr3[0]);
memmove(arr3+2, arr3, 4 * sizeof(int));
printf("arr4: ");
for (int i = 0; i < sz; i++)
{
printf("%d ", arr3[i]);
}
return 0;
}
结果运行图:
- memmove函数的模拟:进行代码展示:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memmove(void* des, void* str, size_t num)
{
void* p = des;
assert(des&&str);
const void* s1 = des;
const void* s2 = str;
if (des < str)
{
while (num--)
{
*(char*)des = *(char*)str;
des = (char*)des + 1;
str = (char*)str + 1;
}
}
else
{
while (num--)
{
des = s1;
str = s2;
str = (char*)str + num;
des = (char*)des + num;
*(char*)des = *(char*)str;
}
}
return p;
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9};
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
my_memmove(arr + 2, arr, 4 * sizeof(int));
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
- memmove函数的模拟讲解:我们对于这个函数的模拟实现要分为两种情况。第一种情况就是:
des<str
时的的拷贝情况。第二种情况就是:des>=str
的拷贝情况。当为第一种情况的时候,要向后拷贝。进行如图所示:
是第二种情况的时候,要向前拷贝,如图所示:
对于向前拷贝的时候,等于的情况是满足的(这是最好的状态),哪怕它越界访问也是满足的(只要str<des
)。
memset函数的使用
- memset函数的功能:具有设置数据的功能。能把整型数组里面的元素全部设为0,也能改变字符串中的某些字符元素。
- memset函数的结构:
// void * memset ( void * ptr, int value, size_t num );
// 因为被改变的数据类型我们不知道(既能改变整型,也能改变字符型……),所以用void*指针接收
// int value为我们想要改变的数据。
// size_t num为我们要改变的内存字节数目。
// 我们还可以指定改变的起始位置。
- memset函数的使用:进行代码展示--------->改变整型数组
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
for (i = 0; i < sz; i++) // 我们以前改变整形数组里面元素的方法
{
arr[i] = 0;
printf("%d ", arr[i]);
}
return 0;
}
结果运行图:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
memset(arr, 0, sz * sizeof(int)); // 使用memset函数直接全部置0
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
结果运行图:
进行代码展示--------->改变整形数组的起始位置 。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
memset(arr+2, 0, 5 * sizeof(int)); // 改变起始位置,从3开始后面置0,
//要注意要改变的元素个数不能超过数组的范围,否则报错
for (i = 0; i < sz; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
结果运行图:
进行代码展示--------->改变字符串。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "hello world";
memset(arr, 'x', 6 * sizeof(char));
printf("%s\n", arr);
return 0;
}
结果运行图:
我们还可以改变字符串要改变的起始位置,大家可以自行尝试一下。
- memset函数使用注意事项:对于改变整形数组时,我们使用
memset
函数主要是置0
,不能置其它的数字,否则得不到想要的结果。也就是说,memset
函数对于改变整形数组里面的元素,只能置0
。进行代码展示:
// 假如,我们给整形数组全部置1
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7 };
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
memset(arr, 1, sz * sizeof(int));
for (i = 0; i < sz; i++)
{
printf("%d \n", arr[i]);
}
return 0;
}
结果运行图:
是不是与我们的预期大相径庭,按理来讲应该全是1,结果全是很大的整数,这是为什么呢? 前面,咱们讲过了这些函数都是对单个字节操作的,由于int 是四个字节,所以每个字节都会改为1。到最后对Int进行解读的时候还是按照4个字节,就会导致解读的数据与期望值不同。结果调试图:如图
从调试图中可以看出来,每个字节都被改为1。所以,我们一般用memset函数置0.
memcmp函数的使用
- memcmp函数的功能:它和
strcmp
函数的功能类似,都是用来比较大小的。strcmp
函数是用来比较字符元素的大小的,而且只能比较字符元素的大小。而memcmp
函数是进行内存单个字节比较,它会每个每个字节进行比较,它不管里面存的什么数据。 - memcmp函数的结构:
// int memcmp ( const void * ptr1, const void * ptr2, size_t num );
// 它和strcmp函数一样,参数放的位置决定比较的前后。
// ptr1大于ptr2返回大于0的值,ptr1等于ptr2返回0的,ptr1小于ptr2返回小于0的值。
// size_t num 为要比较的字节数。
- memcmp函数的使用:进行代码展示--------->整形比较
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,5,6 };
int arr2[] = { 1,2,3,4 };
printf("%d\n", memcmp(arr1, arr2, 3 * sizeof(int)));
printf("%d\n", memcmp(arr1, arr2, 13));
return 0;
}
结果调试图:
这是arr1的内存数据存储的形式,arr2也是一样的。前面,咱们已经讲过了,memcmp函数是每个每个字节进行比较,比如,printf("%d\n", memcmp(arr1, arr2, 13));
这行代码,我们比较了13个字节的空间,arr1和arr2前面12个字节都一样,第13个字节的内容不一样——05大于04。
如图所示的逻辑图:
由于arr1第13个字节的内容大于arr2的内容,所以结果就会输出大于0的值,结果运行图:
-------->字符型比较。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcde";
char arr2[] = "abcf";
printf("%d\n", memcmp(arr1, arr2, 4 * sizeof(char)));
return 0;
}
姐果运行图:
彩蛋时刻!!!
https://www.bilibili.com/video/BV1pT421r7kJ/?spm_id_from=333.1007.tianma.6-3-21.click&vd_source=7d0d6d43e38f977d947fffdf92c1dfad
每章一句:永远相信美好的事情即将发生。
感谢你能看到这里,点赞+关注+收藏+转发是对我最大的鼓励,咱们下期见!!!