前言:
本博客向大家介绍四个内存函数:memcpy(内存拷贝)、memmove(内存移动)、memcmp(内存比较)、memset(内存设置)
memcpy函数的介绍及其应用:
-
num表示要拷贝几个字节
-
遇到'\0'他并不会停下来
-
如果destination和source有任何的重叠,复制的结果都是未定义的
-
不管是什么数据类型,它都能来;strcpy只能针对字符串
-
VS2019编译器中memcpy可以实现重叠拷贝(尝试一下就可以发现),不能保证所有的memcpy都能做到VS2019里的一样可以实现重叠拷贝
-
C语言规定:
memcpy只需要不重叠的拷贝就ok
memmove是需要实现重叠内存的拷贝 -
memcpy的头文件是:#include <string.h>
memcpy的应用:
#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);//拷贝5个整型进去,因为num的单位是字节,所以此时num的值是5*4=20
float str1[] = { 1.0f,2.0f,3.0f,4.0f };//如果不写f编译器会把他当成double类型的数据
float str2[5] = { 0.0 };
memcpy(str2, str1, 8);
return 0;
}
效果:
遇到'\0',并不会停止拷贝:
memcpy的模拟实现:
memcpy函数的设计:void* memcpy(void* destination, const void* source, size_t num);
思路:
格式:既然是模拟,格式应该尽量一模一样。memcpy函数可以接收任何类型的数据,所以模拟的时候要设计成void* 类型。另外,memcpy函数在C语言中是不处理重叠的数据的,因此本次模拟实现也无法处理模拟重叠的数据,即只能处理src与dest无重叠部分的情况。
拷贝:因为memcpy函数是以字节为单位进行拷贝的,所以我们在对数据进行处理的时候要先将其强制类型转换成char*类型,以便以字节为单位进行操作
#include <stdio.h>
#include <assert.h>
//因为memcpy是什么数据类型都能使用的,所以两个参数不应该弄成int或者是char或者是其他的
//应该弄成void
//只有void才可以接收一切的数据类型
void* my_memcpy(void* dest, void* src, size_t num)
{
assert(src);
void* ret = dest;
while (num--)//一次拷贝一个字节,要拷贝num个字节,所以是num--
{
*(char*)dest = *(char*)src;
//先把dest和src都强制类型转换成char类型的指针
//一方面是因为void类型的指针不能直接拿出来使用
//一方面是为了一次只拿一个字节的内容
//error的写法:
//(char*)dest++;
//(char*)src++;
//先使用强制类型转换
//再加加
//但是此时强制类型转换已经没有用了
//相当于白费了
//error的写法:
//((char*)dest)++;
//((char*)src)++;
//将强制类型转换及其的使用对象括起来
//使得其对强制类型转换以后的结果进行加加
//这种写法在有的编译器里能行
//但是不是所有
dest = (char*)dest + 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(arr2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
效果:
memmove函数的介绍及其应用:
- memmove和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠。如果源空间和目标空间出现重叠,就得使用memmove函数处理
- memmove的头文件:#include <string.h>
memmove的应用:
include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memmove(arr2, arr1, 20);//拷贝5个整型进去,因为num的单位是字节,所以此时num的值是5*4=20
float str1[] = { 1.0f,2.0f,3.0f,4.0f };//如果不写f编译器会把他当成double类型的数据
float str2[5] = { 0.0 };
memmove(str2, str1, 8);
return 0;
}
效果:
memmove的模拟实现:
memmove函数的设计:void* memmove( void * destination, const void * source,size_t num);
思路:memmove函数比memcpy函数多处理重叠拷贝的情况(dest和src有重叠的部分)。
画图把所有的情况分析清楚,然后结合上面memcpy函数的模拟实现来改良优化即可。
#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, void* src, size_t num)
{
assert(src);
void* ret = dest;
if (dest < src)
{
while (num--)//从前向后的
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else//后到前
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memmove(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
效果:
memcmp函数的介绍及其应用:
memcmp函数的设计:int memcmp(const void* ptr1, const void* ptr2, size_t num);
- 比较的是ptr1和ptr2指针开始的num个字节(注意num的单位是字节哦)
- ptr1<ptr2--->返回一个小于零的数;ptr1>ptr2--->返回一个大于零的数;ptr1==ptr2--->返回0
- void*又暗示着参数可以是任意类型
- strncmp只能比较字符串的大小;而memcmp可以比较任意类型
- memcmp的头文件是:#include <string.h>
memcmp的应用:
#include <stdio.h>
#include <string.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
int str[] = { 1,2,3,7 };
int ret = memcmp(arr, str, 16);
printf("%d\n", ret);
return 0;
}
效果:
memset函数的介绍及其应用:
memset函数的设计:void* memset(void *ptr, int value,size_t num);
- 内存设置:设置ptr(要设置的内存块)处的内存,将其设成value值,num个字节
- memset的头文件是:#include <string.h>
memset的应用:
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5 };
memset(arr, 0, 12);
//也可以通过调试来看
//调试可以更加直观地观察到改变的是字节
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
效果: