鹏哥C语言复习——内存函数

发布于:2024-05-02 ⋅ 阅读:(25) ⋅ 点赞:(0)

目录

一.memcpy函数

二.memmove函数

三.memset函数

四.memcmp函数


 

一.memcpy函数

该函数是针对内存块进行拷贝操作,mem即为memory,是内存的意思;cpy就是copy,是拷贝的意思

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

如上两个数组,我们如何才能不通过循环的方式将arr数组中的前五个元素放入arr2数组呢?

这时就需要用到memcpy函数

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

注意事项:

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination的位置
  • 这个函数在遇到'\0'时并不会停下来
  • 如果source和destination有任何的重叠,复制的结果都是未定义的(源空间和目标空间在内存使用上不能有重叠)
  • 使用时需要引用头文件<string.h>
  • 学会memcpy函数的模拟实现
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr, 20); //就是把20个字节的内容从arr数组中存入到arr2数组中,即5个元素(整型变量一个占4字节)
	return 0;
}

 模拟实现:

10df79bf13ea4054bde78f69ec6993bf.jpg

 两个指针在开始时,分别指向开始处,可以通过一个字节一个字节拷贝来实现memcpy的功能

int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr, 20); //就是把20个字节的内容从arr数组中存入到arr2数组中,即5个元素(整型变量一个占4字节)
	return 0;
}

//memcpy函数拷贝结束后,会返回目标空间的起始地址,但返回以后需不需要创建一个新指针接收返回的起始地址是随意的
void* my_memcpy(void* dest,const void* src, unsigned int num) //把万能指针作为形参是为了解决实参的不同类型(可能是字符型也可能是整型)
{
	void* ret = dest;
	//一个字节一个字节拷贝,循环num次即可
	int i = 0;
	assert(dest && src); //两个指针不能为空
	while (num--) //后置--,先使用再--
	{

		*(char*)dest = *(char*)src; //为了一个字节一个字节拷贝,所以可以将两个万能指针强转成char*指针(指向char类型的指针),然后再解引用,访问1字节的数据
		src = (char*)src + 1;   //用于运算的表达式必须是指向完整对象类型的指针,因此需要再次进行强制转换;但用于接收的表达式可以是万能指针
		dest = (char*)dest + 1; //同时不能写成(char*)src++的形式,因为强制转换操作符的优先级与++相同,但这两个操作符是从右往左运算,先使用了src,再++,最后在强转,违背了上一条规律
								//但可以写成((char*)src)++这种形式,可以看成(char*)src = (char*)src + 1, = 前面的src强制转化就是在把void * 类型强制转换为char* 类型,依然满足需求
								//笔者在此推荐src = (char*)src + 1这种形式,因为这种形式下用于接收的src还是void*类型,什么指针类型都能接收,而char* 类型只能接受char* 类型
                                //而对于强制类型转换,一个变量的数据类型被强制转换后,出了这条代码后就不会再保持被强制转换后的数据类型了,因为强制类型转换只是作为运算符存在,并不能真的改变src或者dest的数据存放类型
	}
	return ret;
}

注:模拟实现的代码思想与注意事项在上文代码中的批注里

1.那为什么我们必须一个字节一个字节拷贝呢?

如果是以整型变量的数据大小(4字节)来拷贝,那如果我们传入的num大小为7就无法完成拷贝工作,因此需要以1个字节大小来进行拷贝

2.对于上文中所提到的source和destination有任何的重叠,复制的结果都是未定义的,是什么意思?

例如要让arr的前5个数据(12345)memcpy到第三个数据(3)开始中去,即使memcpy函数可以让arr数组变成1212345(vscode编译器),但是是存在复制结果未定义风险的

 

二.memmove函数

该函数与memcpy函数的差别就是memmove处理的源内存块和目标内存块可以是重叠的,因此如果源空间和目标空间出现重叠,就得使用memmove函数处理

模拟实现:

在模拟函数时,关键在于如何将一个数组里的重叠元素拷贝,共分为以下两种情况:

1.src(源空间)的首元素在dest(目标空间)的首元素前,这种情况下需要从dest以及src的末尾元素开始进行拷贝

2.src的首元素在dest的首元素后,这种情况下需要从dest以及src的首元素开始进行拷贝

情形1: 

 589071b7d13946009586524765353b38.jpeg

情形2:

8208fe66ff7b4c8ab16c5616a0d7d222.jpeg

下图含义为,当dest在src前,就从前往后拷贝;当dest在src后,就从后往前拷贝;当src和dest没有重合,就即可从后往前又可以从前往后,笔者在下面的代码中只会尝试从后往前的情况 

3f063a4f53004aaaa175db824c3934df.jpeg

void my_memmove(void* dest, void* src, unsigned int num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src) //存放地址的顺序前后比较,数组是连续存放,存放在低地址处(下标较小处)的顺序靠前,存放在高地址处(下标较大处)的顺序靠后,顺序越靠前越小,越靠后越大
	{
		//从前往后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			src = (char*)src + 1;
			dest = (char*)dest + 1; //从前向后的方式上文已经分析过,此处略
		}
	}
	else
	{
		//从后往前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
			//从后往前,一开始是在最前面的,加上num就变成最后了,随着num减小,两者也在跟着往低地址处跑
		}
	}
	return ret;
}

 

三.memset函数

d0d451206275445289d052eef311fbd4.png

void* ptr:指向要填充的内存块的指针

int value:要设置的值。该值作为 int 传递,但该函数使用此值的无符号字符转换填充内存块

size_t num:要设置为该值的字节数

72e424573af347aa9d73fc06a2222888.jpg

但请注意,对于整型来说,memset函数是以字节为单位进行存放的

int arr[5] = { 0 };
memset(arr, 1, 20);
int i = 0;
for (i = 0; i <= 4; i++)
{
	printf("%d", arr[i]);
}

上述代码最终打印结果为16843009,这是因为memset函数是以字节为单位进行存放,在1个字节当中,10进制的1的16进制表示为:01,如下图所示。一个字节一个字节以01的16进制方式存入内存,而一个整型占四个字节,所以最后输出的结果为16进制下的01 01 01 01,因此才会输出非常大的一个结果

2a31b8901ddc478bbb4fbe7f64f64b4f.jpg

四.memcmp函数

8659089470264b308084db37ea283c6b.png

num即为所需比较的字节数量,且memcmp函数是以字节为单位进行比较的(下文有具体讲解)

该函数就是用来完成内存块比较的。返回值分为正、负、0三种情况;正为大,负为小,0为相等(与strncmp函数返回值含义相同,且非常相似,区别就在于一个可以面向所有类型数据,一个只是面向了字符串与字符型数据)

 

memcmp函数是以字节为单位进行比较的,下面笔者进行讲解:

int arr1[] = { 1,2,3,4,5,6,7,8 };
int arr2[] = { 1,2,3,4,8,8,8,8 };
int ret = memcmp(arr1, arr2, 17);
printf("%d", ret);

在vscode2022编译器下,最终结果为-1。

因为在vscode编译器里是以小端存储(相关文章链接:数据存储),因此小的数字放在高位;而比较17个字节最终的比较是 05和08 这两个以16进制表示的一字节,自然而然就有arr2的数据要比arr1大的结果

1fac8213d5494a3eba2a20dedae04c07.jpg