内存操作函数
1.memcpy函数
void* memcpy(void* destination, const void* source, size_t num);
- 函数memcpy 从source的位置 开始向后复制 num个字节 的数据到destination指向的内存位置
- 这个函数在遇到\0的时候也不会停下来
- 如果source和destination有任何的重叠 ,复制的结果都是未定义的、
我们来看一个memcpy使用的例子
// memcpy 函数的使用 -- 针对内存卡进行拷贝 任何类型都可以进行拷贝
int main()
{
// 之前我们学习过字符串的拷贝 strcpy
// strcpy能实现的功能 memcpy也是能实现的
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
memcpy(arr2, arr1, 20);// 这里的20指的是20字节
for (int i = 0; i < 20; i++)
{
printf("%d", arr2[i]);// 将arr2打印出来 观察是否粘贴了5个元素
}
return 0;
}
memcpy函数的模拟实现:
// memcpy 函数的模拟实现
# include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)// 该函数在结束之后会返回目标空间的起始地址
// 注意了这个size_t num 形参的单位是字节
{
void* ret = dest;
assert(src && dest);
for (int i = 0; i < num; i++)// while循环也是可以的
{
// 由于我们不知道传进来的地址解引用后是什么类型的数据 因此我们就一个一个字节的传
*(char*)dest = *(char*)src; // 我们将传进来的地址强行强制转换
// 为什么是转化成char* 呢 因为万一传进来的类型是奇数大小时 我们仍然可以传输
src = (char*)src + 1;// char*类型的指针刚好是一个字节 符合我们一个一个字节的传输
// ((char*)src)++; // 如果要搞成++ 要这样写
dest = (char*)dest + 1;
// ((char*)dest)++;
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[20] = { 0 };
my_memcpy(arr2, arr1, 20);// 20个字节
for (int i = 0; i < 20; i++) // 12345000000000000000
{
printf("%d", arr2[i]);// 将arr2打印出来 观察是否粘贴了5个元素
}
// memcpy也是可以实现自己给自己复制的
//int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
//my_memcpy(arr1 + 2, arr1, 20);// 20个字节
//for (int i = 0; i < 10; i++)
//{
// printf("%d ", arr1[i]);// 1 2 1 2 1 2 1 8 9 10 这个结果明显和我们想要的结果是不一样的
// // memcpy库函数实是能完成我们想要的结果的 但是实际上这个函数是不负责这个内存重叠的拷贝的
// // 负责内存重叠的拷贝是memmove函数负责的
//}
//return 0;
}
2.memmove函数
memmove和memcpy的差别就是源内存块和 目标内存块是可以重叠的
void* memmove(void* destination, void* source, size_t num);
我们来看memmove的使用
// memmove 函数的使用
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr + 2, arr, 5 * sizeof(int));// 这里的5 * sizeof(int)就是20
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);// 1 2 1 2 3 4 5 8 9 10
}
return 0;
}
memmove 函数的复现:
想要复现memmove函数 我们就要实现内存空间即便重叠了 也能完成复制功能
那么我们就要思考几种情况
第一种情况:
我们的dest 被指向要被复制的地方 在src 的左边 内存空间有重叠 那么这个时候我们考虑从前向后的复制
也就是3–>1 4–>2 5–3 6–4 7–>5
第二种情况:
dest在src的右边但内存空间还是有重叠
这种情况就要从后向前 7复制到9的位置 6复制到8的位置 …
第三种情况 :
dest在src的左边还是右边 但是内存空间没有重叠 那么这个时候从后向前还是从前向后都没也区别
因此我们的代码就要考虑上述几种情况
因此我们现在开始复现memmove函数:
// memmove 函数的复现
# include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src) // 从前向后
{
while (num--)// 用for循环也是可以的 参考上面memcpy复现的代码
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else// 从后向前
{
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr, arr + 2, 4 * sizeof(int));// 这里的4 * sizeof(int)就是16
for (int i = 0; i < 10; i++)
{
printf("%d ", arr[i]);// 1 2 1 2 3 4 5 8 9 10
}
// 也可以用返回值去打印
return 0;
}
3.memset函数的使用
void* memset(void* ptr, int value, size_t num)
ptr 指向的是要被储存的空间
value 把ptr所指向的空间内的数据改成value值
num 是你要让多少字节的数据 被设置成value值
memset函数使用的例子:
// memset函数的使用
int main()
{
// 修改字符类型的数据
char arr1[] = "hello world";
memset(arr1 + 1, 'y', 5);// 5指的是5个字节
printf("%s\n", arr1);// hyyyyyworld
// 修改整形类型的数据
int arr2[5] = { 0 };
memset(arr2, 1, 20);// 20指的是20个字节
// 注意了memset函数在修改数据的时候是以字节为单位的 做不到以元素为单位 因此 这里让arr2里的五个元素都变成1是做不到的
for (int i = 0; i < 5; i++)
{
printf("%d ",arr2);
}
return 0;
}
这里的内存是arr2数组的内存空间
4.memcmp函数的使用
int memcmp(const void* ptr1, const coid* ptr2, size_t num);
memcmp完成的是内存块的比较
比较ptr1 和 ptr2 指针指向的位置开始 向后的num个字节
如果ptr1 > ptr2 返回大于0的值
ptr1 = ptr2 返回0
ptr1 < ptr2 返回小于0的值
我们来看memcmp函数的使用:
// memcmp 函数的使用
int main()
{
int arr1[] = { 1,2,3,4,5,6,7 };
int arr2[] = { 1,2,3,4,7,8,8 };
int ret = memcmp(arr1, arr2, 17);// 这里的17是17个字节
// 如果在17个字节之前比较完毕 的话 那就不会比较到17个字节
printf("%d\n", ret);
return 0;
}
// memcmp 函数的使用
int main()
{
int arr1[] = { 1,2,3,4,5,6,7 };
int arr2[] = { 1,2,3,4,7,8,8 };
int ret = memcmp(arr1, arr2, 17);// 这里的17是17个字节
// 如果在17个字节之前比较完毕 的话 那就不会比较到17个字节
printf("%d\n", ret);
return 0;
}