C语言详解(动态内存管理)1

Hi~!这里是奋斗的小羊,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~
💥💥个人主页:奋斗的小羊
💥💥所属专栏:C语言

🚀本系列文章为个人学习笔记,在这里撰写成文一为巩固知识,二为展示我的学习过程及理解。文笔、排版拙劣,望见谅。


目录

  • 前言
  • 1、为什么要有动态内存分配
  • 2、malloc 和 free
    • 2.1 malloc
    • 2.2 free
  • 3、calloc 和 realloc
    • 3.1 calloc
    • 3.2 realloc
  • 总结

前言

本篇文章将介绍C语言中除指针和结构体外又一重要的内容——动态内存管理
在C语言中,我们更多的需要手动分配和释放内存,这意味着我们必须正确地管理内存,以避免内存泄漏、内存溢出和其他内存错误,这些错误可能导致程序崩溃或安全漏洞。因此,了解内存管理是编写高质量、高效率和健壮性程序的重要部分。


1、为什么要有动态内存分配

目前我们申请内存的方法有两种,创建相关类型变量int n = 0;和创建相关类型数组int arr[10] = { 0 };
但是这样申请的内存是有缺点的:

  • 申请的内存大小是有限的,不能指定大小

  • 数组在声明的时候必须指定长度,数组空间一旦确定下来就不能调整

  • 数组空间在申请前我们不能给出一个准确的大小,大了浪费,小了不够

有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了
为了解决这个问题,C语言引入了动态内存开辟,让我们可以自己申请和释放内存,这样就比较灵活了
空间不够我们可以增大,空间太大我们可以缩小


2、malloc 和 free

使用动态内存管理函数都需要包含头文件<stdlib.h>

2.1 malloc

C语言提供了一个动态内存开辟的函数malloc

void* malloc(size_t size);

malloc 函数的作用是开辟一块指定大小的、连续的、有限的内存空间,大小由size 决定,是不能开辟无限空间的
在x86环境下开辟一块超大内存空间,若开辟失败打印出失败原因:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(INT_MAX);//INT_MAX=2147483647
	if (p == NULL)
	{
		//空间开辟失败
		perror("malloc");
		//失败后用return终止程序
		return 1;
	}
	return 0;
}

在这里插入图片描述

对于malloc函数,我们需要注意:

  • 参数的单位是字节
  • 如果size是0,malloc的行为是未定义的,取决于编译器
  • malloc的返回值是void *类型的指针
  • 申请空间成功的话返回起始地址,反之则返回NULL
  • malloc返回的地址我们基本都会直接强转为我们需要的类型的地址

示例:申请10个整形空间,存入1~10

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		//空间开辟失败
		perror("malloc");
		//失败后用return终止程序
		return 1;
	}
	//可以使用开辟好的空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	return 0;
}

在这里插入图片描述

malloc申请的空间和数组有什么区别?

  • 动态内存的大小可以调整
  • 空间开辟的位置不一样

在这里插入图片描述
我们创建的局部数组就在栈区

虽然空间有区别,但在使用上是一样的


2.2 free

C语言提供了另外一个函数free,专门是用来做动态内存的释放和回收的,mallocfree基本都要成对存在,函数原型如下:

void free(void* ptr);

free函数是用来释放开辟的动态内存的,我们将上面开辟的动态内存释放:

#include <stdio.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(10 * sizeof(int));
	if (p == NULL)
	{
		//空间开辟失败
		perror("malloc");
		//失败后用return终止程序
		return 1;
	}
	//可以使用开辟好的空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}
	//将开辟的动态内存释放
	free(p);
	p = NULL;
	return 0;
}

注意:用free释放动态内存空间后,指针p中还保留着其地址,安全起见我们需要给指针p赋NULL,因此free(p)p = NULL总是一起出现的
既然有free函数,所以说明动态内存是不能自动回收的,所以malloc申请的空间和数组又有了一个区别:

数组在进它的作用域时申请空间,出作用域时自动释放空间;而malloc申请的动态内存空间需要我们手动地释放
如果不释放,程序结束的时候也会被系统自动回收,但是并不建议这样做,自己申请的空间要自己释放,不然会浪费资源,也是不负责任的行为

特别的:

  • 如果参数ptr指向的空间不是动态开辟的,free的行为是未定义的
  • 如果参数ptrNULL指针,则free什么都不做

3、calloc 和 realloc

3.1 calloc

C语言还提供了一个函数calloc,其函数原型是:

void* calloc( size_t num, size_t size );

calloc的作用是开辟num个大小为size的连续空间,同时将内存空间初始化为0

calloc申请10个整型的空间,并打印出内存中的值

#include <stdio.h>
#include <stdlib.h>

int main()
{
	//int* p = (int*)malloc(10 * sizeof(int));
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		//空间开辟失败
		perror("calloc");
		//失败后用return终止程序
		return 1;
	}
	//可以使用开辟好的空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);//*(p + i)
	}
	//将开辟的动态内存释放
	free(p);
	p = NULL;
	return 0;
}

在这里插入图片描述

如果将malloc申请的动态内存空间中的值打印出来,应该都是随机值:

在这里插入图片描述

所以malloccalloc只两个区别:

  • malloc有1个参数,而calloc有2个参数
  • calloc会把申请的动态内存空间内的值初始化为全0,而malloc不会

3.2 realloc

在文章开头我们提到了,有时在定义数组的时候我们并不能给定数组一个准确的长度,大了浪费,小了不够。
realloc函数的出现让动态内存管理更加灵活,它的作用是调整动态内存空间的大小,原型如下:

void *realloc( void *ptr, size_t new_size );
  • ptr:指向之前通过malloccallocrealloc开辟的内存块(必须是起始地址)
  • new_size:内存新大小(单位字节)
  • 返回值void *:调整后的内存起始地址,若失败则返回空指针

当我们想用realloc函数将一个动态内存空间调整的小一点,则相应的动态内存空间就会减小到我们想要的大小;而当我们想用realloc函数将一个动态内存空间调整的大一点,这时候就会有两种情况出现:

情况一:原内存后的可用空间足够我们的扩容
在这里插入图片描述

这时候realloc函数就会按正常程序走,返回原内存的起始地址

情况二:原内存后的可用空间不够我们扩容
在这里插入图片描述

这时候realloc函数会在堆区中找一块足以完成我们目的的内存空间,并将原内存中的内容拷贝到新内存空间中,realloc函数还会自己将原内存空间释放,最后返回新开辟的内存空间的起始地址

当然,不管我们是想将原内存空间调小还是扩容,都有失败的可能
所以,realloc函数的返回值我们不能直接用指向原内存的指针接收,因为如果realloc返回的是NULL,则原内存的地址都会消失
我们可以用一个新指针过渡

#include <stdio.h>
#include <stdlib.h>

int main()
{
	//int* p = (int*)malloc(10 * sizeof(int));
	int* p = (int*)calloc(10, sizeof(int));
	if (p == NULL)
	{
		//空间开辟失败
		perror("calloc");
		//失败后用return终止程序
		return 1;
	}
	//可以使用开辟好的空间
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", p[i]);//*(p + i)
	}
	//调整空间,扩容到20个整型空间
	int* ptr = (int*)realloc(p, 20 * sizeof(int));//用新指针过渡
	if (ptr != NULL)
	{
		p = ptr;
	}
	//使用
	// ...
	
	//将开辟的动态内存释放
	free(p);
	p = NULL;
	return 0;
}

总结

  • 动态内存管理通过使用malloccallocrealloc等函数来分配内存,使用free函数来释放已经分配的内存。
  • 动态内存管理能够优化程序的内存利用率,避免内存泄漏和内存溢出等问题,在C语言中,动态内存管理是我们必须掌握的重要技能之一

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/687983.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MacBook Pro上高cpu上不断重启运行的efilogin-helper

高 cpu 运行这个不知道干什么的进程&#xff0c;让风扇疯狂输出&#xff0c;让人甚是烦躁&#xff0c;苹果社区里的回答比较抽象&#xff0c;要么换设备&#xff0c;要么重装。 尝试过找到这个文件&#xff0c;删了部分内容&#xff0c;无果。。。 stack overflow 有个回答&a…

stm32驱动直流电机实现启动/加速/减速/倒车/停车等功能

不积跬步&#xff0c;无以至千里&#xff1b;不积小流&#xff0c;无以成江海。 大家好&#xff0c;我是闲鹤&#xff0c;公众号 xxh_zone&#xff0c;十多年开发、架构经验&#xff0c;先后在华为、迅雷服役过&#xff0c;也在高校从事教学3年&#xff1b;目前已创业了7年多&a…

UML交互图-协作图

概述 协作图和序列图都表示出了对象间的交互作用&#xff0c;但是它们侧重点不同。序列图清楚地表示了交互作用中的时间顺序&#xff0c;但没有明确表示对象间的关系。协作图则清楚地表示了对象间的关系&#xff0c;但时间顺序必须从顺序号获得。序列图常常用于表示方案&#…

使用紫铜管制作半波天线的折合振子

一、概述 半波天线是一种简单而有效的天线类型&#xff0c;其长度约为工作波长的一半。它具有较好的辐射特性和较高的增益&#xff0c;广泛应用于业余无线电、电视接收等领域。使用紫铜管制作折合振子&#xff0c;不仅可以提高天线的机械强度&#xff0c;还能增强其导电性能。 …

k8s——Pod容器中的存储方式及PV、PVC

一、Pod容器中的存储方式 需要存储方式前提&#xff1a;容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。 首先&#xff0c;当容器崩溃时&#xff0c;kubelet 会重启它&#xff0c;但是容器中的文件将丢失——容器以干净的状态&…

如何让tracert命令的显示信息显示*星号

tracert命令如果在中间某一个节点超时&#xff0c;只会在显示信息中标识此节点信息超时“ * * * ”&#xff0c;不影响整个tracert命令操作。 如上图所示&#xff0c;在DeviceA上执行tracert 10.1.2.2命令&#xff0c;缺省情况下&#xff0c;DeviceA上的显示信息为&#xff1a;…

Xamarin.Android实现通知推送功能(1)

目录 1、背景说明1.1 开发环境1.2 实现效果1.2.1 推送的界面1.2.2 推送的设置1.2.3 推送的功能实现1.2.3.1、Activity的设置【重要】1.2.3.2、代码的实现 2、源码下载3、总结4、参考资料 1、背景说明 在App开发中&#xff0c;通知&#xff08;或消息&#xff09;的推送&#x…

k8s小型实验模拟

&#xff08;1&#xff09;Kubernetes 区域可采用 Kubeadm 方式进行安装。&#xff08;5分&#xff09; &#xff08;2&#xff09;要求在 Kubernetes 环境中&#xff0c;通过yaml文件的方式&#xff0c;创建2个Nginx Pod分别放置在两个不同的节点上&#xff0c;Pod使用hostPat…

Gradio 案例——将文本文件转为词云图

文章目录 Gradio 案例——将文本文件转为词云图界面截图依赖安装项目目录结构代码 Gradio 案例——将文本文件转为词云图 利用 word_cloud 库&#xff0c;将文本文件转为词云图更完整、丰富的示例项目见 GitHub - AlionSSS/wordcloud-webui: The web UI for word_cloud(text t…

AI产品经理岗位需求量大吗?好找工作吗?

前言 在当今这个科技日新月异的时代&#xff0c;人工智能&#xff08;AI&#xff09;已不再仅仅是一个遥远的概念&#xff0c;而是深深嵌入到我们生活的方方面面&#xff0c;从日常的语音助手到复杂的自动驾驶系统&#xff0c;AI的触角无处不在。随着AI技术的广泛应用和持续进…

kettle列转行(行扁平化)的使用

kettle行扁平化节点是将多行数据合并为一行数据如&#xff0c;其行为类似于css中的float排列 将上表格数据转换为下表格数据 namecategorynumjack语文10jack数学20jack英语40rose英语50 name语文数学英语jack102040rose50 使用行扁平化节点配置需要扁平化的字段&#xff0c…

泛微开发修炼之旅--09Ecology作为所有异构系统的待办中心,实现与kafka对接源码及示例

文章链接&#xff1a;泛微开发修炼之旅--09Ecology作为所有异构系统的待办中心&#xff0c;实现与kafka对接源码及示例

龙迅LT6911GX HDMI 2.1桥接到四 PORT MIPI或者LVDS,支持图像处理以及旋转,内置MCU以及LPDDR4

龙迅LT6911GX描述&#xff1a; LT6911GX是一款高性能的HDMI2.1到MIPI或LVDS芯片&#xff0c;用于VR/显示器应用。HDCP RX作为HDCP中继器的上游端&#xff0c;可以与其他芯片的HDCP TX协同工作&#xff0c;实现中继器的功能。对于HDMI2.1输入&#xff0c;LT6911GX可配置为3/4车…

JavaScript的核心语法

JavaScript JavaScript&#xff1a;JavaScript的组成&#xff1a;核心语法&#xff1a;Hello&#xff1a;变量&#xff1a;JS的基本数据类型&#xff1a;特殊点&#xff1a; 数组&#xff1a;流程控制语句&#xff1a;函数&#xff1a;函数的重载&#xff1a;函数的递归:预定义…

python-windows10普通笔记本跑bert mrpc数据样例0.1.001

python-windows10普通笔记本跑bert mrpc数据样例0.1.000 背景参考章节获取数据下载bert模型下载bert代码windows10的cpu执行结果注意事项TODOLIST背景 看了介绍说可以在gpu或者tpu上去微调,当前没环境,所以先在windows10上跑一跑,看是否能顺利进行,目标就是训练的过程中没…

WWDC24即将到来,ios18放大招

苹果公司即将在下周开全球开发者大会(WWDC)&#xff0c;大会上将展示其人工智能技术整合到设备和软件中的重大进展,包括与OpenAI的历史性合作。随着大会的临近,有关iOS 18及其据称采用AI技术支持的应用程序和功能的各种泄露信息已经浮出水面。 据报道,苹果将利用其自主研发的大…

WPF国际化的最佳实践

WPF国际化的最佳实践 1.创建项目资源文件 如果你的项目没有Properties文件夹和Resources.resx文件&#xff0c;可以通过右键项目-资源-常规-添加创建或打开程序集资源 2.添加国际化字符串 打开Resources.resx文件&#xff0c;添加需要翻译的文本字符&#xff0c;并将访问修…

深入了解 Postman 中的变量

在我们进行 API 开发和测试时&#xff0c;使用诸如 Postman 之类的工具可以极大地简化工作流程&#xff0c;提高效率。Postman 的一个强大功能就是变量&#xff08;Variables&#xff09;。利用变量&#xff0c;我们可以使我们的请求变得更加动态和灵活&#xff0c;避免重复输入…

计算机组成实验---Cache的实现

直接映射 先看懂cache的映射原理&#xff0c;根据cache大小与主存大小来计算各个信号线的位数 各个信号线位数 主存地址在逻辑上分为区号、块号、块内地址 Cache结构 Cache访问原理 基本过程 状态机&#xff1a;“三段式”实现 6.3 Verilog 状态机 | 菜鸟教程 (runoob.com) …

问题:当频点数大于载波数时,() #学习方法#知识分享

问题&#xff1a;当频点数大于载波数时&#xff0c;&#xff08;&#xff09; A.基带跳频可以执行&#xff0c;混合跳频可以执行 B.基带跳频不可以执行&#xff0c;混合跳频可以执行 C.基带跳频可以执行&#xff0c;混合跳频不可以执行 D.基带跳频不可以执行&#xff0c;混…