【C语言】——内存函数的使用及模拟实现

【C语言】——内存函数的使用及模拟实现

    • 前言
    • 一、【C语言】——内存函数的使用及模拟实现 函数
      • 1.1、函数功能
        • (1)函数名理解
        • (2)函数介绍
      • 1.2、函数的使用
      • 1.3、函数的模拟实现
    • 二、【C语言】——内存函数的使用及模拟实现 函数
      • 2.1、函数功能
      • 2.2、函数的使用
      • 2.3、函数的模拟实现
        • (1)思路一
        • (2)思路二
        • (3)代码实现
    • 三、【C语言】——内存函数的使用及模拟实现 函数
      • 3.1、函数功能
      • 3.2、函数的使用
      • 3.3、函数的模拟实现
    • 四、【C语言】——内存函数的使用及模拟实现 函数
      • 4.1、函数功能
      • 4.2、函数的使用
      • 4.3、函数的模拟实现

前言

  在【C语言】——字符串函数的使用与模拟实现(上)与【C语言】——字符串函数的使用与模拟实现(下)二文中,我曾详细介绍了字符串相关函数的具体功能及其模拟实现。
  
  然而,实践过程中,我们往往不仅仅只是处理字符串类型的变量,若要对浮点型、整型、结构体等其他类型的数据进行相关操作,又该怎么办呢?
  
  这时,就需要用到的内存操作函数
  
  接下来,让我们一起来学习学习吧

  
  

一、【C语言】——内存函数的使用及模拟实现 函数

1.1、函数功能

(1)函数名理解

  

在介绍 【C语言】——内存函数的使用及模拟实现函数 的功能前,我们先来对函数名进行分析:

  • 【C语言】——内存函数的使用及模拟实现 表示的是 【C语言】——内存函数的使用及模拟实现,它有记忆内存的意思,这里它表示的是内存
  • 【C语言】——内存函数的使用及模拟实现【C语言】——内存函数的使用及模拟实现,表示拷贝

  这样我们从名字就能大体知道这个函数的大致功能啦,像之前的 【C语言】——内存函数的使用及模拟实现函数,它是实现对字符串的拷贝,而 【C语言】——内存函数的使用及模拟实现函数,实现的是对内存块的拷贝
  
  

(2)函数介绍

功能:将源字符串的【C语言】——内存函数的使用及模拟实现内存块内容拷贝到目标字符串中

  • 函数 【C语言】——内存函数的使用及模拟实现【C语言】——内存函数的使用及模拟实现 的位置开始向后复制 【C语言】——内存函数的使用及模拟实现 个字节的数据到 【C语言】——内存函数的使用及模拟实现 指向的内存为止
  • 函数的使用需包含头文件 【C语言】——内存函数的使用及模拟实现
  • 因为拷贝的数据不一定是字符串,函数在遇到 ‘\0’ 时不会停下来
  • 如果 【C语言】——内存函数的使用及模拟实现【C语言】——内存函数的使用及模拟实现 有任何的重叠,复制的结果都是未定义的
  • 【C语言】——内存函数的使用及模拟实现 所指向的空间必须足够大,至少 【C语言】——内存函数的使用及模拟实现 个字节大小,以避免溢出
  • 返回值是目标空间的地址

注:对于重叠的内存,交给 【C语言】——内存函数的使用及模拟实现函数 来处理。

  

1.2、函数的使用

  
例如:我们将整型数组 【C语言】——内存函数的使用及模拟实现 的前 5 个元素复制给 【C语言】——内存函数的使用及模拟实现 数组
  
代码如下:

#include<stdio.h>
#include<string.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
	return 0;
}

  
运行结果:

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr1 + 2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	printf("\n");
	return 0;
}

  
运行结果:

1.3、函数的模拟实现

  

  【C语言】——内存函数的使用及模拟实现函数 的实现思路和 【C语言】——内存函数的使用及模拟实现 的实现差不多,即逐一拷贝每个内存块(详情请看:【C语言】——字符串函数的使用与模拟实现(上))
  

void* my_memcpy(void* dest, void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);

	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}

  
代码逐行解析:

  • 参数用 【C语言】——内存函数的使用及模拟实现* 的原因:【C语言】——内存函数的使用及模拟实现 拷贝的是任意类型的数据,而函数事先并不知道你所用传递的数据的类型,因此用 【C语言】——内存函数的使用及模拟实现 * 指针,以便能接收所有类型的数据
  • assert(dest && src);断言,后面要对指针进行解引用,因此不能传入空指针。
  • while (num--)循环条件的判断,【C语言】——内存函数的使用及模拟实现 不为 0,即为真,进入循环,后 【C语言】——内存函数的使用及模拟实现 自减
  • *(char*)dest = *(char*)src;【C语言】—— 指针一 : 初识指针(上)一文中,曾提到不能对 【C语言】——内存函数的使用及模拟实现 * 指针进行解引用操作,因此要改变其所指向的值,要对其进行强制类型转换。因为 【C语言】——内存函数的使用及模拟实现 * 类型一次的访问权限只有一个字节,因此将其强转成 【C语言】——内存函数的使用及模拟实现* 类型,再进行解引用、赋值
  • dest = (char*)dest + 1;指针向后移,指向下一个字节
      

  这里有小伙伴可能会问:dest = (char*)dest + 1;我觉得有点啰嗦,能不能写成(char*)dest++;呢?
  答案是:不能
  为什么呢?
  因为强制类型转换是临时的。这里的 ++,不是对 【C语言】——内存函数的使用及模拟实现* 类型的 【C语言】——内存函数的使用及模拟实现++,而是对 【C语言】——内存函数的使用及模拟实现* 类型【C语言】——内存函数的使用及模拟实现++
  上述代码可拆分成两句:(char*)dest;dest++;
  
  那((char*)dest)++;可行吗?
  基本是没问题的,但需要注意的是,有部分编译器可能编不过去。
  
  因此,循环语句的内容也可写成:

	while (num--)
	{
		*((char*)dest)++ = *((char*)src)++;
	}

当然,最好还是上面的那种写法。

  
  

二、【C语言】——内存函数的使用及模拟实现 函数

2.1、函数功能

  
  在前面将 【C语言】——内存函数的使用及模拟实现函数 时,我曾提到,对于重叠的内存,交给 【C语言】——内存函数的使用及模拟实现函数 来处理,那么 【C语言】——内存函数的使用及模拟实现函数 又是做什么的呢?
  

功能:将源字符串的【C语言】——内存函数的使用及模拟实现内存块内容拷贝到目标字符串中

  • 【C语言】——内存函数的使用及模拟实现函数 的差别就是 【C语言】——内存函数的使用及模拟实现函数 处理的源内存块和目标内存块可以重叠的。
  • 如果源空间和目标空间出现重叠,就得使用 【C语言】——内存函数的使用及模拟实现函数 处理。

  

2.2、函数的使用

#include<stdio.h>
#include<string.h>

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr + 2, arr, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

  
运行结果:

  
图示:

  
  

2.3、函数的模拟实现

(1)思路一

  
  要实现重叠空间的拷贝,有什么办法呢?

  我们想到,可以另外开辟一个数组,将原数组所有数据进行拷贝。然后,再对需要拷贝的元素进行拷贝。

  但是,这个方法太过繁琐,而且要单独创建数组,浪费空间,这里我们暂不考虑。

  

(2)思路二

  那还有什么办法呢?我们不妨想一想,为什么内存空间重叠不能用memcpy函数?因为新拷贝的内容会将原来真正想拷贝的内容覆盖,这样最终拷贝出来的结果就不是自己想要的
  
图示:

  那有什么办法可以让先拷贝的内容不覆盖后拷贝的内容呢?

  我们可以从后往前拷贝,如图:
  

  
  这时,有小伙伴就说了:只要以后我们都从后往前拷就行了
  但问题又来了,如果是从 【C语言】——内存函数的使用及模拟实现 + 2 开始拷贝 5 个元素到 【C语言】——内存函数的使用及模拟实现 呢?
  
如图:

  
  发现又出问题了,但问题和之前一样,先拷贝的内容覆盖了原数据,导致后拷贝的内容错误
  

  这时就需要用到从前往后拷贝
  

  
  那什么时候从后往前拷,什么时候从前往后拷呢?
  
结论:

  • 当两块空间重叠时,目标空间在源空间前面从前向后拷贝
  • 当两块空间重叠时,目标空间在源空间后面从后向前拷贝
  • 当两块空间不重叠时,两种拷贝方法都行
      

图示:

  
  

(3)代码实现

  
  这里,空间不重叠的部分我们用从后向前,因为这样只用在【C语言】——内存函数的使用及模拟实现 处分界即可

void* my_memmove(void* dest, void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);

	if (src > dest)
	{
		while (num--)
		{
			//前->后
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		//后->前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return 0;
}

  
  这段代码从前往后的情况与 【C语言】——内存函数的使用及模拟实现 情况相同,就不再过多解释,来看从后往前的情况

  • while (num--)这里循环判断条件与上面一样,当 【C语言】——内存函数的使用及模拟实现 大于 0,进入循环,随后进行自减。
  • *((char*)dest + num) = *((char*)src + num);这里我们发现,如果要拷贝 20 字节内容,即 【C语言】——内存函数的使用及模拟实现 等于 20,那么起始位置加 【C语言】——内存函数的使用及模拟实现 – 1 所指向的就是最后一个要拷贝的字节,而 【C语言】——内存函数的使用及模拟实现 在进入循环后已经自减,因此这里直接加 【C语言】——内存函数的使用及模拟实现 。再接着,再次循环,【C语言】——内存函数的使用及模拟实现 再次自减,拷贝倒数第二个字节内容,以此类推,直至 【C语言】——内存函数的使用及模拟实现 为 0 拷贝结束。
      
      

三、【C语言】——内存函数的使用及模拟实现 函数

3.1、函数功能

功能:设置内存,将内存中的值以字节为单位设置成想要的内容

  • 【C语言】——内存函数的使用及模拟实现:指向要设置的内存块
  • 【C语言】——内存函数的使用及模拟实现:要设置的值
  • 【C语言】——内存函数的使用及模拟实现:要设置字节的个数
  • 返回值:要设置的内存,即 【C语言】——内存函数的使用及模拟实现
      

3.2、函数的使用

#include<stdio.h>
#include<string.h>

int main()
{
	char str[] = "hello world";
	memset(str, 'x', 6);
	printf(str);
	printf("\n);
	return 0;
}

  
运行结果:

  
  这时,有小伙伴问能不能用它来设置整型呢?当然可以

#include<stdio.h>
#include<string.h>

int main()
{
	int arr[10];
	memset(arr, 0, 10*sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

  
运行结果:

#include<stdio.h>
#include<string.h>

int main()
{
	int arr[10];
	memset(arr, 1, 10*sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	return 0;
}

  
运行结果:

  

3.3、函数的模拟实现

void* memset(void* ptr, int value, size_t num)
{
	assert(ptr);

	while (num--)
	{
		*((char*)ptr + num) = value;
	}
	
	return ptr;
}

  
  

四、【C语言】——内存函数的使用及模拟实现 函数

4.1、函数功能

功能:比较从 【C语言】——内存函数的使用及模拟实现【C语言】——内存函数的使用及模拟实现 指针指向的位置开始,向后的 【C语言】——内存函数的使用及模拟实现 个字节
  
返回值如下:

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str1 大于 str2。
  • 如果返回值 = 0,则表示 str1 等于 str2。

  

4.2、函数的使用

int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";
	int n = 0;
	n = memcmp(buffer1, buffer2, sizeof(buffer1));

	if (n > 0)
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);

	else if(n < 0)
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);

	else
		printf("'%s' is the same '%s'.\n", buffer1, buffer2);

	return 0;

}

  
运行结果:

4.3、函数的模拟实现

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1 && ptr2);

	while (*(char*)ptr1 == *(char*)ptr2 && num--)
	{
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}

	return *(char*)ptr1 - *(char*)ptr2;
}

版权声明:本文为博主作者:9毫米的幻想原创文章,版权归属原作者,如果侵权,请联系我们删除!

原文链接:https://blog.csdn.net/yusjushd/article/details/138000112

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
青葱年少的头像青葱年少普通用户
上一篇 2024年5月6日
下一篇 2024年5月6日

相关推荐