C语言(内存函数)

2024-06-04 4811阅读

        Hi~!这里是奋斗的小羊,很荣幸各位能阅读我的文章,诚请评论指点,欢迎欢迎~~     

                                                💥个人主页:小羊在奋斗

                                                💥所属专栏:C语言   

        本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为一些学友们展示一下我的学习过程及理解。文笔、排版拙劣,望见谅。 

                                1、memcpy 的使用和模拟实现

                                2、memmove 的使用和模拟实现

                                3、memset 函数的使用

                                4、memcmp 函数的使用

1、memcpy 的使用和模拟实现

        1.1  memcpy 函数的使用 

        memcpy 前面的 mem 指的是 memmory ,英文单词“记忆”,在C语言中指的是内存。后面要介绍的 memmove、memset 和 memcmp 都是如此。

        memcpy 是一个内存拷贝函数,其作用是将一个内存区域内指定的 count 个字节大小的内容拷贝到目标内存空间内。值得注意的是,虽然 memcpy 是一个内存函数,但其是定义在 头文件内的。

C语言(内存函数) 第1张

         上面关于 memcpy 函数的作用及其用法的描述还是很好理解的,这里再做一些说明。

        (1)为了使 memcpy 函数可以实现对任意类型的内容拷贝,其参数定义为了 void *类型的指针;

        (2)跟之前学过的字符串相关的函数一样,memcpy 函数拷贝的目标空间必须是可修改的,而被拷贝的内存区域可用 const 修饰;

        (3)当 source 和 destination 有任何重叠的时候,复制的结果都是未定义的,也就是说memcpy 函数不负责重叠内存的拷贝。

C语言(内存函数) 第2张

C语言(内存函数) 第3张

        这个函数还是比较简单的。

       1.2  memcpy 函数的模拟实现

        memcpy 函数和 strcpy 函数有相似的地方,所以其实现的逻辑也就应该和 strcpy 函数的模拟实现类似。首先我们需要搞清楚,为了实现对任意类型的内容拷贝,我们将 memcpy 函数的参数及其返回值都设定成了 void * 类型的指针,但是 void * 类型的指针有缺点,不能直接进行解引用,也不能对其进行 +- 操作,那我们就要想一个办法解决这个问题。

        其实这个问题我们之前在模拟实现 qsort 函数的时候就有了一个解决办法,就是将其强转为char * 类型的指针,因为 char * 类型的指针指向的对象大小是一个字节,是所有类型中字节大小最小的,不管对象类型是多大字节,只要一个字节一个字节地拷贝,就可以对实现对任意类型的内容拷贝了。

        那有了上面的思路,我们就能写出下面的代码:

#include 
#include 
void* my_memcpy(void* dest, const void* sour, size_t count)
{
	assert(dest && sour);
	void* pd = dest;
	while (count--)//控制拷贝多少个字节
	{
		*(char*)dest = *(char*)sour;
		((char*)dest)++;
		((char*)sour)++;
	}
	return pd;//返回目标空间的起始地址
}
void text1()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	int* pi = my_memcpy(arr2, arr1 + 2, 20);
	for (int i = 0; i  

C语言(内存函数) 第4张

        跟之前我们模拟实现 qsort 函数相比,这就是张飞吃豆芽。 

        上面我们是将一个内存区域的内容拷贝到另一个内存区域,那能不能实现在一个内存区域内的拷贝呢?我们来试一下:

C语言(内存函数) 第5张

        可以看到,当我们在一个内存区域内拷贝,并且内存有重叠的时候,my_memcpy 函数就不能完成我们想要的结果了,这是因为重叠的部分已经被拷贝过来的内容代替,原内容就消失了,当拷贝到重叠的内存区域时,拷贝的还是之前拷贝过来的内容。不过只要内存不重叠,在一个内存区域内拷贝也是可行的。

        但是,我们可以看到 memcpy 函数并没有这个问题,那是我们模拟的函数有问题吗?其实不是的,memcpy 函数之所以没有这个问题,是因为在某些系统上,memcpy函数可能会检测是否源内存和目标内存有重叠,并采取一些措施以确保正确的结果。然而,这种行为是不可靠的,不同的编译器或系统的实现方式可能会导致不同的结果。

         上面的情况和我们在 字符、字符串函数 中介绍到的用 strcat 函数实现一个字符串自己拼接到自己末尾产生的问题是类似的,同样的 strcat 函数表面上虽然也没有什么问题,但是这种行为也是不可靠的。为了代码的可移植性和安全性,最好还是使用memmove 函数来处理重叠内存的情况。接下来我们就来介绍 memmove 函数。 

2、memmove 的使用和模拟实现

        2.1 memmove 函数的使用

C语言(内存函数) 第6张

        对比 memcpy 函数,memmove 函数与之是及其相似的,特别的是 memmove 函数操作的对象是可以重叠的,正如它所描述的它会将内容如同先复制到一个临时数组中,这样就解决了目标内存区域的内容被覆盖的问题。 

C语言(内存函数) 第7张

        2.1 memmove 函数的模拟实现 

        那么了解了 memmove 函数的逻辑,模拟实现它也不是什么难事。我们只需要创建一个临时数组过渡就行,于是就得到了下面的代码:

#include 
#include 
void* my_memmove(void* dest, const void* sour, size_t count)
{
	assert(dest && sour);
	void* pd = dest;
	int i = 0;
	char arr[1000] = { 0 };
	for (i = 0; i  

C语言(内存函数) 第8张

        是不是很简单呢,这样我们就实现了模拟 memmove 函数的功能。但是上面这种创建临时字符数组的办法有一点不足,因为我们并不能确定被拷贝的内容有多大,所以只能模糊地创建一个比较大的数组,但是这个比较大是多大没办法知道,创建大了浪费,创建小了不够,那有没有什么办法能解决这个问题呢? 

        2.3 memmove 函数的模拟优化

        既然我们并不能确定要创建一个多大的临时数组,那我们干脆放弃创建临时数组的方法另辟奇径。

        让我们再回到之前遇到的问题,如果内存重叠时拷贝会将原内容覆盖。那是不是我们拷贝的方法有问题呢?来看:

        将红色方框内的内容拷贝到蓝色方框内:

C语言(内存函数) 第9张

 C语言(内存函数) 第10张

        我们发现,从前向后拷贝行不通,因为会覆盖掉还没拷贝的内容;但从后向前拷贝是可行的,并没有出现还没拷贝的内容被覆盖的情况。

        将蓝色方框内的内容拷贝到红色方框内:

C语言(内存函数) 第11张

C语言(内存函数) 第12张

        我们又发现,从后向前行不通,但从前向后是可行的。

        而之所以有时需要从前向后拷贝,有时需要从后向前拷贝,是取决于是将前面的内容拷贝到后面,还是将后面的内容拷贝到前面。

C语言(内存函数) 第13张

        前面介绍数组的时候我们说过,数组元素随着下标的增大地址逐渐增大。也就是说,如果上面需要将红色方框内的内容拷贝到蓝色方框内,那么当指针p1小于指针p2时,需要从后向前拷贝;当指针p1大于指针p2时,需要从前向后拷贝。而当两个内存区域没有重叠时,从前向后和从后向前都是可行的。

        那么,我们就可以在拷贝之前先比较一下指针dest和指针sour的大小,然后再选择是从前向后拷贝还是从后向前拷贝。

#include 
#include 
void* my_memmove(void* dest, const void* sour, size_t count)
{
	void* pd = dest;
	assert(dest && sour);
	if (dest  

C语言(内存函数) 第14张

C语言(内存函数) 第15张

C语言(内存函数) 第16张

C语言(内存函数) 第17张

        这时候我们写的 my_memmove 函数就比较完善了。

        其实小伙伴们也能感觉到 memmove 函数完全可以代替 memcpy 函数,而且 memmove 函数不用管内存是否重叠的问题。那 memcpy 函数不就没有存在的必要了吗?其实内存重叠只是一种特殊情况,在确定没有内存重叠的情况下使用 memcpy 函数效率会更高,因为 memcpy 函数没有比较指针大小这一步骤。

        当然如果你嫌麻烦始终使用 memmove 函数也是没有什么问题的,就是效率低那么一丢丢而已。

3、memset 函数的使用

C语言(内存函数) 第18张

C语言(内存函数) 第19张

        memset 函数是用来设置内存的,它的作用是将内存中的值以字节为单位设置成想要的内容。 

C语言(内存函数) 第20张

        需要注意的是,memset 函数是以字节为单位设置的,如果对这个规则模糊的话很容易写出下面这种代码:

C语言(内存函数) 第21张

        我们知道整型占4个字节,整数7以16进制表示为:0x07 00 00 00,上面的代码执行过后就变成了:0x01 01 01 01,并没有达到我们想要的效果。所以我们要谨记 memset 函数是以字节为单位一个字节一个字节设置的,并不是以元素为单位的。

4、memcmp 函数的使用

C语言(内存函数) 第22张

        memcmp 函数和 strncmp 函数极其相似,也是比较两个指针指向内容的大小,唯一的区别是 strncmp 只能比较字符串,而 memcmp 可以比较任意类型。和 memset 函数一样 memcmp 也是以字节为单位比较的。 

C语言(内存函数) 第23张

C语言(内存函数) 第24张

C语言(内存函数) 第25张

C语言(内存函数) 第26张  

        以上所有的函数都是可以操作内存的函数,与前面介绍的字符、字符串函数不同的是内存函数可以操作任意类型的内容。 

          如果觉得我的文章还不错,请点赞、收藏 + 关注支持一下,我会持续更新更好的文章。  

                                                点击跳转下一节 —> C语言(数据存储) 


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]