• C语言之内存函数


    在此之间,我们学过许多的字符函数,例如strcpy,strstr,strcmp等等,这里我们以strcpy函数进行举例:

    //实现将arr1拷贝到arr2中
    int main()
    {
    	int arr1[5] = { 1,2,3,4,5 };
    	int arr2[5] = { 0 };
    	strcpy(arr2, arr1);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    程序并没有正确运行,编译器报错:
    在这里插入图片描述

    错误分析如下:
    在这里插入图片描述上述代码是无法实现将arr1拷贝到arr2中,由此可见,像strcmp,strcpy,strstr这种字符串函数,它虽然能够实现字符串拷贝,比较等功能,但是由于它们的操作对象是字符串,因此对于整形数组,浮点型数组等并不适用。

    那么对于整形数组,浮点型数组等其他的数据,我们该如何进行操作呢?

    c语言给出了另一类函数-------内存函数(memcpy/memmove/memcmp)

    memcpy:

    //void*-----通用类型指针:可以接受各种类型的参数
    void*memcpy(void*destination,const void*source,size_num)//size_num的单位是字节
    
    • 1
    • 2

    还是选用上述实例:

    #include
    #include
    //实现将arr1拷贝到arr2中
    int main()
    {
    	int arr1[5] = { 1,2,3,4,5 };
    	int arr2[5] = { 0 };
    	memcpy(arr2, arr1,sizeof(arr1));
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    此时打开监视窗口,我们不难发现,数组之间完美的实现了拷贝。
    在这里插入图片描述

    结构体类型实现拷贝:

    举例:

    struct person 
    {
    	int age;
    	char name[20];
    	char phone[13];
    };
    int main()
    {
    	struct person arr3[] = { {20,"张三","20031319"},{19,"lisa","193684"} };
    	struct person arr4[3] = {0};
    	memcpy(arr4, arr3, sizeof(arr3));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    此时打开监视窗口,我们不难发现,结构体之间完美的实现了拷贝。

    在这里插入图片描述

    模拟实现memcpy:

    以整形进行举例:

    #include
    #include
    void* my_memcpy(void* dest, const void* scr,size_t num)
    {
    	void* ret = dest;
    	//断言:为避免是空指针
    	assert(dest != NULL);
    	assert(scr != NULL);
    	while (num--)//num为要拷贝的长度
    	{
    	//不能直接解引用操作的原因:void*类型不能直接进行++/--操作
    		*(char*)dest = *(char*)scr;//先强制类型转换在解引用
    		dest=(char*)dest+1;
    		scr=(char*)scr+1;
    	}
    	return ret;//不能直接返回dest,因为此时的dest并不是首地址
    }
    int main()
    {
    	int arr1[] = { 1,2,3,4,5 };
    	int arr2[5] = { 0 };
    	my_memcpy(arr2, arr1,sizeof(arr1));
    	for (int i = 0; i < 5; i++)
    	{
    		printf("%d ", arr2[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    1 2 3 4 5
    
    • 1

    上述实例,我们是实现的功能是将一个数组中的元素拷贝到另一个数组中,如果现在我们实现将数组中的前几个元素拷贝到后面的几个呢?

    举例:

    //实现将1 2 3 4 5拷贝到3 4 5 6 7的位置上
    #include
    #include
    void* my_memcpy(void* dest, const void* scr,size_t num)
    {
    	void* ret = dest;
    	assert(dest != NULL);
    	assert(scr != NULL);
    	while (num--)
    	{
    		*(char*)dest = *(char*)scr;
    		dest=(char*)dest+1;
    		scr=(char*)scr+1;
    	}
    	return ret;
    }
    int main()
    {
    	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memcpy(arr1+2, arr1,20);
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    预期输出结果为:

    1 2 1 2 3 4 5 8 9 10
    
    • 1

    实际输出结果:

    1 2 1 2 1 2 1 8 9 10
    
    • 1

    预期和实际不相符分析如下:
    在这里插入图片描述
    由此可得出:我们所编写的my_memcpy函数并不能实现在同一个数组中的拷贝,会出现数的覆盖现象。

    那库函数memcpy能否实现呢?

    #include
    int main()
    {
    	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	memcpy(arr1+2, arr1,20);
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出结果如下:

    1 2 1 2 3 4 5 8 9 10
    
    • 1

    看到这里,相信很多人都会产生疑惑,为什么库函数memcpy可以实现,而我们编写的my_memcpy不能实现呢?难道是我们写错了?

    事实并非如此,memcpy虽然也实现了这样的功能,但C语言标准规定:memcpy:用来处理不重叠的内存拷贝。memmove:处理重叠的内存拷贝

    我们所编写的my_memcpy是因为严格按照C语言的标准所编写,而在VS编译器上memcpy超额完成了任务,相当于抢了memmove的饭碗。

    下面我们就来学习memmove函数!

    memmove:

    依然是上述实例:

    #include
    int main()
    {
    	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	memmove(arr1+2, arr1,20);
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    输出结果如下:

    1 2 1 2 3 4 5 8 9 10
    
    • 1

    现在得出的结果正是我们预期的效果!

    模拟实现memmove:

    目的地的地址低于源头地址:

    正序进行拷贝:

    #include
    #include
    my_memmove(void* dest, const void* scr, size_t count)
    {
    	assert(dest != NULL);
    	assert(scr != NULL);
    	void* ret = dest;
    	while (count--)
    	//直接按照源头一个个进行打印,不存在数的覆盖
    	{
    		*(char*) dest= *(char*) scr;
    		dest = (char*)dest + 1;
    		scr = (char*)scr + 1;
    	}
    	return ret;
    }
    int main()
    {
    	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memmove(arr1, arr1+2,20);
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    这种比较简单,这里就不赘述分析过程了
    在这里插入图片描述输出结果为:

    3 4 5 6 7 6 7 8 9 10
    
    • 1

    目的地址高于源头地址:

    倒序进行拷贝:

    #include
    #include
    my_memmove(void* dest, const void* scr, size_t count)
    {
    	assert(dest != NULL);
    	assert(scr != NULL);
    	void* ret = dest;
    	while (count--)
    	{
    		*((char*)dest + count) = *((char*)scr + count);
    	}
    	return ret;
    }
    int main()
    {
    	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memmove(arr1+2, arr1,20);
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这里插入图片描述输出结果为:

    1 2 1 2 3 4 5  8 9 10
    
    • 1

    对于该行代码的实现过程,我们分析如下:

    *((char*)dest + count) = *((char*)scr + count);
    
    • 1

    在这里插入图片描述
    对于上述两种情况,我们可用一个程序进行表示:

    #include
    #include
    my_memmove(void* dest, const void* scr, size_t count)
    {
    	assert(dest != NULL);
    	assert(scr != NULL);
    	void* ret = dest;
    	if(dest<scr)//正序进行拷贝
    	while (count--)
    	{
    		*(char*)dest = *(char*)scr;
    		dest = (char*)dest + 1;
    		scr = (char*)scr + 1;
    	}
    	else//倒序进行拷贝
    	{
    	while(count--)
    	{
    		*((char*)dest + count) = *((char*)scr + count);
    	}
    	}
    	return ret;
    }
    int main()
    {
    	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
    	my_memmove(arr1+2, arr1,20);
    	for (int i = 0; i < 10; i++)
    	{
    		printf("%d ", arr1[i]);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    输出结果如下:

    1 2 1 2 3 4 5 8 9 10 
    
    • 1

    memcmp:

    将两个存储区的前n个字节进行比较。

    举例:

    #include
    int main()
    {
    	int arr[] = { 1,2,3,4,5 };
    	int arr2[] = { 1,2,5,4,3 };
    	int ret=memcmp(arr, arr2, 9);//arr>arr2,返回大于零的数字,arr
    	//注:VS编译器返回-1/0/1,虽然不严谨,但是并不违背C语言的标准规定
    	printf("%d\n", ret);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    输出结果如下:

    -1
    
    • 1

    分析如下:
    在这里插入图片描述

    memset:内存设置函数

    举例:

    字符型:

    #include
    int main()
    {
    	char arr[10] = "";
    	memset(arr, '#', 10);//10代表更改10个字节
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述整形:

    #include
    int main()
    {
    	int  arr[10] = {0};
    	memset(arr, 1, 10);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出如下:
    在这里插入图片描述让不少人产生疑惑的是:为什么此时并没有实现将数组中的10个元素都修改为1呢?

    原因是:该函数的操作单位是字节,而数组是一个整形数组,其中的元素都为整形,每个元素为4个字节。

    具体分析如下:
    在这里插入图片描述因此,在使用该函数的时候,一定要注意类型。

  • 相关阅读:
    MYSQL函数
    java-net-php-python-jsp校园招聘管理系统计算机毕业设计程序
    集成学习
    【C#项目实战】控制台游戏 勇士斗恶龙(2)——游戏场景的设置以及玩家战斗逻辑
    Java 获取一个时间范围内的最后一天或者第一天
    Spring MVC 四:Context层级
    【Java】包装类
    软件测试面试题:缺陷记录应包含的内容?
    【LeetCode-简单题】202. 快乐数
    java类加载过程
  • 原文地址:https://blog.csdn.net/m0_64365419/article/details/126225864