C语言笔记第10篇:内存函数

上一篇的字符串函数只是针对字符串的函数,而内存函数是针对内存块的,不在乎内存中存储的数据!这就是字符串函数和内存函数的区别。

准备好爆米花,正片开始

1、memcpy的使用和模拟实现

memcpy库函数的功能:任意类型数组的拷贝

memcpy的函数声明:

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

destination是目标空间,source是源,size_t num是拷贝字节的个数。

为什么还有输入拷贝字节个数呢?

因为memcpy可以拷贝任意类型的数组,可以是字符,可以是int,也可以是struct自定义类型的,但是前提是要输入要拷贝的字节个数,因为传过去的地址被void类型的指针接收,所以不能得知元素大小。

memcpy函数的调用:

#include <stdlib.h>
#include <stdio.h>
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	memcpy(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

memcpy函数的模拟实现:

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* dest, const void* source, size_t num)
{
	assert(dest && source);
    void* ret = dest;
	while (num--)
	{
		*(char*)dest = *(char*)source;
		dest = (char*)dest + 1;
		source = (char*)source + 1;
	}
    return ret;
}
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

但是这个函数有一个缺点,就是不能重叠内存拷贝,什么意思呢?就是不能在同一个数组中拷贝,会导致打印信息不正确,例如:

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

我想将arr+2也就是第3个元素的位置开始,拷贝1-5,我们想象的答案是1 2 1 2 3 4 5 8 9 10,但是实际上的答案是1 2 1 2 1 2 1 8 9 10,因为是从前往后开始拷贝,拷贝信息和拷贝的位置重叠了,导致拷贝时更改了拷贝信息,打印出的结果有所差异。

那怎么办?其实还有memmove函数,他和memcpy的拷贝一样,任意类型都可以拷贝,不同的是memmove可以处理重叠内存拷贝。

2、memmove的使用和模拟实现

memmove库函数功能:拷贝任意类型的数组,也可以处理重叠内存拷贝问题

memmove函数的声明:

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

可以看到memmove和memcpy的返回类型和参数一模一样,唯一不同的只是memmove函数的实现细节

memmove函数的调用:

#include <stdlib.h>
#include <stdio.h>
int main()
{
	int arr1[10] = { 0 };
	int arr2[] = { 1,2,3,4,5,6,7,8,9,10 };
	memmove(arr1, arr2, 20);//向拷贝20个字节也就是5个int类型大小的arr2元素到arr1数组中
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

memmove究竟是如何处理拷贝重叠的的呢?请继续往下看

memmove函数的模拟实现:

#include <stdio.h>
#include <assert.h>
void* my_memmove(void* dest, const void* source, size_t num)
{
	assert(dest && source);
    void* ret = dest;
	if (dest < source)//如果拷贝的地址小于拷贝信息的地址就可以从前向后拷贝
	{
		while (num--)
		{
			*(char*)dest = *(char*)source;//从前向后拷贝
			dest = (char*)dest + 1;
			source = (char*)source + 1;
		}
	}
	else//如果拷贝的地址大于或等于拷贝信息的地址就从后向前拷贝
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)source + num);//从后向前拷贝
		}
	}
    return ret;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr + 2, arr, 20);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

memmove模拟实现逻辑:

我们可以使用地址于地址之间的关系运算,简单概述就是两个地址之间比较大小。因为该函数是排序数组的,数组又是连续存放的,所以可以比较两个地址。如果目标空间地址比源地址大,就从后往前拷贝。如果目标空间地址比源地址小,就可以从前往后拷贝。

3、memset的使用和模拟实现

memory - 记忆(内存),set - 设置。memset就是内存设置的意思。

memset库函数功能:将参数ptr的前num个字节设置成指定的value值。

memset的函数声明:

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

比如我有一个字符数组,字符串是 " hello world " ,我想把它改成 " hello xxxxx",那我们就可以使用memset函数。

memset函数的调用:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	char str[] = "hello world";
	memset(str + 6, 'x', 5);
    //参数1:字符数组下标6的位置 参数2:需替换的的源值 参数3:字节为单位,向后拷贝的字节大小
	printf("%s\n", str);//打印 "hello xxxxx"
	return 0;
}

打印结果确实是 " hello xxxxx ",但是我们也可以用它来改变整型数组:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9,10};
	memset(arr, 1, 20);
	int sz = sizeof(arr)/sizeof(arr[0]);
    int i = 0;
    for(i=0;i<sz;i++)
    {
        printf("%d ",arr[i]);
    }
	return 0;
}

我们想象的是改变前20个字节也就是前5个整型元素,打印为:1,1,1,1,1,6,7,8,9,10,但实际上却却是以每个字节更改为01,并不是我们想象的改为五个1,如下图:

所以你想让它的每个字节都是1是可以做的到的,但是你想让它每个整型都是1这个是做不到的,memset本身就是以字节为单位进行设置的。前面的memcpy和memmove虽然也是以字节为单位来拷贝的,但是它们两边都是在变化着拷贝的,所以能够拷贝正确答案。而这个需拷贝的源始终都是一个值,这个值是不会变化的,每次拷贝一个字节都从这里面的一个字节拷贝到另一个空间。

memset的模拟实现:

#include <stdio.h>
#include <stdlib.h>
void my_memset(void* str, int value, size_t num)
{
	assert(str != NULL);
	void* ret = str;
	while (num--)
	{
		*(char*)str = (char)value;
		str = (char*)str + 1;
	}
	return ret;
}
int main()
{
	char str[] = "hello world";
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	my_memset(str+6, 'x', 5);
	my_memset(arr, 1, 20);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
	printf("%s\n", str);
	return 0;
}

4、memcmp的使用和模拟实现

memcmp库函数的功能:和strncmp的功能一样,strncmp是比较两个字符串的,memcmp是比较两个数组内存的

memcmp函数的声明:

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

memcmp返回值:如果ptr1比ptr2大就返回大于0的数字,如果ptr1比ptr2小就返回小于0的数字,如果相等就返回0

memcmp函数的调用:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int arr1[] = { 1,4,3,4,5 };
	int arr2[] = { 1,3,5,7,9 };
	int ret = memcmp(arr1, arr2, 5);
	printf("%d\n", ret);
	return 0;
}

memcmp函数的模拟实现:

#include <stdio.h>
#include <stdlib.h>
int my_memcmp(void* ptr1, void* ptr2, size_t num)
{
	assert(ptr1 && ptr2);
	while (num--)
	{
		if (*(char*)ptr1 != *(char*)ptr2)
		{
			return *(char*)ptr1 - *(char*)ptr2;
		}
		ptr1 = (char*)ptr1 + 1;
		ptr2 = (char*)ptr2 + 1;
	}
	return 0;
}
int main()
{
	int arr1[] = { 1,3,3,4,5 };
	int arr2[] = { 1,4,5,7,9 };
	int ret = my_memcmp(arr1, arr2, 5);
	printf("%d\n", ret);
	return 0;
}

                                                              end

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-06-06 05:10:05       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-06 05:10:05       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-06 05:10:05       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-06 05:10:05       18 阅读

热门阅读

  1. sklearn基础教程

    2024-06-06 05:10:05       10 阅读
  2. 2024.6.05总结1102

    2024-06-06 05:10:05       10 阅读
  3. 文档智能开源软件

    2024-06-06 05:10:05       7 阅读
  4. 常用设计模式

    2024-06-06 05:10:05       7 阅读
  5. 层出不穷的大模型产品,你怎么选?【模板】

    2024-06-06 05:10:05       12 阅读