C语言之内存函数

导读:本篇文章讲解 C语言之内存函数,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

在此之间,我们学过许多的字符函数,例如strcpy,strstr,strcmp等等,这里我们以strcpy函数进行举例:

//实现将arr1拷贝到arr2中
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 0 };
	strcpy(arr2, arr1);
	return 0;
}

程序并没有正确运行,编译器报错:
在这里插入图片描述

错误分析如下:
在这里插入图片描述上述代码是无法实现将arr1拷贝到arr2中,由此可见,像strcmp,strcpy,strstr这种字符串函数,它虽然能够实现字符串拷贝,比较等功能,但是由于它们的操作对象是字符串,因此对于整形数组,浮点型数组等并不适用。

那么对于整形数组,浮点型数组等其他的数据,我们该如何进行操作呢?

c语言给出了另一类函数——-内存函数(memcpy/memmove/memcmp)

memcpy:

//void*-----通用类型指针:可以接受各种类型的参数
void*memcpy(void*destination,const void*source,size_num)//size_num的单位是字节

还是选用上述实例:

#include<stdio.h>
#include<string.h>
//实现将arr1拷贝到arr2中
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 0 };
	memcpy(arr2, arr1,sizeof(arr1));
	return 0;
}

此时打开监视窗口,我们不难发现,数组之间完美的实现了拷贝。
在这里插入图片描述

结构体类型实现拷贝:

举例:

struct person 
{
	int age;
	char name[20];
	char phone[13];
};
int main()
{
	struct person arr3[] = { {20,"张三","20031319"},{19,"lisa","193684"} };
	struct person arr4[3] = {0};
	memcpy(arr4, arr3, sizeof(arr3));
}

此时打开监视窗口,我们不难发现,结构体之间完美的实现了拷贝。

在这里插入图片描述

模拟实现memcpy:

以整形进行举例:

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* scr,size_t num)
{
	void* ret = dest;
	//断言:为避免是空指针
	assert(dest != NULL);
	assert(scr != NULL);
	while (num--)//num为要拷贝的长度
	{
	//不能直接解引用操作的原因:void*类型不能直接进行++/--操作
		*(char*)dest = *(char*)scr;//先强制类型转换在解引用
		dest=(char*)dest+1;
		scr=(char*)scr+1;
	}
	return ret;//不能直接返回dest,因为此时的dest并不是首地址
}
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1,sizeof(arr1));
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}
1 2 3 4 5

上述实例,我们是实现的功能是将一个数组中的元素拷贝到另一个数组中,如果现在我们实现将数组中的前几个元素拷贝到后面的几个呢?

举例:

//实现将1 2 3 4 5拷贝到3 4 5 6 7的位置上
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* scr,size_t num)
{
	void* ret = dest;
	assert(dest != NULL);
	assert(scr != NULL);
	while (num--)
	{
		*(char*)dest = *(char*)scr;
		dest=(char*)dest+1;
		scr=(char*)scr+1;
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr1+2, arr1,20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

预期输出结果为:

1 2 1 2 3 4 5 8 9 10

实际输出结果:

1 2 1 2 1 2 1 8 9 10

预期和实际不相符分析如下:
在这里插入图片描述
由此可得出:我们所编写的my_memcpy函数并不能实现在同一个数组中的拷贝,会出现数的覆盖现象。

那库函数memcpy能否实现呢?

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr1+2, arr1,20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

输出结果如下:

1 2 1 2 3 4 5 8 9 10

看到这里,相信很多人都会产生疑惑,为什么库函数memcpy可以实现,而我们编写的my_memcpy不能实现呢?难道是我们写错了?

事实并非如此,memcpy虽然也实现了这样的功能,但C语言标准规定:memcpy:用来处理不重叠的内存拷贝。memmove:处理重叠的内存拷贝

我们所编写的my_memcpy是因为严格按照C语言的标准所编写,而在VS编译器上memcpy超额完成了任务,相当于抢了memmove的饭碗。

下面我们就来学习memmove函数!

memmove:

依然是上述实例:

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1+2, arr1,20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

输出结果如下:

1 2 1 2 3 4 5 8 9 10

现在得出的结果正是我们预期的效果!

模拟实现memmove:

目的地的地址低于源头地址:

正序进行拷贝:

#include<stdio.h>
#include<assert.h>
my_memmove(void* dest, const void* scr, size_t count)
{
	assert(dest != NULL);
	assert(scr != NULL);
	void* ret = dest;
	while (count--)
	//直接按照源头一个个进行打印,不存在数的覆盖
	{
		*(char*) dest= *(char*) scr;
		dest = (char*)dest + 1;
		scr = (char*)scr + 1;
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1, arr1+2,20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

这种比较简单,这里就不赘述分析过程了
在这里插入图片描述输出结果为:

3 4 5 6 7 6 7 8 9 10

目的地址高于源头地址:

倒序进行拷贝:

#include<stdio.h>
#include<assert.h>
my_memmove(void* dest, const void* scr, size_t count)
{
	assert(dest != NULL);
	assert(scr != NULL);
	void* ret = dest;
	while (count--)
	{
		*((char*)dest + count) = *((char*)scr + count);
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+2, arr1,20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

在这里插入图片描述输出结果为:

1 2 1 2 3 4 5  8 9 10

对于该行代码的实现过程,我们分析如下:

*((char*)dest + count) = *((char*)scr + count);

在这里插入图片描述
对于上述两种情况,我们可用一个程序进行表示:

#include<stdio.h>
#include<assert.h>
my_memmove(void* dest, const void* scr, size_t count)
{
	assert(dest != NULL);
	assert(scr != NULL);
	void* ret = dest;
	if(dest<scr)//正序进行拷贝
	while (count--)
	{
		*(char*)dest = *(char*)scr;
		dest = (char*)dest + 1;
		scr = (char*)scr + 1;
	}
	else//倒序进行拷贝
	{
	while(count--)
	{
		*((char*)dest + count) = *((char*)scr + count);
	}
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1+2, arr1,20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

输出结果如下:

1 2 1 2 3 4 5 8 9 10 

memcmp:

将两个存储区的前n个字节进行比较。

举例:

#include<stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,5,4,3 };
	int ret=memcmp(arr, arr2, 9);//arr>arr2,返回大于零的数字,arr<arr2,返回小于零的数字,二者相等,返回0
	//注:VS编译器返回-1/0/1,虽然不严谨,但是并不违背C语言的标准规定
	printf("%d\n", ret);
	return 0;
}

输出结果如下:

-1

分析如下:
在这里插入图片描述

memset:内存设置函数

举例:

字符型:

#include<stdio.h>
int main()
{
	char arr[10] = "";
	memset(arr, '#', 10);//10代表更改10个字节
	return 0;
}

在这里插入图片描述整形:

#include<stdio.h>
int main()
{
	int  arr[10] = {0};
	memset(arr, 1, 10);
	return 0;
}

输出如下:
在这里插入图片描述让不少人产生疑惑的是:为什么此时并没有实现将数组中的10个元素都修改为1呢?

原因是:该函数的操作单位是字节,而数组是一个整形数组,其中的元素都为整形,每个元素为4个字节。

具体分析如下:
在这里插入图片描述因此,在使用该函数的时候,一定要注意类型。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/81467.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!