C语言内存函数

       本章包含C语言内存函数memcpy、memmove、memset、memcmp函数的讲解,从语法形式到使用示例再到模拟实现几个层次进行讲解。

一、memcpy 函数

1.1 语法形式

#include <string.h> 
void * memcpy ( void * destination, const void * source, size_t num );

功能:

① 对于 strcpy 函数只操作字符串,而 memcpy 函数则是完成内存块的拷贝,即不关注内存中存放的数据是什么(不管是字符串还是整型还是浮点型,都能完成拷贝);
② 函数 memcpy 从 source 的位置开始向后复制 num 个字节的数据到 destination 指向的内存位置;
③ 如果 source 和 destination 有任何的重叠,复制的结果都是未定义的。(所以再处理内存重叠的情况时使用的是 memmove 函数);

参数:

① destination:指针,指向目标空间,拷贝的数据存放在这里;
② source:指针,指向源空间,要拷贝的数据从这里来;
③ num:要拷贝的数据占据的字节数。

返回值:

拷贝完成后,返回目标空间的起始地址

1.2 使用示例

① 整型

#include <stdio.h>
#include <string.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 40); // 将40个字节,即10个整型元素(arr1全部元素)复制到arr2中
    memcpy(arr2, arr1, 12); // 3个元素(1,2,3)复制到arr2中
    memcpy(arr2 + 2 , arr1, 12); // 从arr2第3个元素开始复制
	return 0;
}

② 浮点型

#include <stdio.h>
#include <string.h>

int main()
{
	float arr1[] = { 1.1f,2.3f,3.8f,4.9f,5.6f };
	float arr2[20] = { 0 };
	memcpy(arr2, arr1, (5 * sizeof(float))); // memcpy(arr2, arr1, 20); 
	for (int i = 0; i < 6; i++)
	{
		printf("%.1f ", arr2[i]); // 保留一位小数
	}
	return 0;
}

浮点型 float 类型的数据在计算机中存储是不精确的,这涉及到浮点数在内存中的存储的知识,下一篇文章将会对此讲解。但是如果要输出 arr2 中的元素内容,输出结果就是 arr1 的元素:

 1.3 模拟实现 memcpy 函数

 实现思路与 strcpy 函数类似,见上文《C语言中操作字符串的函数》。改变的条件是循环次数,在 memcpy 函数中传入的参数 num 多大,循环就循环多少次。

#include <stdio.h>
#include <string.h>
#include <assert.h>

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--) // num是多少,循环就遍历多少
	{
		*(char*)dest = *(char*)src;
		/*src = (char*)src + 1;
		dest = (char*)dest + 1;*/
		// 强制类型转化是临时的,因此不能写成(char*)dest++ / (char*)src++;
		++(char*)dest;
		++(char*)src;
	}
	return ret;
}

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

	return 0;
}

如果想使用 my_memcpy 函数将 arr 数组中的1,2,3,4,5复制到元素 2 的后面,即覆盖3,4,5,6,7。意思是将 { 1,2,3,4,5,6,7,8,9,10 } 修改为 { 1,2,1,2,3,4,5,8,9,10 },这就是上文提及到的内容重叠:

int main()
{
	// 调用模拟的memcpy函数:
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr + 2, arr, 20); // 结果为1,2,1,2,1,2,1,8,9,10,没达到预期

	// 调用库函数:
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr + 2, arr, 20); // 达到预期,但实际上memcpy函数在处理重叠的情况时,复制的结果都是未定义的。
	// 内存重叠的情况使用memmove就行

	return 0;
}

 二、memmove 函数

2.1 语法形式

#include <string.h>
void* memmove(void* destination, const void* source, size_t num)

功能:也是完成内存块拷贝的,与 memcpy 函数类似,但区别在于是 memmove 函数多了一项功能,即处理的源内存块和目标内存块是可以重叠的。

 2.2 使用示例

#include <stdio.h>
#include <string.h>
#include <assert.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	//memcpy(arr2, arr1, 20);
	memmove(arr2, arr1, 20); // 与memcpy函数的效果一样
	return 0;
}
#include <stdio.h>
#include <string.h>
#include <assert.h>

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

2.3 模拟实现 memmove 函数

实现思路比较复杂,笔者通过画图的方式讲解:

根据第(2)种情况编写程序:

#include <stdio.h>
#include <assert.h>

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;

    if (num == 0)
        return dest;  // 处理 num=0 的情况

	if (dest < src)
	{
		// 从前向后拷贝
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		// 重叠区域:从后向前拷贝
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}
int main()
{
	// 内存重叠
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    my_memmove(arr + 2, arr, 20);
    //my_memmove(arr, arr + 2, 20);
	return 0;
}

根据第(1)种情况编写:

void* my_memmove(void* dest, const void* src, size_t num)
{
    if (num == 0)
        return dest;  // 处理 num=0 的情况
	assert(dest && src);
	void* ret = dest;

	if (dest >= src && dest < ((char*)src + num))
	{
		// 后 -> 前
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	else
	{
		// 前 -> 后
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

三、memset 函数

3.1 语法形式

#include <string.h>
void * memset ( void * ptr, int value, size_t num );

功能:

memset函数是用来设置内存块的内容的,将内存中指定长度的空间设置为特定的内容。

参数:

① ptr:指针,指向要设置的内存空间,也就是存放了要设置的内存空间的起始地址;
② value:要设置的值,函数将会把value值转换成unsigned char的数据进行设置的。也就是以字节为单位来设置内存块的。
③ num:要设置的内存长度,单位是字节。

返回值:返回的是要设置的内存空间的起始地址。

 3.2 使用示例

#include <stdio.h>
#include <string.h>

int main()
{
	char str[] = "hello world";
	memset(str + 2, 'r', 4);
	printf("%s\n", str);

	return 0;
}

输出 herrrrworld

3.3 注意事项

重点:memset 函数是以字节为单位来设置内存数据的

#include <stdio.h>
#include <string.h>

int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 40);
	return 0;
}

一个元素4个字节,每个字节都被修改为01,没达到将数组全部元素改为1的效果。但如果想将数组所有元素修改为0呢?

#include <stdio.h>
#include <string.h>

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

若要使用 memset 函数将数组元素全部修改为0,是可行的

四、memcmp 函数

4.1 语法形式

#include <string.h>
int memcmp ( const void * ptr1, const void * ptr2, size_t num );

功能:比较指定的两块内存块的内容,比较从ptr1ptr2指针指向的位置开始,向后的num个字节

参数:

① ptr1:指针,指向一块待比较的内存块;
② ptr2:指针,指向另外一块待比较的内存块
③ num:指定的比较长度,单位是字节

 4.2 使用示例

#include <stdio.h>
#include <string.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,4,8 };
	int r = memcmp(arr1, arr2, 16);
	if (r > 0)
		printf(">\n");
	else if (r < 0)
		printf("<\n");
	else
		printf("==\n");
	
	return 0;
}

前16个字节就是前4个整型元素1,2,3,4;输出结果为 ==

#include <stdio.h>
#include <string.h>

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,4,8 };
	int r = memcmp(arr1, arr2, 17);
	if (r > 0)
		printf(">\n");
	else if (r < 0)
		printf("<\n");
	else
		printf("==\n");
	
	return 0;
}

因为 arr1 的第17个字节比 arr2 的要小,因此输出 < 

4.3 模拟实现 memcmp 函数

#include <stdio.h>
#include <assert.h>

int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
	assert(ptr1 && ptr2);
	if (num == 0)
		return 0;
	while (num--)
	{
		if (*(char*)ptr1 != *(char*)ptr2)
		{
			if (*(char*)ptr1 > *(char*)ptr2)
				return 1;
			else
				return -1;
		}
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}
	return 0;
	
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,2,3,4,4 };
	int r = my_memcmp(arr1, arr2, 17);
	if (r > 0)
		printf(">\n");
	else if (r < 0)
		printf("<\n");
	else
		printf("==\n");
	return 0;
}

五、总结

       计算机中存储的最小单位是字节,因此以上内存函数传参的 num 值都是以字节为单位的,是一个一个字节进行处理的。

① memcpy 函数是对内存块进行拷贝,不管内存块存储的数据是什么形式的;

② memmove 函数也是对内存块进行拷贝,与 memcpy 函数的差别在 memmove 函数能实现重叠;

③ memset 函数是用来设置内存块的内容的,单位是字节,因此不能将整型数组中所有元素设置成大于0的数;

④ memcmp 函数是用来比较指定的两块内存块的内容的。

以上就是C语言内存函数的一些内容,有错误的地方欢迎同学们指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值