C语言动态内存空间分配

news2024/11/20 0:45:09

1. 前言

        在讲内存分配前,咱来聊一下为什么会有内存分配这个概念呢,大家都知道C语言当中是有着许多的数据类型,使用这些数据类型就会在内存上开辟其相对应的空间,那既然会开辟相应的空间,为什么还会有内存分配呢,我们会发现C语言分配内存空间,是已经被固定好了的,固定开辟多少空间,但是这样开辟空间会不会太死板了呢,如果我用不上这些空间呢,这样会导致内存的利用率很低,有没有一种,我需要多少空间,就开辟多少空间的方法呢,这个时候C语言就为我们引进了动态内存分配的这个概念,顾名思义,就是你想分配多少空间就分配多少空间,下边我们来一起了解一下怎样在内存上开辟我们想要的空间

(注意:使用这些函数都需要包含#include<stdlib.h>这个头文件)

2. malloc函数:

2.1 malloc函数的声明:

函数向内存申请⼀块连续可⽤的空间,并返回指向这块空间的指针(由于不知道你要申请的类型,使用返回值为void*)

  1. 如果开辟成功,则返回⼀个指向开辟好空间的指针。
  2. 如果开辟失败,则返回⼀个 NULL 指针,因此malloc的返回值⼀定要做检查
  3.  如果参数 size 为0,malloc的⾏为是标准是未定义的取决于编译器决于编译器(vs2022)

所以一个malloc的使用可以分为以下几步:

  1. 使用malloc函数对内存空间申请指定空间
  2. 判断返回值是否为NULL
  3. 内存空间的释放

2.2 malloc函数的简单应用:

#include <stdlib.h>
int main()
{
	int* p = (int*)malloc(sizeof(int) * 5); //向内存申请5个int型的内存空间
	if (p == NULL)  //判断返回值是否为空
	{
		perror("malloc");  //为空,则报错malloc函数处有问题
		return 1;
	}
	free(p);  //释放内存,在下边会说喔
    p = NULL;  //将p指针赋为NULL,防止出现野指针
	return 0;
}

3. free函数:

        在前边的代码当中,我们了解到free函数是专门用来释放动态申请的内存空间,同时也说明了它只能释放动态申请的内存空间,关于这一点兄弟们不要搞混咯

3.1 free函数声明:

​​​​​

  1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的⾏为是未定义的
  2. 如果参数 ptr 是NULL指针,则函数什么事都不做
关于free函数的使用前面有提到,有需要的兄弟可以往上翻翻

4. malloc与free的实际运用:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);//申请10个int型元素
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)  //对malloc申请的空间进行赋值
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)  //输出
	{
		printf("%d ", *(p + i));
	}
	free(p);  //释放空间
	p = NULL;
	return 0;
}
//执行结果
0 1 2 3 4 5 6 7 8 9

5. calloc函数:

5.1 calloc函数的声明:

calloc

void* calloc (size_t num, size_t size);
  1. 与malloc相似,也是用来开辟内存空间
  2. 开辟num个size大小的元素
  3. 相较于malloc来说,calloc开辟好空间后,还会将空间内的每个字节初始化为0

5.2 calloc的简单应用:

下边我们来一段代码调试进行了解:

int main()
{
	int* p1 = (int*)calloc(10, sizeof(int));
	if (p1 == NULL)
	{
		perror("calloc");
		return 1;
	}
	free(p1);
	return 0;
}

题解分析:

6 realloc函数:

        当你觉得动态内存申请的空间,不够使用的时候,这个时候就可以运用realloc函数对内存空间进行扩容,所以realloc函数是对动态内存空间进行扩容的

6.1 realloc函数的声明:

realloc

void* realloc (void* ptr, size_t size);

        1. ptr 是要调整的内存地址

        2. size 为调整之后新大小
        3. 返回值为调整之后的内存起始位置
        4.  这个函数调整原内存空间⼤⼩的基础上,还会将原来内存中的数据移动到新的空间

6.2 relloc函数扩容内存时的两种情况:

        1. 原有的空间在扩容的时候,后边有足够的空间

        2.  原有的空间在扩容的时候,后边没有足够的空间

6.3 relloc函数的简单应用:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 10);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i;
	}
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
	int* p1 = (int*)realloc(p,sizeof(int) * 15);  //扩容5个int型
	if (p1 == NULL)
	{
		perror("realloc");
		return 2;
	}
	for (int i = 10; i < 15; i++)
	{
		*(p1 + i) = i;
	}
	for (int i = 0; i < 15; i++)
	{
		printf("%d ", *(p1 + i));
	}
	free(p1);
	p1 = NULL;
	return 0;
	return 0;
}
//执行结果
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14

6.4 柔性数组:

        在c99中规定,一个结构体中最后的数组如果没有指定大小,则这个数组被称为柔性数组

柔性数组的特点:

  1. 结构中的柔性数组成员前⾯必须⾄少⼀个其他成员
  2. sizeof 返回的这种结构⼤⼩不包括柔性数组的内存
  3. 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小
举例:
struct S
{
	int a;
	char a1;
	int a3[];
}q;

int main()
{
	printf("%结构体大小为:zd", sizeof(q)); //计算结构体大小,了解柔性数组是否分配了大小
	struct S* s = (struct S*)malloc(sizeof(struct S) + sizeof(int) * 5); //为a3数组分配了5个int型的空间
    return 0;
}

题解分析:

6.4.1 柔性数组的简单应用:

        申请一块动态内存空间后,对齐扩容,扩大柔性数组的内存空间,然后对柔性数组进行赋值,最后输出结构体中成员的值:

//柔性数组
struct S
{
	int a;
	char a1;
	int a3[];
}q;
	int main()
	{
		int ret = sizeof(struct S);
		struct S* p2 = (struct S*)malloc(sizeof(struct S));
	
		if (p2 == NULL)
		{
			perror("malloc");
			return 1;
		}
		p2->a = 100;
		p2->a1 = 48;
	
		struct S* p3 = (struct S*)realloc(p2, sizeof(struct S)+sizeof(int)*5); //扩容柔性数组的大小
		printf("%zd\n", sizeof(p3));
		if (p3 == NULL)
		{
			perror("realloc");
			return 2;
		}
		p2 = NULL;
		for (int i = 0; i < 5; i++)
		{
			p3->a3[i] = i;
		}
	
		printf("%d\n", p3->a);
		printf("%d\n", p3->a1);
		for (int i = 0; i < 5; i++)
		{
			printf("%d ", p3->a3[i]);
	
		}
		free(p3);
		p3 = NULL;
		return 0;
	}
//执行结果
8
100
48
0 1 2 3 4

7. 常见的动态内存错误:

7.1 使用free对非动态内存进行释放:

int main()
{
	int a = 10;
	int* p = &a;
	free(p); //free对非动态内存申请的空间进行释放
	return 0;
}

执行结果:

7.2 动态内存越界访问:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);//向内存动态申请5个int型空间
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 6; i++) //向p执行的空间放入6个int型元素
	{
		*(p + i) = i;
	}
	return 0;
}

执行结果:

(程序死循环加上报错)

7.3 使⽤free释放⼀块动态开辟内存的⼀部分:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	for (int i = 0; i < 5; i++) 
	{
		*p = i;
		p++;   //每执行一次p++,则p指向的地址加1
	}
	free(p);
	return 0;
}

题解分析:

执行结果:

(程序死循环加上报错)

7.4 对同⼀块动态内存多次释放

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	free(p);
	free(p);
	return 0;
}

执行结果:

(程序死循环加上报错)

7.5 动态开辟内存忘记释放(内存泄漏):

        什么意思呢,简单来讲,就是你申请了空间,但是没有及时的将空间释放掉,与上边多次释放刚好相反:

int main()
{
	int* p = (int*)malloc(sizeof(int) * 5);
	if (p == NULL)
	{
		perror("malloc");
		return 1;
	}
	//free(p);  //为注释掉的内容
	//free(p);
	return 0;
}

执行以后不会报错,但是会造成内存上的浪费


7.6 对NULL指针的解引⽤操作

int main()
{
	int* p = (int*)malloc(INT_MAX*10);
	*p = 100;
	free(p);
	return 0;
}

由于并没有判断p是否为NULL,当p为NULL时,就会发生以下报错:

(死循环直至程序崩溃)

(今日分享到此结束,如觉得对您有帮助,还请点赞关注支持一下,Thanks♪(・ω・)ノ!!!)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

函数式编程(一)

函数式编程总体介绍 函数式编程(functional programming)其实是个很古老的概念&#xff0c;诞生距今快60年啦&#xff01; 最古老的函数式编程语言Lisp 新出现的函数式编程语言&#xff1a;比如Erlang、Scala、clojure等 热门语言&#xff1a;Python、java、JavaScript、C等…

Scala第十九章节(Actor的相关概述、Actor发送和接收消息以及WordCount案例)

Scala第十九章节 章节目标 了解Actor的相关概述掌握Actor发送和接收消息掌握WordCount案例 1. Actor介绍 Scala中的Actor并发编程模型可以用来开发比Java线程效率更高的并发程序。我们学习Scala Actor的目的主要是为后续学习Akka做准备。 1.1 Java并发编程的问题 在Java并…

【Python】无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称解决方案

【Python】无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇【Python】无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称解决方案✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 今天寸铁…

java(7)之跳转语句

1、break跳转语句 说到break其实也不是跳转&#xff0c;它更像是一个终结语句&#xff0c;常用于在循环语句需要停止出现例如 while&#xff08;&#xff09;{ if&#xff08;&#xff09;{ break&#xff1b; }} 这样的形式或者 switch&#xff08;&#xff09;{ case…

LEAP模型的能源环境发展、碳排放建模预测及不确定性分析教程

原文链接&#xff1a;LEAP模型的能源环境发展、碳排放建模预测及不确定性分析教程https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247599754&idx4&sn243c9f8bff355235a7056c2cbb1331fa&chksmfa82076dcdf58e7b871c3369c95ead9ff1d90baa0431318b26b6abd27…

C语言进阶课程学习记录-第23课 - #error 和 #line 使用分析

C语言进阶课程学习记录-第23课 - #error 和 #line 使用分析 实验-#errer的使用实验-缺少#error实验-#line 1的使用实验-#line 1用于标记代码小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 实验-#er…

C++读取.bin二进制文件

C读取.bin二进制文件 在C中&#xff0c;可以使用文件输入/输出流来进行二进制文件的读写操作&#xff0c;方便数据的保存和读写。 //C读取bin二进制文件 int read_bin() {std::ifstream file("data_100.bin", std::ios::in | std::ios::binary);if (file) {// 按照…

水离子雾化壁炉如何实现火焰的虚实变化?

水离子雾化壁炉通过调节水雾的密度和电子控制器的设置来实现火焰的虚实变化。具体实现方法如下&#xff1a; 调节水雾密度&#xff1a; 超声波振动器可以调节水分子的雾化效果&#xff0c;从而控制水雾的密度。增加水雾的密度会使火焰看起来更实&#xff0c;而减少水雾的密度则…

初始C语言最后一章《编译、链接与预处理详解》

前言 感谢老铁们的陪伴和支持&#xff0c;初始C语言专栏在本章内容也是要结束了&#xff0c;这创作一路下来也是很不容易&#xff0c;如果大家对 Java 后端开发感兴趣&#xff0c;欢迎各位老铁来我的Java专栏&#xff01;当然了&#xff0c;我也会更新几章C语言实现简单的数据结…

【文献分享】机器学习 + 分子动力学 + 第一性原理 + 热力学性质 + 微观结构

分享一篇关于机器学习 分子动力学 第一性原理 热学性质&#xff08;密度、比热容、导热率和粘度&#xff09; 微观结构的文章。 感谢论文的原作者&#xff01; 关键词&#xff1a; 1. Deep potential 2. Machine learning 3. Molecular dynamics 4. Microscopic structu…

RTK-GNSS天线的方向对接收器性能有哪些影响?

RTK-GNSS天线的方向对接收器性能有哪些影响&#xff1f;它取决于许多难以准确定义的因素。 在这篇文章中&#xff0c;我们试图做一个定性分析&#xff0c;看是否能得出结论。 测试环境&#xff1a; 天线安装在三脚架上&#xff0c;环境近乎理想&#xff0c;视野开阔&#xff0…

【javaWeb 第十一篇】(Spring )事务管理AOP

事务管理&AOP 事务管理Spring事务管理事务属性rollbackFor事务属性propagation AOP快速入门AOP概念AOP的执行流程 AOP进阶通知类型通知顺序 切入点表达式切入点表达式-execution切入点表达式-annotation 连接点 事务管理 事务&#xff1a; 事务是一组操作的集合&#xff0…

CSS - 你实现过0.5px的线吗

难度级别:中级及以上 提问概率:75% 我们知道在网页显示或是网页打印中,像素已经是最小单位了,但在很多时候,即便是最小的1像素,精度却不足以呈现所需的线条精度和细节。因此,为了在网页显示和网页打印中呈现更加细致的线条,为了在视觉…

【计算机网络】epoll

IO多路转接 - epoll 一、I/O多路转接之 epoll1. epoll 接口&#xff08;1&#xff09;epoll_create()&#xff08;2&#xff09;epoll_wait()&#xff08;3&#xff09;epoll_ctl() 2. epoll 原理3. epoll 的优点4. epoll 的使用5. epoll 的工作模式&#xff08;1&#xff09;水…

中高级前端? 这些一元运算符,你真的搞清楚了吗

前言 一元运算符&#xff0c;不太起眼&#xff0c;作用很大&#xff0c;请别忽视她&#xff01; 走近她&#xff0c;爱上她&#xff01; 定义 只需要一个操作数的运算符称为一元运算符。 还是代码容易懂&#xff1a; 1 // 一个操作数1 2 // 两个操作数一元运算符清单 运…

MacOS - brew 和 brew cask 有什么区别?

brew 是 ruby 的包管理&#xff0c;后来看 yangzhiping 的博客介绍了 brew cask&#xff0c;感觉 cask 是更好的关联关系管理&#xff0c;但是&#xff0c;我后来使用过程中&#xff0c;发现很多软件 brew cask 里没有&#xff0c;但是 brew 里面倒是挺多&#xff01;今天来给说…

一键批量高效记账,支持通过关键词来筛选某个人的借款记录,方便高效管理收支明细

随着生活节奏的加快&#xff0c;个人和企业财务管理变得越来越复杂。尤其是在处理大量记账任务时&#xff0c;如何快速、准确地完成&#xff0c;并且能够方便地追踪和筛选特定借款记录&#xff0c;成为了许多人关注的焦点。现在&#xff0c;我们为您提供一款全新的财务管理工具…

小程序appsecret在哪里看

问题开发中需要用到appid和secret。但找不到secret。后面发现是在微信开发者平台自己生成才行 微信公众平台 (qq.com) 在开发者平台的开发管理当中&#xff0c;生产即可

U盘位置不可用?数据恢复有高招!

在我们日常生活和工作中&#xff0c;U盘已经成为不可或缺的数据存储工具。然而&#xff0c;有时我们会遭遇一个令人头疼的问题&#xff1a;将U盘插入电脑后&#xff0c;系统提示“U盘位置不可用”。这究竟意味着什么呢&#xff1f;简单来说&#xff0c;这就是电脑无法识别或访问…

mysql慢sql排查与分析

当MySQL遇到慢查询&#xff08;慢SQL&#xff09;时&#xff0c;我们可以通过以下步骤进行排查和优化&#xff1a; 标题开启慢查询日志&#xff1a; 确保MySQL的慢查询日志已经开启。通过查看slow_query_log和slow_query_log_file变量来确认。 如果没有开启&#xff0c;可以…