【C】剖析C语言内存函数

前言:

上一篇文章详细介绍了字符串函数,那字符串函数和内存函数到底有什么区别呢?

最根本的区别在于,他们操作的对象不同,视角不同。

字符串函数针对的是一个个的字符,而内存函数顾名思义关注的是内存,存储在内存中的一个个字节。

一、memcpy函数

功能:

复制内存块,可以将任意类型的数据进行拷贝。

将source的num个字节的内容拷贝到destination内存中

参数和返回值:

前面两个参数分别是目标内存的起始地址和源内存的起始地址,第三个参数是需要拷贝内容的字节个数

返回值是destination的首元素地址

头文件:

#include <string.h>

与strcpy的区别

  • memcpy不需要考虑' '的问题,因为操作对象就是内存,视角不同
  • memcpy可以拷贝任意类型的数据,而strcpy只能拷贝字符的数据

注意事项:

  • 因为要接收任意类型的指针数据,所以函数参数类型就是void*
  • 如果source和destination有任何的重叠,复制的结果都是未定义的,容易拷贝已经重叠的内容。

模拟实现:

char* my_memcpy(void* dest, const void* sou, size_t num)
{
	void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)sou;
		//(char*)dest++;
		dest = (char*)dest + 1;
		sou = (char*)sou + 1;
	}
	return ret;
}


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

灵魂总结:

  • 如果函数的参数可以接收任意类型的数据,那么设置这种函数参数一般用void*
  • 如果函数可以对任意类型的数据进行改变,并且根据字节的个数来决定,那么函数参数的类型设置为char*

二、memmove函数

功能:

与memcpy函数功能相似,但是memmove函数功能更强大,可以拷贝带有重叠的内存块,因此我们以后可以直接采用memmove函数去拷贝内容,不论是重叠或不重叠 。

TIP:memcpy和memmove的渊源:

为何memcpy函数拷贝重叠的字符串会有问题呢?

 比如我们有这样的一段内存,然后soul为起始地址,传3个整型(12个字节)到dest位置上。

但是如果用memcpy拷贝的方法,发现已经先把1覆盖到了3的位置,所以3就变成了1,之后再想拷贝3发现已经被覆盖为1,所以此法不通。

解决方法: 

我们可以从后向前拷贝,先将sou最后的3拷贝到dest最后的5,接着再向前拷贝,这样就避免了重叠的问题

新的问题:

 还是一样的需求,把sou为起始地址,传3个整型(12个字节)到dest位置上。

但这时再从后向前拷贝,会将3重叠为5,之后再想拷贝3,就会变成5.

解决方法:

我们从前向后拷贝,从sou的3开始拷贝,这样也避免了重叠问题。

总结:

遇到不同的情况,我们采取不同的措施,分情况的标准就是sou,和dest的地址高低,若sou<dest,我们就采用从后向前拷贝,若sou>dest,我们就采用从前向后的拷贝方法。

模拟实现memmove函数

#include<assert.h>

void* my_memmove(void* dest, void* soul, size_t num)
{
	void* ret = dest;
	if (soul < dest)
	{
		dest = (char*)dest + num-1;//需要减去1,因为+1就是两个字节了
		soul = (char*)soul + num-1;
		while (num--)
		{
			*(char*)dest = *(char*)soul;
			dest = (char*)dest - 1;
			soul = (char*)soul - 1;
		}
	}
	else
	{
		while (num--)
		{
			*(char*)dest = *(char*)soul;
			dest = (char*)dest + 1;
			soul = (char*)soul + 1;
		}
	}
	return ret;
}

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

三、memcmp函数

功能:

两个指针指向的内容进行比较,并从起始位置往后的num个字节内容进行比较

参数和返回值:

参数为两个指针,并且加上限制字节的个数

返回值,如果前者大于后者,返回大于0的数字,如果前者小于后者,返回小于0的数字,两者相等,返回0。

注意:

  • 可以比较任意类型的数据
  • 比较的方式就是一个字节的内容和另一个字节的内容进行比较

四、memset函数

功能:

字节为单位,将ptr指向的内容修改为num个字节的value值 

实操:

上面是char类型,下面是int类型,int类型更便于我们理解

 

时时刻刻要想到内存函数操作的对象是字节,比如上面的整型例子,memset操作了10个字节,相当于把两个整型的内容改成01 01 01 01,然后第三个元素改成01 01 00 00