C 语言内存函数

一、memcpy() 函数的使用和模拟实现

函数原型:

// memcpy() 函数原型
void * memcpy ( void * dest, const void * src, size_t n);

函数功能:
该函数把指针 src 指向的内存区域的前 n 个字节拷贝到指针 dest 指向的内存区域的前 n 个字节。(跟 qsort() 函数类似,两个函数都是预先不知道传入的地址是什么类型,所以使用 void* 类型的指针接收。这样也方便拷贝任何类型的数据。)

注意事项:
(1)确保传入指针是合法的
(2)确保拷贝的字节数在合法范围内
(3)拷贝对应的两块内存空间不能重叠
(4)包含头文件 string.h

使用示例:
(1)正确示例
在这里插入图片描述
(2)错误示例,拷贝对应的两块内存出现重叠(由于 VS 下的 memcpy() 函数不能演示错误示例,这里使用的是作者模拟的。)

在这里插入图片描述
如果拷贝成功,那么打印结果应该是 1 2 3 1 2 3 4 5 9 0

模拟实现:
从两个传入的地址开始逐个字节拷贝,直到拷贝了 n 个字节。

// 模拟实现库函数 memcpy()
void* my_memcpy(void* dest, const void* src, size_t n)
{
    
    
	// 存储目标地址
	void* ret = dest;
	// 开始逐个字节拷贝
	int i;
	for (i = 0; i < n; ++i)
		*((char*)dest + i) = *((char*)src + i);
	// 返回
	return ret;
}

1. 为什么 memcpy() 不能处理内存重叠的情况

如下代码:

int arr1_i[10] = {
    
     1,2,3,4,5,6,7,8,9,0 };
// 把 1 2 3 4 5 拷贝到 4 5 6 7 8
memcpy(arr1_i + 3, arr1_i, sizeof(int) * 5);
// 打印验证
int i;
for (i = 0; i < 10; ++i)
	printf("%d ", arr1_i[i]);
printf("\n");

使用 memcpy() 函数,把 1 2 3 4 5 拷贝到 4 5 6 7 8。而 memcpy() 函数是从前往后依次拷贝的,如下图:
在这里插入图片描述
所以这就是为什么 memcpy() 函数不能处理内存重叠的情况,因为 memcpy() 函数在设计的时候是从前往后拷贝的。

二、memmove() 函数的使用和模拟实现

函数原型:

扫描二维码关注公众号,回复: 17484395 查看本文章
// menmove() 函数原型
void* memmove(char* dest, const char* src, size_t n);

函数功能:
该函数的功能和 memcpy() 函数一模一样,只不过当拷贝对应的两块空间出现重叠的时候,该函数也可以使用。

注意事项:
(1)保证传入的指针是有效的
(2)保证拷贝的字节数在合法范围之内
(3)包含头文件 string.h

使用示例:
拷贝对应的两块内存重叠:
在这里插入图片描述

模拟实现:

// 模拟实现库函数 memmove()
void* my_memmove(void* dest, const void* src, size_t n)
{
    
    
	// 存储目标地址
	void* ret = dest;
	// 判断两个地址的前后顺序
	if (dest < src)
	{
    
    
		// 从前往后拷贝
		int i;
		for (i = 0; i < n; ++i)
			*((char*)dest + i) = *((char*)src + i);
	}
	else
	{
    
    
		// 从后往前拷贝
		while (n--)
			*((char*)dest + n) = *((char*)src + n);
	}

	// 返回目标地址
	return ret;
}

1. 为什么 memmove() 可以处理内存重叠的情况

从上面模拟的 my_memmove() 函数中可以发现,该函数会先判断一下两个指针的大小关系,如果目标地址大,那么就从后往前拷贝;如果目标地址小,那么就从前往后拷贝。如下图:
在这里插入图片描述
这样就无论内存如何重叠,该函数都可以解决。(大家可以自己在纸上简单的演示一下,很容易验证)

三、memset() 函数的使用和模拟实现

函数原型:

// memset() 函数原型
void * memset ( void * ptr, int value, size_t num );

函数功能:
该函数把指针 ptr 所指向的空间的前 num 个字节设置为 value 值。

注意事项:
(1)确保指针 ptr 合法
(2)确保 num 为正,且在合法范围之内
(3)确保 value 的大小为 1 个字节
(4)包含头文件 string.h

使用示例:
在这里插入图片描述
上述代码不能使用 sizeof(s1) 替换 strlen(s1),如果替换,那么字符串 s1 末尾的空字符也会被替换为字符 ‘x’,这样打印该字符串就会越界访问。

模拟实现:

// 模拟实现库函数 memset()
void* my_memset(void* dest, int set, size_t n)
{
    
    
	// 存储目标地址
	void* ret = dest;
	// 设置
	int i;
	for (i = 0; i < n; ++i)
		*((char*)dest + i) = set;
	// 返回
	return ret;
}

四、memcmp() 函数的使用和模拟实现

函数原型:

// memcmp() 函数原型
int memcmp ( const void * ptr1, const void * ptr2, size_t num );

函数功能:
该函数依次比较两个指针指向的空间的前 num 个字节,如果指针 ptr1 指向的内存区域大于指针 ptr2 指向的内存区域,返回一个正整数。如果指针 ptr1 指向的内存区域小于指针 ptr2 指向的内存区域,返回一个负整数。否则,返回 0。

注意事项:
(1)确保传入的两个指针合法
(2)num 需为合法正整数
(3)包含头文件 string.h

使用示例:
在这里插入图片描述

模拟实现:

// 模拟实现库函数 memcmp()
int my_memcmp(const void* p1, const void* p2, size_t n)
{
    
    
	int i = 0;
	while (i < n)
	{
    
    
		// 不相同
		if (*((char*)p1 + i) != *((char*)p2 + i))
			return *((char*)p1 + i) - *((char*)p2 + i);
		else
			++i;
	}
	// 相同
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_70742989/article/details/143320150