• C语言之详解内存操作函数


    个人主页:平行线也会相交
    欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创
    收录于专栏【C/C++
    在这里插入图片描述

    前言

    memcpy叫做内存拷贝,memmove叫做内存移动,memcmp叫做内存比较,大家可以想一想为什么要有这些函数呢?我们不是已经由字符串拷贝、字符串比较、字符串追加这些函数了吗?

    我们首先知道,所谓的字符串函数:strcpy、strcat、strcmp、strncpy、strncat、strncmp这些函数操作的对象是字符串,或多或少都会跟'\0'打交道。
    假设我们现在要操作的是一个数组,比如我们要拷贝整型数组、浮点型数组、又或者是结构体数组,那我们还能不能用strcpy函数来进行拷贝呢?不妨来试一下,请看:
    在这里插入图片描述
    可以清楚的看到这里会弹出警告⚠。报警告只是一方面,那它能不能把数组arr1中的内存拷贝到数组arr2中去呢?请看调试结果:
    在这里插入图片描述
    可以看到并没有把数组arr1的内容拷贝到数组arr2中去,**但是数组arr2中的首元素为什么会变成1呢?**我们先来看数组arr1中的1 2 3 4 5在内存中的布局:

    当前机器是小端存储
    01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
    
    • 1
    • 2

    在这里插入图片描述
    由此我们可以发现这里用strcpy函数压根就做不到,stycpy的局限性就体现出来了。
    由此也出现了内存函数:memcpy、memmove、memcmp、memset

    memcpy

    memcpy叫做内存拷贝,只要是放到内存中的数据我们都可以进行拷贝。

    1.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
    2.这个函数在遇到'\0'的时候并不会停下来。
    3.如果source和destination有任何的重叠,复制的结果都是未定义的。

    在这之前我们还是要回顾以前的回调函数qsort中的void*-通用类型的指针-无类型指针-即可以接受任意类型的指针
    所以,我们来看memcpy函数
    的参数:void* memcpy(void * destination, const void * source ,size_t num);这种参数设计的方式与函数strncpy的参数设计方式类似,可以对照学习一下。注意这里参数num的单位是字节。
    我们来看举例:
    在这里插入图片描述
    再来举一个结构体类型的数组,请看举例:
    在这里插入图片描述

    知道这里我们就大体对函数memcpy的功能有了一定了解,下面我们来模拟实现memcpy函数的功能。

    模拟实现

    #include
    #include
    #include
    struct S
    {
    	char name[20];
    	int age;
    };
    void* my_memcpy(void* dest, const void* src, size_t num)
    {
    	void* ret = dest;
    	assert(dest && src);
    	while (num--)
    	{
    		*(char*)dest = *(char*)src;
    		++(char*)dest;
    		++(char*)src;
    	}
    	return ret;
    }
    int main()
    {
    	int arr1[] = { 1,2,3,4,5 };
    	int arr2[5] = { 0 };
    	struct S arr3[] = { {"张三",20},{"李四",30} };
    	struct S arr4[3] = { 0 };
    	my_memcpy(arr2, arr1, sizeof(arr1));
    	my_memcpy(arr4, arr3, sizeof(arr3));
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    然而我们写的这个模拟memcpy函数只能处理内存不重叠的情况;当内存出现重叠时,我们会有专门的函数memmove来处理。

    memmove

    在这里插入图片描述
    所以,memmove函数可以处理这种重叠拷贝的现象。
    刚刚说过我们写的my_memcpy函数无法处理重叠拷贝的情况,其实库里的memcpy函数是可以实现重叠拷贝的情况的,请看举例:
    在这里插入图片描述
    可以看到库里的memcpy函数的确可以处理重叠拷贝的情况,但是我们刚刚写的my_memcpy函数却无法做到,这并不意味的我们✍的这个my_memcpy函数不好。
    因为C语言标准规定:memcpy函数只需要能处理不重叠的内存拷贝就可以了。
    memmove函数用来处理内存重叠的拷贝。

    所以说,我们写的my_memcpy函数刚刚好满足C语言的标准。并没有一些额外的功能。
    下面我们来模拟实现memmove函数。

    模拟实现

    这里涉及到到底是从前向后拷贝还是从后向前拷贝的问题。
    在这里插入图片描述
    在这里插入图片描述

    #include
    #include
    void* my_memmove(void* dest, const void* src, size_t count)
    {
    	void* ret = dest;
    	assert(dest && src);
    	if (dest < src)
    	{
    	    //前->后
    		while (count--)
    		{
    			*(char*)dest = *(char*)src;
    			++(char*)dest;
    			++(char*)src;
    		}
    	}
    	else
    	{
    		//后->前
    		while (count--)
    		{
    			*((char*)dest + count) = *((char*)src + count);
    		}
    	}
    	return ret;
    
    }
    int main()
    {
    	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memmove(arr + 2, arr, 20);
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    在这里插入图片描述
    这种写法也可以:

    #include
    #include
    void* my_memmove(void* dest, const void* src, size_t count)
    {
    	void* ret = dest;
    	assert(dest && src);
    	if (dest < src||(char*)dest>(char*)src+count)
    	{
    		//前->后
    		while (count--)
    		{
    			*(char*)dest = *(char*)src;
    			++(char*)dest;
    			++(char*)src;
    		}
    	}
    	else
    	{
    		//后->前
    		while (count--)
    		{
    			*((char*)dest + count) = *((char*)src + count);
    		}
    	}
    	return ret;
    
    }
    int main()
    {
    	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memmove(arr + 2, arr, 20);
    	int i = 0;
    	for (i = 0; i < 10; i++)
    	{
    		printf("%d ", arr[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    memcmp

    memcmp:int memcmp(const void * ptr1, const void * ptr2, size_t num );(内存比较)

    比较从ptr1和ptr2指针开始的num个字节

    请看举例:

    在这里插入图片描述
    说明前8个字节一样。倘若我们要比较前9个字节呢?请看:
    在这里插入图片描述
    注意是小端存储。

    memset

    memset—内存设置
    memset:void *memset(void *dest,int c,size_t count);
    这里的c是你要设置的字符是什么。
    这里的count是你要设置多少个字符。

    请看举例:
    在这里插入图片描述
    那我们可不可以这样使用呢?请看:

    #include
    #include
    int main()
    {
    	int arr[10] = {0};
    	memset(arr, 1, 10);
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上述的代码就是打错特错,千万要记住:memset(arr,1,10),这里的10并不是10个元素的意思,而是10个字节。
    如果你非要看一下调试结果,可以🙆‍,请看:
    在这里插入图片描述
    这是怎么一回事呢?数组arr的大小是40个字节,而这里在40个字节中我们对前10个字节进行更改,而且前十个字节我们改成了1。
    即:01 01 01 01 01 01 01 01 01 01 00 00 00 00...
    在这里插入图片描述
    切记:memset函数可以把一块任何什么数据的空间都可以改,但是它改的单位是字节,我们要改几个字节,每个字节的内容又是什么。

  • 相关阅读:
    为什么需要扩展标签属性?(搭建场景体会)
    leetcode 148. 排序链表
    RPC 核心原理理论分析
    界面组件DevExpress Reporting v23.1亮点 - 全新升级报表查看器
    简单学习LVM、LVM的实践、通俗易懂的LVM
    Azure Data Factory(十一)Data Flow 的使用解析
    docker 添加端口及获取dockerfile
    ElementUI的Dialog弹窗实现拖拽移动功能
    宇视网络视频录像机添加摄像机提示离线
    计算机网络 :网络层
  • 原文地址:https://blog.csdn.net/m0_74352571/article/details/127874644