【C语言】内存函数——按字节拷贝

  • Post author:
  • Post category:其他



目录


一.memcpy函数


例题


例1:


例二:


模拟实现:





.


memmove函数


地址重叠


des>sou


des<>


模拟实现


三.memset函数


例题:


使用场景:


总结:


一.memcpy函数

void* memcpy(void* destination, const void* source,size_t num);
//传参后使用void*接收,说明这个函数不论是什么类型都会执行。
  • size_t表示无符号数字

  • 函数memcpy从source的位置开始向后复制num个

    字节

    的数据到destination的内存位置。

  • 这个函数在遇到’\0’的时候并不会停下来。

  • 如果source和destination有任何的重叠,复制的结果都是未定义的。

例题

例1:

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr, 20);

	return 0;
}

例二:

int main()
{
	float arr1[] = { 1.0f,2.0f,3.0f,4.0f };//数值后加f表示该数为浮点数
	float arr2[5] = { 0 };
	memcpy(arr2, arr1, 8);

	return 0;
}

模拟实现:

//这种写法不是最好的写发,有些编译器是编不过去的
#include<assert.h>
void* my_memcpy(void* destination, const void* source,size_t num)
{
    assert(destination);
    assert(source);
    void* ret = destination;
	for (int i = 0; i < num; i++)
	{
		*((char*)destination)++ = *((char*)source)++;
	}
    return ret;
}

//这种方法可以在所以编译器中使用
void my_memcpy(void* destination, const void* source,size_t num)
{
    assert(destination);
    assert(source);
    void* ret = destination;
	while(num--)
	{
		*(char*)destination = *(char*)source;
        destination = (char*)destination + 1;
        source = (char*)source + 1;
	}
    return ret;
}
  • 当我们测试source和destination在一个数组并且它们拷贝的范围部分重合时,会发生错误。

例如使用下面的代码传递:

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
my_memcpy(arr+2, arr, 20);

内存展示为:

而编译器定义的memcpy函数却不是这样定义的,memcpy的结果为:

这并不是说之前写的模拟函数有问题,在这里我们需要明白一点,在

C语言规定

中,那段错误的结果才是memcpy真实的结果,但我使用的是VS编译器,由于编译器的不同,它将memcpy改的更为强大了而已,其它编译器并非如此。

在C语言规定中,当拷贝的两组数地址部分重合时,应该使用memmove函数,memmove函数的功能是

强于

memcpy的,接下来讨论的编译过程也是属于memmove的但在VS编译器当中两者差异不大,这里以

C语言规定

为准,但大家在平时练习的时候需要注意这一点。

二.memmove函数

void* memmove(void* destination,const void* source,size_num);
  • 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

  • 如果源空间和目标空间出现重叠,就得使用memmove函数处理。

地址重叠

当拷贝的两组数出现重叠时,让我们看看memmove函数是如何处理的。

这个时候我们就要分类讨论了:

  • des == destination

  • sou == source

  • num == 20;

  • 此时des和sou分别是destination和source所表示的地址

des>sou

  • 如图,当des的地址大于sou时我们就需要从sou最后一位数开始拷贝,这样才能防止被覆盖。

des<sou

  • 这个时候我们就要从前依次往后拷贝,才能顺利完成拷贝。

模拟实现

void* my_memmove(void* destination, const void* source, size_t num)
{
	void* tem = destination;
	if (destination < source)
	{
		for (int i = 0; i < num; i++)
		{
			*(char*)destination = *(char*)source;
			destination = (char*)destination + 1;
			source = (char*)source + 1;
		}
	}
	else
	{
		destination = (char*)destination + num - 1;
		source = (char*)source + num - 1;
		for (int i = 0; i < num; i++)
		{
			*(char*)destination = *(char*)source;
			destination = (char*)destination - 1;
			source = (char*)source - 1;
		}
	}
	return tem;
}

三.memset函数

  • 此函数目前我见到使用最多的场景是用来

    初始化一块连续的空间(可以看作数组)

在第一次完成这篇博客时,我没想着把这个函数加入其中,我感觉它不是很重要,在今天做题的时候,勾起我对这个memset复杂的回忆。

之前做题时,创建数组后初始化都是进行循环一个个初始化,一次看题解不经意的一瞥,注意到这个函数,用了以后感觉是真香。但毕竟做题少而且平时练习的时候都是直接给数组初始化为0,不像做题的时要自己malloc一个新的空间。

void* memset(void* ptr,int value,size_t num);
  • sets the first num bytes of the block of memory pointed by ptr the specified value(interpreted as  an unsigned char).
  • memset函数为内存填充函数
  • num表示填充多少个字节
  • value表示填充的值

例题:

int main()
{
	int arr1[] = { 1,2,3,4,5 };

	memset(arr1, 9, 8);

	return 0;
}

arr1数组最开始的内存:

memset后:

  • 将前8个字节全都填充为9

使用场景:

int main()
{
	int* arr = (int*)malloc(sizeof(int) * 5);//开创了5个int大小的空间
	memset(arr, 0, 20);

	return 0;
}

memset前:

memset后:

总结:

  • 按找C语言的标准来说,memmove的适用范围要比memcpy的更广
  • 这两个函数是以字节为单位,按字节改变的。
  • memset常用来初始化空间



版权声明:本文为m0_52094687原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。