【C语言】内存你知多少?详解C语言动态内存管理

news2024/11/15 10:57:07

 

目录

一, 计算机中的内存

二,动态内存申请函数

2.1 头文件

2.2  malloc函数

2.3 free函数

2.3 calloc函数

2.4 realloc函数——调整空间函数

情况1:原有空间之后有足够大的空间

情况2:原有空间之后没有足够大的空间  

2.5 经典笔试题

 1.

2. 

三,柔性数组

结语


一, 计算机中的内存

    我们知道目前内存有,栈区,堆区,静态区。

C/C++程序内存分配的几个区域:

  •   栈区(stack):在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些 存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。 栈区主要存放运行函数而分配的局部变量、函数参数、返回数据、返回地址等。
  •   堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS(操作系统)回收 。分配方式类似于链表。
  •  数据段(静态区)(static)存放全局变量、静态数据。程序结束后由系统释放。
  •  代码段:存放函数体(类成员函数和全局函数)的二进制代码。

 目前我们有这些开辟内存空间的方法:

int  h  = 100;             //  在静态区创建全局变量

int main()

{

static  z  = 10;         // 变量储存在静态区

int val = 20;               //在栈空间上开辟四个字节

char arr[10] = {0};     //在栈空间上开辟10个字节的连续空间

   return 0;

}

但是上述的开辟空间的方式有两个特点

1. 空间开辟大小是固定的。

2. 数组在申明的时候,必须指定数组的长度,它所需要的内存在编译时分配。

但是对于空间的需求,不仅仅是上述的情况。有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编 译时开辟空间的方式就不能满足了。 这时候就只能试试动态存开辟了。 

二,动态内存申请函数

2.1 头文件

#include<stdlib.h>

2.2  malloc函数

void* malloc (size_t size);  // szie 字节数的意思,可以直接填数字

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

这个函数向内存堆区申请一块连续可用的随机空间,并返回指向这块空间的指针。

  • 如果开辟成功,则返回一个指向开辟好空间的指针
  • malloc可以申请0字节的空间,会返回一个没有空间的指针,不能访问。
  • 如果开辟失败,则返回一个NULL指针,因此malloc的返回值一定要做检查。
  • 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定

 运用:

int * a = (int *)malloc(4); // 4可以改成 sizeof(int)
if	(a == NULL)
{
  perror("malloc"); // 开辟失败打印原因
}

2.3 free函数

void free (void* ptr);

free函数用来释放动态开辟的内存。

  • 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。
  • 如果参数 ptr 是NULL指针,则函数什么事都不做。
  • 当我们不释放动态申请的内存时,如果持续申请内存,那么电脑内存不断变少,没有内存时就会死鸡,当程序结束时,操作系统自动回收内存。
  • 如果程序不结束,那么不回收的内存,会越来越多,这就是出现内存泄露问题。

2.3 calloc函数

void* calloc (size_t num, size_t size);

参数解析:

  • num:  创建数据类型的个数。
  • size:  每个数据类型所占的字节数

特点: 

  •  函数的功能是为 num 个大小为 size 的元素开辟一块空间,并且把空间的每个字节初始化为0。
  • 与函数 malloc 的区别只在于 calloc 会在返回地址之前把申请的空间的每个字节初始化为全0。 
#include <stdio.h>
#include <stdlib.h>
int main()
{
	int* a = (int*)malloc(8); // 2个整型
	int* b = (int*)calloc(2,sizeof(int)); // sizeof(int) 可以是 4,反正表示字节数
	return 0;
}

 查看内存验证:

2.4 realloc函数——调整空间函数

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

参数解析:

  • ptr :是要调整的内存地址。(如果为空指针,那么同malloc功能类似。)
  • size :调整之后新大小
  • 返回值 : 为调整之后的内存起始位置。

realloc函数的出现让动态内存管理更加灵活。 有时会我们发现过去申请的空间太小了,有时候我们又会觉得申请的空间过大了,那为了合理的使用内存, 我们一定会对内存的大小做灵活的调整。

 realloc 函数就可以做到对动态开辟内存大小的调整。 函数原型

如下:

     这个函数调整原内存空间大小的基础上,还会将原来内存中的数据移动到新的空间。

realloc在调整内存空间的是存在两种情况:

  • 情况1:原有空间之后有足够大的空间

  • 情况2:原有空间之后没有足够大的空间  

 

 看以下代码,思考代码那里不合理:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int a = 10;
	int* p = &a;
	p = (int*)realloc(p, sizeof(int));
	printf("%d", *p);
	return 0;
}

 我们可以看出,这里没有判断realloc是否成功,如果申请失败,返回NULL,那么p就会丢掉原有的地址,这是不合理的,因此我们需要进行判断,所以正确的代码是:

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int a = 10;
	int* p = &a;
	int *tmp= (int*)realloc(p, sizeof(int));
	if (tmp == NULL)
	{
		perror("realloc");
		return -1;// 或者exit(-1);
	}
	p = tmp;
	printf("%d", *p);
	return 0;
}

2.5 经典笔试题

 1.

   思考:程序结果

char* GetMemory(void)
{
	char p[] = "hello world"; // 栈区开辟,函数结束内存收回
	return p;
}
void Test(void)
{
	char* str = NULL;
	str = GetMemory();  // 非法访问。访问被系统收回的内存
	printf(str);        // 无法打印,已被覆盖
}

int main()
{
	Test();
	return 0;
}

2. 

  程序中存在的问题

void Test(void)
{
	char* str = (char*)malloc(100); // 缺少对malloc返回值检查
	/*加上:if(str == null)
	{
		perror("malloc");
		exit(-1);
	}*/
	strcpy(str, "hello");
	free(str);      
	// 加上 str = null;
	if (str != NULL)         // str还存有原先内存的地址,出现野指针 
	{
		strcpy(str, "world");
		printf(str);
	}
}

int main()
{
	Test();
	return 0;
}

三,柔性数组

     也许你从来没有听说过柔性数组(flflexible array这个概念,但是它确实是存在的。 C99 中,结构中的最 后一个元素允许是未知大小的数组,这就叫做『柔性数组』成员。

例如:

typedef struct st_type

{

  int i;

  int a[];  //柔性数组成员

}type_a;

 特点:

  • 结构中的柔性数组成员前面必须至少一个其他成员
  • sizeof 返回的这种结构大小不包括柔性数组的内存。

例如: 

typedef struct mystruct
{
    double a;
    int c[];
}MS;

int main()
{
    printf("%d\n", sizeof(MS)); // 8
    return 0;
}
  • 包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小。

如下: 

typedef struct mystruct
{
    double a;
    int c[];
}MS;

int main()
{
    MS * p1 = (MS* )malloc(sizeof(MS) + 40);  // 那就是48个字节,40就是对柔性数组申请的                                                                                                                                 //预期空间
    return 0;
}

也可以这样:

代码2
typedef struct mystruct
{
    double a;
    int *c;
}MS;
int main()
{
    MS * p1 = (MS* )malloc(sizeof(MS));  
    if  (p1 == NULL)
{
   perror ( "malloc");
   exit(-1);
}
   p1->c = (int *)realloc(c, 40);
    return 0;
}

 代码1相较于代码2的优势:

  • 方便释放内存。代码2需要2次释放内存。
  • 有利于提高数据命中率,提升运行效率,减少内存碎片。

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

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

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

相关文章

Android大图加载优化方案,避免程序OOM

我们在编写Android程序的时候经常要用到许多图片&#xff0c;不同图片总是会有不同的形状、不同的大小&#xff0c;但在大多数情况下&#xff0c;这些图片都会大于我们程序所需要的大小。比如微博长图&#xff0c;海报等等。所以我们就要对图片进行局部显示。 大图加载基本需求…

信号与系统复习笔记——信号与系统的时域和频域特性

信号与系统复习笔记——信号与系统的时域和频域特性 傅里叶变换的模和相位表示 一般来说&#xff0c;傅里叶变换的结果是复数&#xff0c;所以能够使用模和相位来表示&#xff0c;具体的有&#xff1a; X ( j ω ) ∣ X ( j ω ) ∣ e j ∡ X ( j ω ) X(j\omega) |X(j\ome…

浅尝Transformer和LLM

文章目录 TransformerTransformer的衍生BERTPre-trainingBERT与其他方法的关系怎么用BERT做生成式任务&#xff1f; GPTPre-trainingFine-Tuning Transformer工具开源库特点 LLM系列推理服务 大语言模型势不可挡啊。 哲学上来说&#xff0c;语言就是我们的一切&#xff0c;语言…

MySQL 高级(进阶) SQL 语句

创建两个表格 use awsl; create table location (Region char(20),Store_Name char(20)); insert into location values(East,Boston); insert into location values(East,New York); insert into location values(West,Los Angeles); insert into location values(West,Houst…

JMU20 软件工程经济学 复习总结

文章目录 碎碎念0. 基准收益率 i1. 现金流量图2. 净现值 NPV&#xff0c;内部收益率 IRR3. 单利&#xff0c;复利计算4. 等额年金NAV5. 动态回收期 P t ′ P_t Pt′​6. 固定资产折旧 [书P44]7. 增值税8. 软件行业增值税的即征即退9. 利息备付率 ICR&#xff0c;偿债备付率 DSC…

C语言之分支与循环

一、语句 什么是语句 C语言中&#xff0c;由一个分号&#xff08; &#xff1b;&#xff09;隔开的即为一条语句。 这些都是语句&#xff1a; &#xff08; 一行里只有 &#xff1b;的语句&#xff0c;我们称其为 “空语句” &#xff09; int main(void) {printf("hel…

UVM1.2究竟在UVM1.1上做了哪些升级

想必大家平时也没有很注意UVM1.1版本和UVM1.2版本的不同之处&#xff0c;只有在用一些以前UVM1.1能支持的功能&#xff0c;到了UVM1.2却出现编译报错&#xff0c;找不到对应的变量或者函数或者类的时候&#xff0c;才意识到这两个版本的差异。笔者也是遇到了1个打印问题&#x…

利用Django的视图类TemplateView将模板、视图与模板变量方便快速的整合在一起

TemplateView是Django提供的通用视图类之一&#xff0c;它允许您在不编写任何Python代码的情况下将模板与视图关联起来。下面是关于TemplateView类的一些介绍&#xff1a; 渲染模板&#xff1a;TemplateView负责渲染指定的模板并返回生成的HTML响应。您只需提供模板名称或路径即…

华为OD机试真题B卷 Java 实现【删除字符串中出现次数最少的字符】,附详细解题思路

一、题目描述 删除字符串中出现次数最少的字符&#xff0c;如果多个字符出现次数一样则都删除。 二、输入描述 一个字符串。 三、输出描述 删除字符串中出现次数最少的字符&#xff0c;如果多个字符出现次数一样则都删除&#xff0c;如果都被删除 则换为empty。 四、解题…

【安装lnmp实操】

文章目录 一、安装Nginx服务1.安装依赖包2、创建运行用户3、编译安装4、优化路径5、添加 Nginx 系统服务 二、【安装 MySQL 服务】1、安装Mysql环境依赖包2、创建运行用户3、编译安装4、修改mysql 配置文件5、更改mysql安装目录和配置文件的属主属组6、设置路径环境变量7、初始…

MySQL数据库高级SQL语句(图文详解!)

一、MySQL高级语句 1.SELECT 2.DISTINST 3.WHERE 4.AND|OR ​5.IN 6.BETWEEN 7.通配符 8.LIKE 9.ORDER BY 10.函数 &#xff08;1&#xff09;数学函数 &#xff08;2&#xff09;聚合函数 &#xff08;3&#xff09;字符串函数 11.GROUP BY 12.HAVING 13.别名 14.子查询 …

团体程序设计天梯赛-练习集L1篇③

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的普通人。 &#x1f3c0;个人主页&#xff1a;陈童学哦CSDN &#x1f4a1;所属专栏&#xff1a;PTA &#x1f381;希望各…

插件 - 插件机制触手可及

文章目录 Pre方案流程图优点缺点Code小结Pre 插件 - 一份配置,离插件机制只有一步之遥 中是不是有依赖, 我不想依赖 ,肿么办? 方案 应用A定义服务接口,约定插件实现的功能规范。应用B,C,D等各自实现该接口,并打包成jar包,放置在应用A约定的读取目录下。应用A在运行时,读取…

MATLAB 动画的制作与保存

matlab有三种方法来创建动画&#xff1a; ①以质点运动轨迹的方式来创建动画 theta0:0.001:2*pi; %定义圆的半径 r10; %生成圆上各点的横纵坐标 xr*cos(theta); yr*sin(theta); comet(x,y); 生成的动画效果如下 &#xff08;其中的某一步&#xff09; 最终结果如下 comet(x,…

怎么将存入此电脑中的图片放入电脑D盘

先找到图片的目录&#xff0c; 目录为&#xff1a;xxx 然后 现在图片目录就移动到D盘了

算法设计 - KMP算法

字符串模式匹配问题 假设有两个字符串S&#xff0c;T&#xff0c;其中S是主串&#xff08;正文串&#xff09;&#xff0c;T为子串&#xff08;模式串&#xff09;&#xff0c; 我们需要在S中查找与T相匹配的子串&#xff0c;如果成功找到&#xff0c;则返回匹配的子串第一个…

华为OD机试真题 JavaScript 实现【太阳能板最大面积】【2022Q4 100分】,附详细解题思路

一、题目描述 给航天器一侧加装长方形或正方形的太阳能板&#xff08;图中的红色斜线区域&#xff09;&#xff0c;需要先安装两个支柱&#xff08;图中的黑色竖条&#xff09;&#xff0c;再在支柱的中间部分固定太阳能板。 但航天器不同位置的支柱长度不同&#xff0c;太阳…

logback日志框架基本知识

本文来说下logback日志框架基本知识 文章目录 概述logback简介SpringBoot对logback的支持SpringBoot的集成 概述 Spring Boot已经将logback做为默认集成的日志框架&#xff0c;全面了解学习是必然了。曾经log4j是流行的日志框架&#xff0c;现在已被它的继任者logback替代&…

第三节 折线图

文章目录 折线图1.1 numpy介绍1.2 预测趋势1.3 折线图流程工具 Pandas1.3.1 读取并生成 CSV1.3.2 输出列数据1.3.3 画折线图1.5 添加横纵坐标,标题 屏幕属性 1.4 画双折线图1.5 扩展:做三线图 折线图 1.1 numpy介绍 上节课我们学了柱状图, 通过柱状图可以了解, numpy主要是科…

部署lnmp框架nginx在上一章节

目录 一.安装mysql服务 1.下载mysql和模块boost并解压包到/opt目录下 2.创建运行用户 3.进入mysql包目录下面进行编译安装 4.创建普通用户管理mysql useradd -s /sbin/nologin mysqlchown -R mysql:mysql /usr/local/mysql/ 5.修改配置文件 6.设置环境变量&#xff0c;申…