C语言——内存函数
时间:2024-04-19 21:40:18 来源:网络cs 作者:言安琪 栏目:数据分析 阅读:
引言
在之前的两篇文章中,我们学习了字符函数和字符串函数,C语言中还有一类库函数叫做内存函数
我们接下来去学习一下这类函数
内存函数
内存函数的功能和某些字符函数的功能相似,它们是通过访问地址的方式操作对象,可应用于任何类型的对象。它们的使用需要包含头文件<string.h>
memcpy
1.memcpy的用法
memcpy用于内存拷贝。它的主要作用是将指定源地址处的内容复制到指定的目标地址处,即将一段内存中的数据拷贝到另一段内存中。
函数原型为:
void * memcpy ( void * destination, const void * source, size_t num );
其中,其参数的含义如下:
destination:指向目标内存区域的指针,即要接收源内存内容的位置
source:指向源内存区域的指针,即要复制的内容所在的起始位置
num:要拷贝的字节数
返回值:
返回一个指向目标内存区域destination的指针
功能:
函数memcpy从source的位置开始向后赋值num个字节的数据到destination指向的内存位置
注意:
memcpy不会对目标内存进行初始化或清除,只是简单地覆盖指定大小的字节
如果源地址和目标地址重叠,则memcpy的行为是未定义的。在这种情况下,应该使用memmove函数
使用memcpy时,必须确保目标内存区域destination有足够的空间来容纳要复制的n个字节,否则会导致缓冲区溢出,引发程序错误或安全漏洞
2.memcpy的使用
#include<stdio.h> #include<string.h>int main(){int arr1[] = { 0,1,2,3,4,5,6,7,8,9 };int arr2[10] = { 0 };memcpy(arr2, arr1, 20);for (int i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;}
输出结果为:
0 1 2 3 4 0 0 0 0 0
3.memcpy的模拟实现
思路:与strncpy类似,只不过memcpy是将指定长度的内存内容到目标地址,并返回目标地址的起始位置。
代码实现如下:
void* my_memcpy(void* dest,const void* src, int n){void* ret = dest;assert(dest && src);//循环复制内存内容,直到复制了 n 个字节while (n--){// 将源地址的当前字节复制到目标地址的当前位置 *(char*)dest = *(char*)src;dest = (char*)dest + 1;src = (char*)src + 1;}return ret;}int main(){int arr1[] = { 0,1,2,3,4,5,6,7,8,9 };int arr2[20] = { 0 };my_memcpy(arr2, arr1, 20);for (int i = 0; i < 10; i++){printf("%d ", arr2[i]);}return 0;}
memmove
1.memmove的用法
memmove用于在内存中移动(复制)字节块。它的主要用途是处理源和目标内存区域重叠的情况,这是 memcpy 函数无法处理的
函数原型为:
void * memmove ( void * destination, const void * source, size_t num );
其中,其参数的含义如下:
destination:指向目标内存区域的指针,即要接收源内存内容的位置
source:指向源内存区域的指针,即要复制的内容所在的起始位置
num:要拷贝的字节数
返回值:
memmove 函数返回指向目标内存区域的指针,即 destination
注意:
使用 memmove 时,即使源和目标内存区域重叠,它也能正确复制数据。这是因为 memmove 可能会采用不同的策略来确保数据的一致性和正确性,比如先临时存储源数据,然后再复制到目标位置
2.memmove的使用
int main(){int arr[] = { 0,1,2,3,4,5,6,7,8,9 };memmove(arr + 2, arr, 20);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;}
输出结果为:
0 1 0 1 2 3 4 7 8 9
解释:
memmove 将数组的前5个整数(0, 1, 2, 3, 4)复制到了从第三个位置开始的内存
int main(){int arr[] = { 0,1,2,3,4,5,6,7,8,9 };memmove(arr, arr + 2, 20);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;}
输出结果为:
2 3 4 5 6 5 6 7 8 9
解释:
由于memmove将数组从第三个元素开始的五个整数(2, 3, 4, 5, 6)复制到了数组的开始位置
3.memmove的模拟实现
我们现在已经大概理解了memmove的用法,现在我们来试着模拟实现memmove
我们先来分析一下:
情况1:
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };memmove(arr + 2, arr, 20);
从2的位置开始拷贝:2->0 3->1 4->2 5->3 6->4 拷贝成功
从6的位置开始拷贝:6->4 5->3 4->2 3->1 2->0 拷贝失败
情况2:
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };memmove(arr, arr + 2, 20);
从0的位置开始拷贝:0->2 1->3 2->4 3->5 4->6 拷贝失败
从4的位置开始拷贝:4->6 3->5 2->4 1->3 0->2 拷贝成功
情况3:
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };memmove(arr, arr + 5, 20);
从5的位置开始拷贝:5->0 6->1 7->2 8->3 9->4 拷贝成功
从9的位置开始拷贝:9->4 8->3 7->2 6->1 5->0 拷贝成功
情况4:
int arr[] = { 0,1,2,3,4,5,6,7,8,9 };memmove(arr + 5, arr, 20);
从0的位置开始拷贝:0->5 1->6 2->7 3->8 4->9 拷贝成功
从4的位置开始拷贝:4->9 3->8 2->7 1->6 0->5 拷贝成功
我们可以根据这些情况得出结论:
如果dest在src左边,就从首元素开始拷贝;如果dest在src右边,就从末尾元素开始拷贝
代码实现如下:
void* my_memmove(void* dest, void* src, int num){ void* ret = dest; assert(dest && src); //判断目标地址是否小于或等于源地址 //如果是,则按照从前往后的顺序复制,防止覆盖还未复制的源数据 if (dest <= src) { //从前往后复制数据 while (num--) //循环num次,每次复制一个字节 { *(char*)dest = *(char*)src; //将src指向的字节复制到dest指向的位置 dest = (char*)dest + 1; // 将dest指针向后移动一个字节 src = (char*)src + 1; // 将src指针向后移动一个字节 } } else { // 如果目标地址大于源地址,则按照从后往前的顺序复制 // 这样可以避免在复制过程中覆盖还未读取的源数据 dest = (char*)dest + num - 1; // 将dest指针移动到目标区域的最后一个要复制的字节 src = (char*)src + num - 1; // 将src指针移动到源区域的最后一个要复制的字节 while (num--) { *(char*)dest = *(char*)src; //将src指向的字节复制到dest指向的位置 dest = (char*)dest - 1; //将dest指针向前移动一个字节 src = (char*)src - 1; //将src指针向前移动一个字节 } } return ret; }int main(){int arr[] = { 0,1,2,3,4,5,6,7,8,9 };my_memmove(arr + 2, arr, 20);for (int i = 0; i < 10; i++){printf("%d ", arr[i]);}return 0;}
memset
1.memset的用法
memset用于将某一块内存中的内容全部设置为指定的值
函数原型为:
void * memset ( void * ptr, int value, size_t num );
其中,其参数的含义如下:
ptr:指向要设置的内存区域的指针
value:要设置的值,该值被转换为 unsigned char 并复制到目标内存区域
num:要设置的字节数
返回值
返回指向被设置内存区域的起始位置的指针
注意:
memset 是按字节操作的,因此当你对非字符类型(如整数、浮点数等)的数组使用 memset 时,需要特别小心。确保你了解目标数组的类型,并知道如何正确地初始化它。对于非零初始化,特别是非字符类型,memset 可能不会按预期工作。
2.memset的使用
int main(){char arr[] = "hello world";memset(arr, 'x', 5); //将前5个字符替换为'x'printf("%s\n", arr); //打印修改后的字符串return 0;}
输出结果为:
xxxxx world
错误演示:
int main(){int arr[5] = { 1,2,3,4,5 };memset(arr, 1, sizeof(arr));int i = 0;for (i = 0; i < 5; i++){printf("%d ", arr[i]);}return 0;}
输出结果为:
16843009 16843009 16843009 16843009 16843009
memset设置是以字节为单位,容易造成不符合我们预期的情况
3.memset的模拟实现
思路:memset的模拟实现和strncpy有点相似,但memset需要将数据类型强制转换为(char*)
代码如下:
void* my_memset(void* str, int c, size_t n){ assert(str); // 将void*类型的指针转换为char*类型的指针,以便按字节访问 char* tmp = (char*)str; // 使用while循环遍历n个字节 while (n--) { // 将当前字节设置为字符c *tmp = (char)c; // 移动到下一个字节 tmp++; } return str;}int main(){ char str[] = "hello world"; my_memset(str, 'x', 6); printf("%s\n", str); return 0;}
memcmp
1.memcmp的用法
memcmp用于比较内存区域的内容
函数原型为:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
其中,其参数的含义如下:
s1 和 s2 是指向要比较的内存区域的指针
n 是要比较的字节数
返回值:
返回值:
如果返回值 < 0,则表示 ptr1 小于 ptr2
如果返回值 > 0,则表示 ptr1 大于 ptr2
如果返回值 = 0,则表示 ptr1 等于 ptr2
注意:
memcmp 函数按字节比较两个内存区域的内容,而不是按数据类型或元素进行比较。因此,它对于比较任意类型的内存区域都是有效的,只要你知道要比较的字节数
2.memcmp的使用
int main(){int arr1[] = { 0,1,2,3,4,5,5,5 };int arr2[] = { 0,1,2,3,4,6,6,6 };int ret = memcmp(arr1, arr2, 21);printf("%d\n", ret);return 0;}
输出结果为:
-1
3.memcmp的模拟实现
思路:memcmp的模拟实现与strncmp差不多,只是也需要先强制类型转换
代码如下:
int my_memcmp(const void* str1, const void* str2, size_t n){assert(str1 && str2);char* p1 = (char*)str1;char* p2 = (char*)str2;while (n-- && (*p1 == *p2)){p1++;p2++;}return *p1 - *p2;}int main(){int arr1[] = { 0,1,2,3,4,3,6,7 };int arr2[] = { 0,1,2,3,4,2,4,4 };int ret = my_memcmp(arr1, arr2, 24);printf("%d\n", ret);char str1[] = "abcddd";char str2[] = "abcdef";ret = my_memcmp(str1, str2, 5);printf("%d\n", ret);return 0;}
输出结果为:
1
-1
结束语
希望看完这篇文章能对友友们有所帮助!!!
求点赞收藏关注!!!
谢谢各位!!!
本文链接:https://www.kjpai.cn/news/2024-04-19/160495.html,文章来源:网络cs,作者:言安琪,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!