Bootstrap

内存函数的相关知识点

1 strerror
char * strerror ( int errnum )
从语言的库函数在运行的时候,如果发生错误,就会将错误码放在一个变量中,这个变量是errnor.

//strerror(errno)
// fopen
//FILE * fopen ( const char * filename, const char * mode );
//如果打开文件成功,就返回一个有效的指针,如果打开失败,就返回空指针。
int main()
{//打开该文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		printf("打开文件失败\n");
		return 1;
	}
		//读文件
			//关闭文件
		fclose(pf);
	return 0;
}

在这里插入图片描述
在这边我们就可以使用strerror来打印报错信息
在这里插入图片描述
这边显示没有该文件,我们在工程里面创建一个该文件。
另外还有一个报错的函数是perror函数
void perror ( cconst char*string )
直接打印错误信息,在打印错误信息前,会先打印自定义的信息,perror=printf+strerror

int main()
{//打开该文件
	FILE* pf = fopen("test.txt", "r");
	if (pf == NULL)
	{
		perror("fopen");
		return 1;
	}
		//读文件
			//关闭文件
		fclose(pf);
		pf = NULL;
	return 0;
}

在这里插入图片描述
以上就是字符串函数介绍。
但是在代码中常常有其他类型的函数,如结构体,整型,浮点型等等,我们就需要学习一些内存函数。如memcpy,memmove,memcpy,memset等等
2. 内存函数的拷贝 memcpy

void * memcpy ( void * destination, const void * source, size_t num );

函数介绍:
memcpy函数是一个用于拷贝两个不相关的内存块的函数。memcpy函数会从src的位置开始向后复制count个字节的数据到dest的内存位置,并返回dest的首地址。
注意点:
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
例如我要从arr1数组中打印3,4,5,6,7五个元素到arr2数组中去。

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

因为一个整型是4个字节,所以我们可以移动num=20个字节就可以移动5个元素了。
2.1 memcpy的模拟实现

void* my_memmcpy(void* dest, void* src, size_t num)
{
	char* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1 + 2, 20);
	return 0;
}

在这里插入图片描述
如图中就是要src打印的数字,我们把src强制类型转化成char*的类型,首先是3的地址解引用将src的值赋给dest。
然后指针+1,这样在就可以按照一个字节一个字节的进行打印,num为0时,为假就跳出循环。

3 memmove(字符串拷贝函数)
函数介绍以及与(memcpy的区别)

void * memmove ( void * destination, const void * source, size_t num );

我们发现memmove函数的参数和返回值与memcpy函数一模一样。没错,memmove函数和memcpy函数的功能一样,也是从src的位置开始向后复制count个字节的数据到dest的内存位置,并返回dest的首地址。
那么它们有什么不同呢?
memmove函数和memcpy函数的差别就是,memmove函数的源内存块和目标内存块是可以重叠的,而memcpy函数的源内存块和目标内存块是不可以重叠的。
如果我们要将arr数组中的1,2,3,4,5,6,7,8,9,10我们要打印1,2,3,4,5放到3,4,5,6,7上去,应该怎么打印呢?

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

但是使用memcpy的时候,会打印成1,2,1,1,1为啥会变成这样呢?原因是因为前面的3被覆盖成1,导致后面全变成1了。但是我们从后面打印呢?
就会变成7变5,6变4,5变3,4变2,3变1,最后打印就变成了1,2,1,2,3,4,5,8,9,10.
在这里插入图片描述
2 模拟实现
在这里插入图片描述
我们发现,当源内存块和目标内存块发生重叠的时候,我们不能像memcpy函数一样直接从前向后依次拷贝,我们需要分情况讨论。
在这里插入图片描述

通过画图,我们可以根据dest指针和src指针的相对位置将情况分为三类:
第一类:dest指针位于src内存块左边,采用从前向后拷贝。
第二类:dest指针位于src内存块内,采用从后向前拷贝。
第三类:dest指针位于src内存块右边,采用从前向后和从后向前均可以。
注:当dest指针与src指针位于同一位置时不用拷贝。

#include<assert.h>
void* my_memmove(void* dest,void* scr, size_t num)
{
	assert(dest && scr);
	char* ret = dest;
	if (dest < scr)
	{
		while (num--)
		{
			*(char*)dest = *(char*)scr;
			dest = (char*)dest + 1;
			scr = (char*)scr + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)scr + num);
		}
	}
	return ret;
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr+2, arr, 20);
	return 0;
}


memcmp

int memcmp ( const void * ptr1,
const void * ptr2, size_t num );

memcmp函数是一个用于比较两个内存块大小的函数。它会比较从buf1和buf2指针开始的count个字节,当buf1大于buf2的时候返回一个大于0的数;当buf1等于buf2的时候返回0;当buf1小于buf2的时候返回一个小于0的数

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7 };
	int arr2[] = { 1,2,3,5,6,7,8,9 };
	int ret1=memcmp(arr1, arr2, 8);
	int ret2=memcmp(arr1, arr2, 16);
	printf("%d\n", ret1);
	printf("%d\n", ret2);

	return 0;
}

在这里插入图片描述
第四个 memset

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

memset函数可以将内存块的某一部分设置为特定的字符。三个参数中,第一个参数是开始设置内存的起始位置,第二个参数是要将内存设置成的字符,第三个参数是从起始位置开始需要设置的内存的字节数。

int main()
{
	char arr[] = "hello world";
		memset(arr,'1',5);//将hello改成字符'1'
		printf("%s\n", arr);
		memset(arr + 6, '2', 5);//将world改成'2;
		printf("%s\n", arr);
	return 0;
}

在这里插入图片描述
如果用这个内存函数将整型数组arr中的元素全部改成1呢?

int main()
{
   int arr[10]={1,2,3,4,5,6,7,8,9,10};
   memset(arr,'1',40);
  return 0;
}

这样子可以吗?
我们可以调试一下看看。
在这里插入图片描述
我们会发现数字变得特别大。原因是memset函数是一个字节一个字节移动的·,每移动一个字节,就会改变成一个1。
在这里插入图片描述
调试到arr的地址,调试之后到memset就会变成
在这里插入图片描述

;