C语言内存函数详细讲解及各自模拟实现(含memcpy,memmove,memset,memcmp)

在这里插入图片描述

1.前言

2.内存函数讲解及各自模拟实现

2.1.memcpy

2.1.1.讲解

2.1.2.模拟实现

2.2.memmove

2.2.1.讲解

2.2.2.模拟实现

2.3.memset

2.3.1.讲解

2.4.memcmp

2.4.1.讲解

3.完结撒花

                    创作不易,点赞关注,感谢支持qaq

1.前言

所谓内存函数,与指针地址一样,都是基于内存上面完成的函数,与其他一些基于数值运算不同,其相当于在根本上完成了运算,之前我在《函数栈帧的创建与销毁》这篇博客中讲过,每一次函数调用都是在内存上开辟了一块新的空间,所以我们所敲的代码,运算最根本是在内存中完成,所以我们深入学习就必须认清内存存储的原理,在此之前建议大家把指针,地址的知识弄明白,再进行内存函数的学习。这节所讲的内存函数都是基于内存进行操作的,一些比较抽象的地方我会画图帮助大家理解。

2.内存函数讲解及各自模拟实现

2.1.memcpy

2.1.1.讲解

memcpy与之前所讲的字符串拷贝函数strcpy,对于字符串的使用作用一样,但是原理不同,并且memcpy不仅可以拷贝字符串,对于结构体,整形数组等也能进行拷贝,所以这就不难理解memcpy是基于内存进行拷贝的了。下面我们可以在MSDN上面查找该内存函数,先观察其形参是如何传递的,见下图:
在这里插入图片描述可以看到memcpy其所包含的头文件可以是string.h或者是memory.h,其形参传递形式为两个泛型指针,一个是源头src即拷贝的对象,另一个是目的地dest即要进行拷贝的目的地,count是表示要拷贝的字节数,注意这里进行内存拷贝所拷贝的长度单位为字节。
下面我们来拷贝一个整形数组给大家进行讲解,见下图:
在这里插入图片描述我们是将arr1中的前5个数据进行拷贝,拷贝到arr2中,需要注意的就是后面拷贝的字节数据是如何进行传参的。
对于memcpy基于内存进行操作我们如何来观察,可以对代码进行调试,然后点击[调试] -> [窗口] -> [内存],打开两个内存监视窗口,这里所展示的就是内存中的存储,下面是拷贝完成后的内存情况:
arr1:
在这里插入图片描述arr2:
在这里插入图片描述可以看到内存存储显示也是发生了变化。
最后在MSDN上的查找中可以看到,memcpy的返回类型是void*,其实在完成了拷贝之后,memcpy函数会返回拷贝目的地的首地址即arr2的首地址,不过类型为void*类型,如下图我们以拷贝字符串为例:
在这里插入图片描述

虽然编译器可以运行,但是会报警告
我们需要对其进行强制类型转换,如下图所示:
在这里插入图片描述
代码如下:

int main()
{
	char arr1[10] = "12345aaaaa";
	char arr2[10] = { 0 };

	char* p1 = (char*)memcpy(arr2, arr1, 6 * sizeof(char));
	printf("%s", p1);
	return 0;
}

这里我们还需要注意memcpy在拷贝字符串时,遇见\0也不会停止拷贝,其会将所传的字节范围的数据全都拷贝下来,见下图:
在这里插入图片描述我们可以看到memcpy进行拷贝后,arr2数组中连同arr1中的\0也进行了拷贝。

2.1.2.模拟实现

我们知道memcpy的原理,下面我们仿照memcpy函数自己上手进行一下模拟实现与其功能一样的函数,实现代码如下:

void* my_memcpy(void* dest, const void* sor, size_t count)
{
	assert(dest && sor);
	void* p1 = dest;
	while (count--)
	{
		*(char*)dest = *(char*)sor;
		dest = (char*)dest + 1;
		sor = (char*)sor + 1;
	}
	return p1;
}

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

上面代码就对memcpy进行了实现,可以看到我们所设计的my_memcpy与
在MSDN上面搜索的memcpy的结构是一致的,这保证了我们所实现的功能与memcpy一样。
这里我们需要知道memcpy对重叠部分的拷贝是不做处理的,什么是重叠的意思?我们看下图:
在这里插入图片描述可以看到我们要做的操作是将arr1的数组前26字节的数据拷贝到arr1+2中,可能有人不知道arr1+2是什么意思,这里的arr1表示的是数组首元素地址,加2则指向数组arr1的第三个元素的地址,那么按照我们之前所讲memcpy进行拷贝,所打印出来的结果应为1,2,1,2,3,4,5,6,9,10,才对,但其打印结果与所推理并不符合,其实我们对这打印出来的结果仔细观察便可以发现,从arr1[4]和arr1[5]开始,其拷贝的是arr1[2]和arr1[3]上的数据,而arr1[2]和arr1[3]上的数据在此之前已经进行了拷贝,所以数据便会重叠,进而形成所打印的1,2,1,2,1,2,1,2,9,10.
所以memcpy不会处理重叠的情况,但在一些编译器上面比如VS2022中使用memcpy拷贝重叠数据会被处理,进而打印出想要的结果,这是因为在此编译器中memcpy函数是被进行了优化处理过的,而在标准的C语言规定中memcpy确实对于拷贝重叠数据的情况是不做处理的
那么问题来了,如果我们在拷贝时对于重叠状况需要处理的话,我们应该怎么办,下面就让我们进入memmove函数的讲解。

2.2.memmove

2.2.1.讲解

大家可以理解为,memcpy是对于重叠情况不做处理的内存拷贝函数,而memmove是对重叠情况做出相应处理的内存拷贝函数。我们也在MSDN
上面查看一下该函数的构造:
在这里插入图片描述可以看到该函数的构造与memcpy完全一样,下面我们看下图:
在这里插入图片描述可以看到对于重叠的情况已经做出处理

2.2.2.模拟实现

对于memmove的模拟实现我们也相应的要对重叠部分做出处理,我们先把模拟实现代码奉上:

void* my_memmove(void* dest, const void* sor, size_t count)
//实现这个函数需要分情况考虑是从前向后还是从后向前进行拷贝
{
	assert(dest && sor);
	void* p1 = dest;
	//前 -> 后
	if (sor > dest)
	{
		while (count--)
		{
			*(char*)dest = *(char*)sor;
			dest = (char*)dest + 1;
			sor = (char*)sor + 1;
		}
	}
	else//后 -> 前
	{
		while (count--)
		{
			*((char*)dest + count) = *((char*)sor + count);
		}
	}
	return p1;
}

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

要想实现memmove函数的模拟,我们就需要对于重叠部分进行分析,对于拷贝起源地址sor和拷贝目的地址dest的重叠部分一共有两种可能:
下面我进行画图来方便大家理解,我们假设在一个整型数组内要拷贝20个字节。
1.sor > dest,即起源地址sor的地址高于目的地址dest:
在这里插入图片描述2.sor < dest:
在这里插入图片描述对于第一中重叠我们就可以进行从前向后的拷贝,结果也不会出现重叠的状况,而对于第二种我们如果还是进行从前向后的拷贝,那么打印的结果就会出现重叠的情况,因为我们在对4,5进行拷贝后,此处地址中所存储的是2,3,而我们继续拷贝在sor中已经拷贝到原先4处的地址,而现在4处存放的是2,而5处存放的是3,所以就会出现重叠的情况。所以我们在sor < dest的情况中要进行从后向前的拷贝,即从sor中的6开始向前,与dest中的8开始向前进行拷贝,这样就不会出现重叠的状况。

2.3.memset

2.3.1.讲解

memset为内存设置函数
在这里插入图片描述上图可知,memset函数是设置了三个形参,其所包含的头文件可以是memory.h或string.h,其功能是设置内存,将内存中的值以字节为单位设置成想要的内容:
在这里插入图片描述上图我们就将arr数组的前5个字节在内存中设置成了x。
这里我们需要注意的是,看下面举例:
在这里插入图片描述这里我们将arr数组中的40个字节设置成了1,其是以字节为单位进行设置的,所以并不是将数组arr中的值都改成了1.

2.4.memcmp

2.4.1.讲解

memcmp与函数strcmp相似,不过memcmp是基于内存进行比较的,如下图:
在这里插入图片描述在这里插入图片描述所以总体归类,memcmp为比较函数,当产生大于值时返回的是大于0的数字,当产生小于值时返回的是小于0的数字。
在这里插入图片描述当然,其比较也是以字节为单位进行,上图我们比较16个字节,而对于arr1和arr2中前16字节是相同的,所以返回的是0.
在这里插入图片描述我们在比较17个字节的时候,arr2中第17个字节为6,大于arr1的5,所以返回的是小于0的数字。

3.完结撒花
今天的知识分享到此为止,如果对你有帮助希望可以点赞收藏一下,今后我也还会分享更多在学习路上遇到的知识,希望我们可以一起学习共同进步。
在这里插入图片描述

  • 35
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. atof(): 将字符串转换为double类型的值。 例如: ```c++ char str[] = "3.14"; double num = atof(str); printf("%f", num); ``` 结果为:3.140000 2. atoi(): 将字符串转换为int类型的值。 例如: ```c++ char str[] = "1234"; int num = atoi(str); printf("%d", num); ``` 结果为:1234 3. atol(): 将字符串转换为long类型的值。 例如: ```c++ char str[] = "1234567"; long num = atol(str); printf("%ld", num); ``` 结果为:1234567 4. strtod(): 类似于atof(),将字符串转换为double类型的值。 例如: ```c++ char str[] = "3.14"; double num = strtod(str, NULL); printf("%f", num); ``` 结果为:3.140000 5. strtol(): 将字符串转换为long类型的值,同时支持指定转换的基数(例如10进制、16进制等)和错误检查。 例如: ```c++ char str[] = "0110"; long num = strtol(str, NULL, 2); printf("%ld", num); ``` 结果为:6 6. strtoul(): 类似于strtol(),不过返回的是无符号的long类型。 例如: ```c++ char str[] = "0xA"; unsigned long num = strtoul(str, NULL, 16); printf("%lu", num); ``` 结果为:10 7. memset(): 将一段内存区域设置为指定的值。 例如: ```c++ char str[10]; memset(str, 'a', sizeof(str)); printf("%s", str); ``` 结果为:aaaaaaa 8. memcpy(): 将一段内存区域的内容复制到另一段内存区域。 例如: ```c++ char src[] = "hello"; char dst[10]; memcpy(dst, src, sizeof(src)); printf("%s", dst); ``` 结果为:hello 9. memmove(): 和memcpy()类似,但是保证在有重叠的情况下会正确工作。 例如: ```c++ char str[] = "hello"; memmove(str + 2, str, 3); printf("%s", str); ``` 结果为:hehlo 10. memcmp(): 比较两段内存区域的内容是否相等。 例如: ```c++ char str1[] = "hello"; char str2[] = "Hello"; int result = memcmp(str1, str2, 5); printf("%d", result); ``` 结果为:32(h和H的ASCII码差值) 11. memchr(): 在一段内存区域中搜索指定的字符,并返回指向该字符的指针。 例如: ```c++ char str[] = "hello"; char* ptr = (char*)memchr(str, 'l', 5); printf("%s", ptr); ``` 结果为:ll 12. strcpy(): 将一个字符串复制到另一个字符串。 例如: ```c++ char src[] = "hello"; char dst[10]; strcpy(dst, src); printf("%s", dst); ``` 结果为:hello 13. strncpy(): 类似于strcpy(),不过只会复制指定长度的字符。 例如: ```c++ char src[] = "hello"; char dst[10]; strncpy(dst, src, 3); dst[3] = '\0'; printf("%s", dst); ``` 结果为:hel 14. strcat(): 将一个字符串附加到另一个字符串的末尾。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; strcat(str1, str2); printf("%s", str1); ``` 结果为:helloworld 15. strncat(): 类似于strcat(),不过只会附加指定长度的字符。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; strncat(str1, str2, 3); printf("%s", str1); ``` 结果为:helloworld 16. strcmp(): 比较两个字符串是否相等。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; int result = strcmp(str1, str2); printf("%d", result); ``` 结果为:-15 17. strncmp(): 类似于strcmp(),不过只会比较指定长度的字符。 例如: ```c++ char str1[] = "hello"; char str2[] = "world"; int result = strncmp(str1, str2, 3); printf("%d", result); ``` 结果为:0 18. strchr(): 在一个字符串中搜索指定的字符,并返回指向该字符的指针。 例如: ```c++ char str[] = "hello"; char* ptr = strchr(str, 'l'); printf("%s", ptr); ``` 结果为:llo 19. strrchr(): 类似于strchr(),不过会从字符串的末尾开始搜索。 例如: ```c++ char str[] = "hello"; char* ptr = strrchr(str, 'l'); printf("%s", ptr); ``` 结果为:lo 20. strstr(): 在一个字符串中搜索指定的子字符串,并返回指向该子字符串的指针。 例如: ```c++ char str[] = "hello world"; char* ptr = strstr(str, "world"); printf("%s", ptr); ``` 结果为:world

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值