C语言笔记|C语言进阶笔记(六) | 详解内存函数及其模拟

目录
memcpy
函数介绍
代码演示
模拟memcpy
memmove
函数介绍
代码演示
模拟memmove
memcmp
函数介绍
代码演示
memset
函数介绍
代码演示
代码效果
memcpy 函数介绍 void * memcpy ( void * destination, const void * source, size_t num );

将num个字节的值从源指向的位置直接复制到目标指向的内存块。
源指针和目标指针指向的对象的基本类型与此函数无关;
这个函数在遇到 '\0' 的时候并不会停下来,总是精确复制num字节。
为避免溢出,目标和源参数指向的数组大小应至少为num字节,且不应重叠;
(对于重叠的内存块,memmove是一种更安全的方法)。
结果是数据的二进制副本。
功能 -内存拷贝
代码演示
#define _CRT_SECURE_NO_WARNINGS#include #include //调用memcpy需要包含的头文件 int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; //假设小端存储: //01 00 00 00 02 00 00 00... int arr2[20] = { 0 }; //strcpy - 只适用于字符串,且遇0便停止,此处不适用 memcpy(arr2, arr1, 20); //拷贝20个字节,即5个整型元素 for (int i = 0; i < 20; i++) { printf("%d ", arr2[i]); //1 2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 } return 0; }

模拟memcpy
#include #includevoid* my_memcpy(void* dest, const void* src, size_t num) { assert(dest && src); //写法一 /*for (int i = 0; i < num; i++) { *((char*)dest+i) = *((char*)src+i); } return dest; */ //写法二 void* ret=dest; while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = https://www.it610.com/article/(char*)src + 1; } return ret; }int main() { int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 }; int arr2[20] = { 0 }; my_memcpy(arr2, arr1, 20); for (int i = 0; i < 20; i++) { printf("%d ", arr2[i]); } return 0; }

my_memcpy应该只用于拷贝内存不重叠的部分。
#include #includevoid* my_memcpy(void* dest, const void* src, size_t num) { assert(dest && src); void* ret = dest; while (num--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = https://www.it610.com/article/(char*)src + 1; } return ret; }int main() { int arr1[10] = { 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]); //1 2 1 2 1 2 1 8 9 10 //不能拷贝出理想中1 2 1 2 3 4 5 8 9 10的效果 } return 0; }

但是我们会发现库函数memcpy本身却能够拷贝出1 2 1 2 3 4 5 8 9 10的效果。
#include #include int main() { int arr1[10] = { 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]); //1 2 1 2 3 4 5 8 9 10 } return 0; }

其实memcpy本身只要实现了不重叠拷贝就可以了,但是在VS中的实现,既可以拷贝不重叠内存块,也可以拷贝重叠内存块,算是超额完成了任务,虽然它在VS中可以拷贝重叠内存块,但是我们仍不建议用memcpy拷贝重叠内存块,对于重叠的内存块,memmove是一种更安全的方法。
memmove 函数介绍 void* memmove(void* destination, const void* source, size_t num);
将num bytes的值从源指向的位置复制到目标指向的内存块。
复制就像使用了中间缓冲区一样进行,允许目标和源重叠。
源指针和目标指针指向的对象的基本类型与此函数无关;
这个函数在遇到 '\0' 的时候并不会停下来,总是精确复制num字节。
为避免溢出,目标和源参数所指向的数组的大小应至少为num字节。
结果是数据的二进制副本。
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的,如果源空间和目标空间出现重叠,就使用memmove函数处理。
代码演示
#include #include int main() { int arr1[10] = { 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]); //1 2 1 2 3 4 5 8 9 10 } return 0; }

模拟memmove 拷贝源数据,可以从前向后拷贝,也可以从后向前拷贝,有重叠部分时,不同方式拷贝,拷贝效果不同。
C语言笔记|C语言进阶笔记(六) | 详解内存函数及其模拟
文章图片

当有重叠部分,并且dest>src时,我们可以选择从后向前拷贝来避免后面要拷贝的内容被前面拷贝的内容覆盖;同理,当有重叠部分,并且dest
#include #include void* my_memmove(void* dest, const void* src, size_t num) { assert(dest && src); if (dest > src) { while (num--) { *((char*)dest + num) = *((char*)src + num); } } else { for (int i = 0; i < num; i++) { *((char*)dest + i) = *((char*)src + i); } } return dest; }int main() { int arr1[10] = { 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]); //1 2 1 2 3 4 5 8 9 10 } return 0; }

memcmp 函数介绍
将ptr1指向的内存块的前num个字节与ptr2指向的内存块的前num个字节进行比较,如果它们都匹配,则返回零;如果不匹配,则返回一个不同于零的值,表示哪个值更大。
请注意,与strcmp不同,函数在找到空字符后不会停止比较;
比较的是首个不同字节数值的大小,而不是长短。
ptr1小于ptr2,返回值<0;
ptr1等于ptr2,返回值0;
ptr1大于ptr2,返回值>0。
代码演示
#include #includeint main() { float arr1[] = { 1.0,2.0,3.0,4.0,5.0 }; float arr2[] = { 1.0,1.0 }; int ret = memcmp(arr1, arr2, 4); //比较arr1和arr2的前4个字节 printf("%d\n", ret); //0 ret = memcmp(arr1, arr2, 8); printf("%d\n", ret); //-1 return 0; }

这里细心的朋友会发现,第二次用memcpy比较时,竟然出现了1.0>2.0的情况,不必惊慌,结果是没有问题的。
首先我们要明确,memcpy函数比较的是首个不同字节数值的大小,而不是长短,然后就是要了解浮点数在内存中的储存规则。
若对于浮点数储存规则不了解,可见拙作进行了解C语言进阶笔记(一) | 深度剖析数据在内存中的存储_KYG__Y_O的博客-CSDN博客目录数据类型的基本归类类型的意义整型家族浮点数家族构造类型指针类型空类型整形在内存中的存储原码、反码和补码大小端介绍练习题练习1练习2练习3练习4浮点型在内存中的存储浮点数表示形式IEEE 754规定有效数字M的保存指数E的保存指数E从内存中取出练习题数据类型的基本归类类型的意义1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。2. 如何看待内存空间的视角。整型家族cha...https://blog.csdn.net/KYG__Y_O/article/details/119896867下面我们来比较1.0和2.0
1.0 转化为二进制是 1.0
1.0 可以写成 (-1)^1.0*2^0
储存在内存中为
SE+127M
0 01111111 00000000000000000000000
也就是
0011 11111000 00000000 00000000 0000
字节4字节3字节2字节1
2.0 转化为二进制是 10.0
10.0 可以写成 (-1)^0*1.0*2^1
储存在内存中为
SE+127M
0 10000000 00000000000000000000000
也就是
0100 00000000 00000000 00000000 0000
字节4字节3字节2字节1
这样很明显可以看出二者字节1、字节2相等,进而比较字节3,1.0中的字节3大于2.0中的字节3,所以此处比较结果是1.0>2.0。
memset 函数介绍 void* memset(void* ptr, int value, size_t num);
将ptr指向的内存块的前num个字节设置为指定值value
代码演示
#includeint main() { int arr[10] = { 0 }; % memset(arr, 1, 20); //注意是以字节为单位设置内存 易错 return 0; }

代码效果 C语言笔记|C语言进阶笔记(六) | 详解内存函数及其模拟
文章图片

【C语言笔记|C语言进阶笔记(六) | 详解内存函数及其模拟】

    推荐阅读