首页 > 系统相关 >C语言进阶【3】---C语言内存函数(你确定你很了解内存函数吗?)

C语言进阶【3】---C语言内存函数(你确定你很了解内存函数吗?)

时间:2024-09-14 18:50:07浏览次数:11  
标签:arr 函数 int void C语言 内存 sizeof include

本章概述

本章函数概述

我们在本章的博客中讲的内容是有关内存的操作,我们 直接通过内存块对数据进行操作。 因为我们是直接对内存块操作,所以可以对任意类型数据进行操作(我们没必要管它是什么类型的数据,我们只对这个内存块操作)。接下来所讲的这些内存函数的头文件为:<string.h>这些内存函数是对每个字节进行操作

memcpy使用和模拟

  • memcpy的功能:和strcpy函数的功能类似strcpy函数是对元素进行拷贝,而memcpy函数是对内存块进行拷贝所以内存块里面的数据类型没意义。比如,前面咱们讲过了strcpy和strncpy函数,它们只能对字符串进行拷贝。但是,当我们想进行两个整形数据的拷贝时候,就不能用strcpy和strncpy函数了,就只能用memcpy函数了。memcpy函数能拷贝的数据类型比strcpy和strncpy函数拷贝的数据库类型要广泛些
  • memcpy函数的结构:
//	void * memcpy ( void * destination, const void * source, size_t num );
//	因为我们进行内存块的拷贝,所以里面是什么类型的数据咱们是不知道的,所以当你传给我地址的时候我只能用void*指针来接收。
//	因为咱们是进行内存块的拷贝,所以要知道具体拷贝多少个字节的空间,size_t num 就是要拷贝的字节数。
//	返回的是传给des的首元素地址,因为是什么类型咱不知道,所以返回void*指针数据类型。
//	des和source的地址是可以被我们指定的。

由于size_t num是表示要拷贝的字节数,咱们是第一次见到它的这种意义,所以我给大家放个它的截图,给大家看一下。如图:在这里插入图片描述

  • memcpy函数的使用:----->对字符串的拷贝
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcdefghu"; 	
	char arr2[30] = {0};
	void* p = memcpy(arr2, arr1, 4 * sizeof(char));
	printf("%s\n", (char*)p);
	return 0;
}

结果运行图:在这里插入图片描述
------->对整形数据进行拷贝

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr3[] = { 1,2,3,4,5,6 };
	int arr4[10] = { 0 };
	int sz = sizeof(arr4) / sizeof(arr4[0]);
	  memcpy(arr4, arr3, 4 * sizeof(int));
	  printf("arr4: ");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr4[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述

  • memcpy函数的模拟:因为这个函数对内存进行操作,所以它能够拷贝多种数据类型模拟的核心思想我们要对单个内存进行操作,也就是把各种指针类型全部转换为char *(因为char *能够访问单个字节),进行代码展示:
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
#include <assert.h>

void* my_memcpy(void* des, const void* str, size_t num)
{
	void* p = des;
	assert(des && str);
	while (num--)
	{
		*(char*)des = *(char*)str;
		des = (char*)des + 1;
		str = (char*)str + 1;

	}
	return p;
}
int main()
{
	int arr3[] = { 1,2,3,4,5,6 };
	int arr4[10] = { 0 };
	int sz = sizeof(arr4) / sizeof(arr4[0]);
	my_memcpy(arr4, arr3, 4 * sizeof(int));
	printf("arr4: ");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr4[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述

  • 拓展使用:------->当我们用memcpy函数进行自己给自己拷贝数据(重叠数据拷贝)
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(arr3) / sizeof(arr3[0]);
	  memcpy(arr3+2, arr3, 4 * sizeof(int));
	  printf("arr4: ");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述
--------->当我们用my_memcpy函数进行自己给自己拷贝数据(重叠数据拷贝)

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
#include <assert.h>

void* my_memcpy(void* des, const void* str, size_t num)
{
	void* p = des;
	assert(des && str);
	while (num--)
	{
		*(char*)des = *(char*)str;
		des = (char*)des + 1;
		str = (char*)str + 1;

	}
	return p;
}
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(arr3) / sizeof(arr3[0]);
	  my_memcpy(arr3+2, arr3, 4 * sizeof(int));
	  printf("arr4: ");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述
当自己给自己进行重复数据拷贝的时,memcpy函数能达到我们的预想值。但是,我们自己模拟的my_memcpy函数就无法达到预想值,难道是我们的模拟函数有问题?答案是否。我们来举个生活中的例子,100分的试卷。考到60分就欧克了,但是你却偏偏考到80多分,说明有能考到60分的能力,但是你还能超越自己。memcpy函数就是这个道理 ,它有拷贝数据的能力(不重叠数据),但是,它还能超越自己(拷贝重叠数据)。而我们自己模拟的my_memcpy函数完成了它的基本功能。memcpy函数不太常用于拷贝重叠数据我们常用memmove函数拷贝重叠数据。

memmove使用和模拟

  • memmove函数的功能它的功能比memcpy函数要广泛些,它既能拷贝不重叠的数据,有能拷贝重叠的数据
  • memmove函数的结构:
//	void * memmove ( void * destination, const void * source, size_t num );

memmove函数的结构与memcpy函数的结构相同,但是它的功能比memcpy函数要广泛些。

  • memmove函数的使用:进行代码展示---------->数据不重叠
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9 };
	int arr4[10] = { 0 };
	int sz = sizeof(arr3) / sizeof(arr3[0]);
	memmove(arr4, arr3, 4 * sizeof(int));
	printf("arr4: ");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr4[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述
进行代码展示---------->数据重叠

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr3[] = { 1,2,3,4,5,6,7,8,9 };
	int sz = sizeof(arr3) / sizeof(arr3[0]);
	memmove(arr3+2, arr3, 4 * sizeof(int));
	printf("arr4: ");
	for (int i = 0; i < sz; i++)
	{
		printf("%d ", arr3[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述

  • memmove函数的模拟:进行代码展示:
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memmove(void* des, void* str, size_t num)
{
	void* p = des;
	assert(des&&str);
	const void* s1 = des;
	const void* s2 = str;
	if (des < str)
	{
		while (num--)
		{
			*(char*)des = *(char*)str;
			des = (char*)des + 1;
			str = (char*)str + 1;
		}
	}
	else
	{
		while (num--)
		{
			des = s1;
			str = s2;
			str = (char*)str + num;
			des = (char*)des + num;
			*(char*)des = *(char*)str;
		}
	}
	return p;
}
int main()
{
	int arr[] = {1,2,3,4,5,6,7,8,9};
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	my_memmove(arr + 2, arr, 4 * sizeof(int));
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

  • memmove函数的模拟讲解:我们对于这个函数的模拟实现要分为两种情况第一种情况就是:des<str时的的拷贝情况第二种情况就是:des>=str的拷贝情况。当为第一种情况的时候,要向后拷贝。进行如图所示:在这里插入图片描述
    是第二种情况的时候,要向前拷贝,如图所示:在这里插入图片描述
    对于向前拷贝的时候,等于的情况是满足的(这是最好的状态),哪怕它越界访问也是满足的(只要str<des

memset函数的使用

  • memset函数的功能具有设置数据的功能能把整型数组里面的元素全部设为0,也能改变字符串中的某些字符元素。
  • memset函数的结构:
//	 void * memset ( void * ptr, int value, size_t num );
// 因为被改变的数据类型我们不知道(既能改变整型,也能改变字符型……),所以用void*指针接收
//	int value为我们想要改变的数据。
// size_t num为我们要改变的内存字节数目。
//	我们还可以指定改变的起始位置。
  • memset函数的使用:进行代码展示--------->改变整型数组
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++) //	我们以前改变整形数组里面元素的方法
	{
		arr[i] = 0;
		printf("%d ", arr[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	memset(arr, 0, sz * sizeof(int)); //	使用memset函数直接全部置0
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述
进行代码展示--------->改变整形数组的起始位置

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	memset(arr+2, 0, 5 * sizeof(int)); //	改变起始位置,从3开始后面置0,
									//要注意要改变的元素个数不能超过数组的范围,否则报错
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述
进行代码展示--------->改变字符串

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "hello world";
	memset(arr, 'x', 6 * sizeof(char));
	printf("%s\n", arr);
	return 0;
}

结果运行图:在这里插入图片描述
我们还可以改变字符串要改变的起始位置,大家可以自行尝试一下。

  • memset函数使用注意事项对于改变整形数组时,我们使用memset函数主要是置0,不能置其它的数字,否则得不到想要的结果。也就是说,memset函数对于改变整形数组里面的元素,只能置0。进行代码展示:
//	假如,我们给整形数组全部置1
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	memset(arr, 1, sz * sizeof(int)); 
	for (i = 0; i < sz; i++)
	{
		printf("%d \n", arr[i]);
	}
	return 0;
}

结果运行图:在这里插入图片描述
是不是与我们的预期大相径庭,按理来讲应该全是1,结果全是很大的整数,这是为什么呢? 前面,咱们讲过了这些函数都是对单个字节操作的,由于int 是四个字节,所以每个字节都会改为1。到最后对Int进行解读的时候还是按照4个字节,就会导致解读的数据与期望值不同。结果调试图:如图在这里插入图片描述
从调试图中可以看出来,每个字节都被改为1。所以,我们一般用memset函数置0.

memcmp函数的使用

  • memcmp函数的功能:它和strcmp函数的功能类似,都是用来比较大小的strcmp函数是用来比较字符元素的大小的,而且只能比较字符元素的大小。而memcmp函数是进行内存单个字节比较,它会每个每个字节进行比较,它不管里面存的什么数据。
  • memcmp函数的结构:
//	 int memcmp ( const void * ptr1, const void * ptr2, size_t num );
//	它和strcmp函数一样,参数放的位置决定比较的前后。
//	ptr1大于ptr2返回大于0的值,ptr1等于ptr2返回0的,ptr1小于ptr2返回小于0的值。
//	size_t num 为要比较的字节数。
  • memcmp函数的使用:进行代码展示--------->整形比较
#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,5,6 };
	int arr2[] = { 1,2,3,4 };
	printf("%d\n", memcmp(arr1, arr2, 3 * sizeof(int)));
	printf("%d\n", memcmp(arr1, arr2, 13));
	return 0;
}

结果调试图:在这里插入图片描述
这是arr1的内存数据存储的形式,arr2也是一样的。前面,咱们已经讲过了,memcmp函数是每个每个字节进行比较,比如,printf("%d\n", memcmp(arr1, arr2, 13));这行代码,我们比较了13个字节的空间,arr1和arr2前面12个字节都一样,第13个字节的内容不一样——05大于04
如图所示的逻辑图:在这里插入图片描述
在这里插入图片描述
由于arr1第13个字节的内容大于arr2的内容,所以结果就会输出大于0的值,结果运行图:在这里插入图片描述
-------->字符型比较

#define  _CRT_SECURE_NO_WARNINGS	1
#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcf";
	printf("%d\n", memcmp(arr1, arr2, 4 * sizeof(char)));
	return 0;
}

姐果运行图:在这里插入图片描述

彩蛋时刻!!!

https://www.bilibili.com/video/BV1pT421r7kJ/?spm_id_from=333.1007.tianma.6-3-21.click&vd_source=7d0d6d43e38f977d947fffdf92c1dfad在这里插入图片描述
每章一句永远相信美好的事情即将发生。感谢你能看到这里,点赞+关注+收藏+转发是对我最大的鼓励,咱们下期见!!!

标签:arr,函数,int,void,C语言,内存,sizeof,include
From: https://blog.csdn.net/2401_83009236/article/details/142181553

相关文章

  • 记一次 公司.NET项目部署在Linux环境压测时 内存暴涨分析
    一:背景讲故事公司部署在某碟上的项目在9月份压测50并发时,发现某个容器线程、内存非正常的上涨,导致功能出现了异常无法使用。根据所学,自己分析了下线程和内存问题,分析时可以使用lldb或者windbg,但是个人比较倾向于界面化的windbg,所以最终使用windbg开干。二:WinDbg分析到底是......
  • c++类和对象(3):默认成员函数(下)
    1.拷贝构造函数如果⼀个构造函数的第⼀个参数是自身类类型的引用,且任何额外的参数都有默认值,则此构造函数也叫做拷贝构造函数,也就是说拷贝构造是⼀个特殊的构造函数。c++规定:类类型的传值传参必须用拷贝构造1.1拷贝构造函数的特点1.拷贝构造函数是构造函数的⼀个重载......
  • 服务器释放内存对网站有影响
    服务器释放内存是维持系统稳定性和性能的常规操作。然而,如果处理不当,这一过程可能会对网站运营产生一定的影响。以下是服务器释放内存可能对网站造成的影响:应用程序重启:如果服务器释放内存导致运行中的应用程序被重启,那么在此过程中,网站可能会短暂不可访问或响应变慢。数据丢失风险......
  • C语言 13 指针
    指针可以说是整个C语言中最难以理解的部分了。什么是指针还记得在前面谈到的通过函数交换两个变量的值吗?#include<stdio.h>voidswap(int,int);intmain(){inta=10,b=20;swap(a,b);printf("a=%d,b=%d",a,b);}voidswap(inta,intb)......
  • 天梯赛(常用STL函数)+ 常见算法
    0.(森森美图)判断一个点x3,y3在一条直线(由x1,y1和x2,y2组成)的哪一边若(y2-y3)/(x2-x3)-(y1-y3)/(x1-x3)>0逆时针方向否则顺时针方向1.vectorvector<node>ve;//定义ve.insert(ve.begin()+i,k);//中间插入ve.insert(ve.begin()+i,num,key);ve.erase(ve.begin()+i);//删......
  • 高等数学 2.2 函数的求导法则
    目录1、常数和基本初等函数的导数公式2、函数的和、差、积、商的求导法则3、反函数的求导法则4、复合函数的求导法则1、常数和基本初等函数的导数公式公式公式(1)\((C)'=0\)(2)\((x^{\mu})'=\mux^{\mu-1}\)(3)\((\sinx)'=\cosx\)(4)\((\cosx)'=-\sinx\)......
  • codesys将自定义的功能块或者函数保存到本地库
    将通过ST代码实现的自定义功能保存到codesys的本地库,其他project可以直接实现调用。提高灵活性和效率。1、创建库工程 这里可能会提示涉及个别库没有安装或版本更新,根据提示安装对应库或更新即可。2、添加功能块和函数3、编写功能块和函数的参数定义及逻辑实现    ......
  • Nuxt Kit 自动导入功能:高效管理你的模块和组合式函数
    title:NuxtKit自动导入功能:高效管理你的模块和组合式函数date:2024/9/14updated:2024/9/14author:cmdragonexcerpt:通过使用NuxtKit的自动导入功能,您可以更高效地管理和使用公共函数、组合式函数和VueAPI。无论是单个导入、目录导入还是从第三方模块导入,您都可......
  • MYSQL中 IF() IFNULL() NULLIF() ISNULL() 函数的使用
    IF()函数的使用IF(expr1,expr2,expr3),如果expr1的值为true,则返回expr2的值,如果expr1的值为false,则返回expr3的值。SELECTIF(TRUE,'A','B');--输出结果:ASELECTIF(FALSE,'A','B');--输出结果:BIFNULL()函数的使用IFNULL(expr1,expr2),如果expr1的值为null,则返回......
  • Golang并发编程中匿名函数的应用与性能考量
    在Golang并发编程中,匿名函数是一种常见且非常实用的编程工具。匿名函数是指没有名称的函数,可以在函数内部定义和使用,或者作为参数传递给其他函数。这种方式不仅简化了代码结构,还能增强代码的灵活性,尤其在并发编程中,匿名函数的使用更能展现其优势。不过,如何合理使用匿名函数并在实......