C语言内存函数

文章目录

一、memcpy函数

🍏🍏mem其实就是memory单词的缩写,就是内存 的意思。内存函数顾名思义就是操作内存的函数。通过访问地址的方式对内存中的数据进行操作。内存函数可以应用于任何类型的对象

1.memcpy函数的使用

memcpy是针对内存块进行拷贝的,memcpy函数的使用需要包含头文件string.h。函数的声明如下:

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

🍆函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
🍆这个函数在遇到'\0'的时候并不会停下来。
🍆如果source和destination有任何的重叠,那复制的结果可能是未定义的。

memcpy函数的使用例子,看面一段代码:

c 复制代码
#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	memcpy(arr2, arr1, 20);//将arr1里的前5个整型拷贝到arr2所指向的内存空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

程序运行结果:

2.memcpy函数的模拟实现

在memcpy函数拷贝结束以后,会返回目标空间的起始地址。那现在我们模拟实现一个memcpy函数:

c 复制代码
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	int i = 0;
	while(num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 0 };
	my_memcpy(arr2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

程序运行结果:
如果source和destination有任何的重叠,那复制的结果可能是未定义的,这句话怎么理解呢?看下面的一个例子:

c 复制代码
#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	int i = 0;
	while (num--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr1 + 2, arr1, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

上面是用自定义的函数my_memcpy进行拷贝,将数组arr1中的前五个整型拷贝放到arr1+2(从arr1数组的第三个元素空间处开始存放5个整型)所指向的空间里,这里就是source和destination有重叠的情况。我们想要的拷贝结果其实是:

1 2 1 2 3 4 5 8 9 10

但实际的运行结果为:
仔细想一下,就能知道当把1、2先拷贝放到arr+2和arr+3的地址处,其实原来数组的3和4就被1和2覆盖了,所以之后再来拷贝arr1+2和arr1+3地址处的元素到arr1+4和arr1+5所指向的空间其实也是拷贝了1和 2。后面的拷贝也是一样的原因,所以出现了上面的结果。但使用内存函数memcpy进行上面source和destination有重叠的拷贝,其实是可以打印出我们想要的效果的,因为memcpy函数在当前的VS编译器下的复制功能是比较完善的。那为什么说有重叠种情况下的复制结果是未定义的呢?其实memcpy函数只需要用来拷贝那些source和destination不重叠的情况即可,而有重叠情况的拷贝可以用别的内存函数来完成。

二、memmove函数

1.memmove函数的使用

memmove函数的声明如下:

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

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

c 复制代码
#include<stdio.h>
#include<string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr + 2, arr, 5*sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

程序运行结果:

2.memmove函数的模拟实现

注意:memmove函数拷贝完成以后,会返回目标空间的起始地址。我们可以模拟实现一个和memmove函数一样功能的函数,先分析一下要怎么拷贝:其实有重叠的拷贝有三种情况,假设现在有一个数组:

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

🧀由于数组在内存中是连续存放的,并且数组元素随着下标的增长,地址由低到高。

①如果要拷贝的目标空间的地址dest小于源空间的地址src ,比如现在目标空间的地址为arr,源空间的地址为arr+2,要将这个数组中的3,4,5,6,7拷贝放到1,2,3,4,5的位置,那么可以从前往后拷贝。什么意思呢?看下面的示意图:

当目标空间的地址dest小于源空间的地址src时,用从前向后的方式进行拷贝,可以避免源空间里要拷贝的数据被覆盖。

②如果目标空间的地址dest大于源空间的地址src 时,还是上面假设的数组,如果现在目标空间的地址为arr+2,源空间的地址为arr,要将这个数组中的1,2,3,4,5拷贝放到3,4,5,6,7的位置,那么可以从后往前拷贝
在dest>src这种情况下,用从后往前的方式进行拷贝,也是为了避免源空间里要拷贝的数据被覆盖。

③上面的两种情况,不管是dest<src还是dest>src,都是有重叠的情况,那第三种情况就是没有重叠的情况,比如:
没有重叠的情况,无论是从前往后拷贝还是从后往前拷贝都是没有问题的,因为不会出现源空间里要拷贝的数据被覆盖的情况。那我们统一 一下,如果是目标空间的地址dest小于源空间的地址src的情况,我们就采用从前向后的方式拷贝;如果是目标空间的地址dest大于源空间的地址src的情况,我们就采用从后往前的方式拷贝;这样所有可能的情况就都考虑到了。代码的实现方式如下:

c 复制代码
#include<stdio.h>
#include<assert.h>
void* my_memmove(void* dest, const void* src, size_t num)
{
	void* ret = dest;
	assert(dest && src);
	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);
		}
	}
	return ret;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr, arr+2, 5*sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

程序的运行结果:(从前向后拷贝)
上面是模拟在有重叠的情况下进行拷贝,最重要的是有会分析实现有重叠拷贝的思路,这种思路特别在后面学习数据结构的时候也可能会用到。

三、memset函数

memset函数是用来设置内存的,将内存中的值以字节为单位设置成想要的内容。函数声明如下:

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

🥑将ptr指向的内存块的前num个字节设置为指定的值value。 (value值作为int类型传递,但函数使用该值的unsigned char转换来填充内存块)

举一个例子,假如现在有一个字符数组arr,里面存放着"hello world",现在我们想把这个数组里的前5个字符设置为字符'x',就可以使用memset函数:

c 复制代码
#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 5);//以字节为单位来设置的
	printf("%s\n", arr);
	return 0;
}

程序运行的结果:
当然memset函数也可以设置其他类型的数组,不一定是char类型的数组。但是要切记:memset设置指定的值是以(1个)字节为单位来设置的 ,而不能以(数组)元素为单位来设置值。上面char类型的数组能以元素为单位来设置值,是因为char类型数组的每个元素就占一个字节。要是换成整型数组就不能一个元素一个元素的来设置值了。

四、memcmp函数

memcmp函数的功能是:将ptr1所指向的内存块的前num字节与ptr2所指向的前num字节进行比较,如果它们都匹配(相同)则返回0,如果不匹配则返回不同于0的值,表示哪个值更大。函数的声明如下:

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

🍞比较从ptr1和ptr2指针指向的位置开始,向后的num个字节
🍞返回值如下:
memcmp函数在内存进行比较时,其实也是一对字节一对字节的进行比较,如果第一次比较到不匹配的字节,那返回值就是看ptr1和ptr2所指向的那个字节的值谁大?如果ptr1所指向的字节的值比ptr2所指向的大,则返回大于0的值;如果ptr1所指向的字节的值比ptr2所指向的小,则返回<0的值;如果比较完了内存块中的num个字节的值,ptr1和ptr2所指向的字节的值都一样,则返回0。

c 复制代码
#include<stdio.h>
#include<string.h>
int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 1,2,3,4,5,6,7,8,8,8 };
	char arr3[] = "abcdefghijk";
	char arr4[] = "abcdefgxyz";

	int ret1 = memcmp(arr1, arr2, 36);
	int ret2 = memcmp(arr1, arr2, 16);
	int ret3 = memcmp(arr3, arr4, 8);
	printf("%d\n", ret1);
	printf("%d\n", ret2);
	printf("%d\n", ret3);
	return 0;
}

程序运行结果:

相关推荐
测试界的酸菜鱼4 分钟前
C# NUnit 框架:高效使用指南
开发语言·c#·log4j
GDAL4 分钟前
lua入门教程 :模块和包
开发语言·junit·lua
李老头探索5 分钟前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
CSXB997 分钟前
三十四、Python基础语法(文件操作-上)
开发语言·python·功能测试·测试工具
web Rookie26 分钟前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
很楠不爱37 分钟前
Qt——窗口
开发语言·qt
yi碗汤园38 分钟前
【一文了解】C#基础-集合
开发语言·前端·unity·c#
skaiuijing1 小时前
Sparrow系列拓展篇:对调度层进行抽象并引入IPC机制信号量
c语言·算法·操作系统·调度算法·操作系统内核
Cachel wood1 小时前
Github配置ssh key原理及操作步骤
运维·开发语言·数据库·windows·postgresql·ssh·github
დ旧言~1 小时前
【网络】应用层——HTTP协议
开发语言·网络·网络协议·http·php