动态内存管理(2)

news2024/11/24 13:02:09

TIPS

1. scanf读取与空格:

我们都知道,scanf()在从输入缓冲区里面读取数据的时候,如果中间碰到了空格,那么就会直接停下来,而如果在最前面有个空格,直接无视空格。 

 

2. scanf()读取与\n,如果是读取字符串或者数字,那么最开头的\n根本起不到什么影响但是如果scanf%c去读取字符的话,如果输入缓冲区里的\n没有被清理掉,就会直接去读\n这个字符 

3.  

有关动态内存分配题目1

附:

1. 绝大部分情况下,指针(变量)也是临时变量,是放在内存栈区的,内存栈区里面的东西一旦出了其作用域,就会直接销毁。
2. 实参是指针变量,形参是指针变量,这两个指针变量在交接的时候都指向同一地址,是传值调用,形参是实参的一份临时拷贝。交接完之后,形参在怎么折腾,与实参无关。但同时,由于形参也是指针,它解引用在内存里面改来改去的话当函数调用结束后会遗留下影响的。但是形参有自己的独立空间
3. NULL空指针其实就是0。0地址这块空间是不允许普通程序去访问的。写入的时候会发生访问冲突。但是对空指针取地址是可以的,解引用访问是不行的
4. 里面也存在内存泄露。这个malloc向堆区申请来的空间函数里面没有释放,出了函数也不给留个"标记"指针,这块空间没人记得了也没还给操作系统。 
6. 实参为指针变量还是传值调用,实参为货真价实的地址就是传址调用

解决办法:

void GetMemory(char** p)
{
	char* pa = (char*)malloc(100);
	if (pa == NULL)
	{
		perror("");
	}
	*p = pa;
}
void Test(void)
{
	char* str = NULL;
	GetMemory(&str);
	strcpy(str, "hello world");
	printf(str);
	free(str);
	str = NULL;
}
int main()
{
	Test();
	return 0;
}

有关动态内存分配题目2

 

附:

1. 对于内存栈区与堆区一定要区分开。不能混淆在一起,哪个东西在哪边要清清楚楚。
2. 局部变量(包括数组,指针变量等等),函数的参数与函数栈帧,反正一切临时的变量都是在内存栈区里面的,对于内存栈区里面的东西,进入其作用域就会创建,出了之后就会销毁,原先的那个内存空间已经还给操作系统了。而对于内存堆区而言,我是专门用来进行动态内存的开辟与释放的,只要你一旦创建好,没有手动用free(指向那个堆区空间的指针)释放的话,那个空间就像鬼屋一样一直在那。
3. 当函数内部有一个指针指向一个栈区空间,然后你返回指针,这时候函数调用结束后里面的所有东西全部销毁,原先指针指向的空间也已经还给操作系统了。这时候返回一个指针,如果还厚着脸皮去解引用,就会造成非法访问。这个叫做返回栈空间地址的问题。地址我虽然存起来了,但是我空间已经销毁了啊。但是会有偶然现象发生,有时候这个空间还给操作系统了,但是这个数据倒还是停留在里面没有被覆盖掉。
(常量字符串不像局部变量一样,局部变量是在栈区里面的,进入范围就创建,出了范围就会销毁。而常量字符串并不是局部范围的变量,不是在栈区的,而在可读数据区,所以并不会随着函数的返回而销毁)
(static修饰的话就是说即使出了作用域,变量也不会销毁,空间还是属于我们的,没有还给操作系统,static 可以修饰数组与变量..)

4. 至于说内存泄露,那是对堆区而言,malloc等申请了一个堆区空间,结果我不用了,又不返回,别人用不了,回头这个空间还可能丢了。再次强调一下:栈区与堆区千万不能一团浆糊搞在一起。

有关动态内存分配题目3

附:

1. 唯一的问题就在于内存泄漏

2. 

 1. 用malloc,calloc,realloc开辟的堆区空间不会自己去释放,一定需要你手动用free去释放
 2. 你如果不释放的话,就会形成内存泄露:
你不用了
你不释放
别人还用不了
回头你这块空间还可能找不到了 

有关动态内存分配题目4  

附: 

非法访问,属于操作系统的内存空间是你能随便这么用野指针解引用去访问的吗?

1. free(p)的话也就是把p指向的堆区空间还给了操作系统。但是对于那个指针p的话,它还是这么孤零零的指向着那边,虽然他指向的那个堆区里面原先的空间早已去世了,这时候就会很危险,万一对它解引用操作的话就会非法访问,这也是为什么叫回收置空,要置空,置空,置空,置空,别以为free完了之后就可以拍拍屁股走了
2. free()的意思就是把指针指向的堆区空间还给操作系统,但是我这块堆区空间还在那儿放着,里面的数据可能没有被别人修改。但是已经属于操作系统了,使用权限不在我这儿了。
3. 指向属于操作系统的堆区/栈区空间的指针就是一条野狗,野指针。
4. 你去使用修改与访问属于操作系统的栈区/堆区空间,已经形成了非法访问了,代码出问题了。
修改:将str置为空

C/C++程序的内存开辟 

那我们数据在内存里面开辟空间的时候,到底是怎么在内存里面开辟的呢?
C/C++程序到底是如何去内存里面开辟空间的呢?
1. 我们之前知道内存分为三个区域:栈区,堆区,静态区。
2. 但是让我们现在分析的更加细一点。
3. C/C++程序当中内存区域的划分其实是比较多的。

内核空间

1. 比如说有内核空间,用户的代码是不能去读写这个区域的。这里面是来放我们操作系统的内核的,我把操作系统跑起来的时候,它有一个内核,而这里面是它内核运行所需要的空间,用户代码不得访问!

2. 这块区域专门留给操作系统,任何人都不能访问。我们自己写的程序压根儿就没有权限去访问的,是留给操作系统内核去使用的。

栈区

1. 栈区:在执行函数时,函数内局部变量(数组啊,指针变量啊,整型啊等等等)的存储单元都可以在栈上搭建。当函数运行结束时,函数栈帧销毁,这些存储单元也自动被释放

2. 栈内存的分配运算内置于处理器的指令集中,效率非常高,但是分配的内存容量有限

3. 栈区主要存放运行函数分配的局部变量,函数参数,返回数据,返回地址等,总而言之都是临时的变量

内存映射段 

暂时不用去了解它 

堆区

1. 堆区:堆区里面空间一般由程序员去分配,去释放。如果程序员不释放,程序运行结束的时候(一定是要在结束的时候,如果没结束就回收不了,内存泄露)可能由操作系统回收。分配方式类似于链表(链表现在也不懂不要紧)

2. malloc , calloc ,realloc ,free 专属操作区域

数据段/静态区

1. 数据段:也就是经常叫的静态区,里面是用来存放全局变量,静态数据由static修饰的那些局部变量

2. 由操作系统的程序结束之后回收

代码段

1. 代码段:.......常量字符串就放在只读数据区
2. 在数据段下面还有代码段,在这边放一些可执行的代码,只读的常量啊,字符串常量就是放在这里边的。


1. 全局变量、静态变量、被static修饰的局部变量是放在内存里面的数据段的。
2. 在函数里面创建的数组啊,指针变量啊,整型啊等等,这些普通的局部变量是在栈区上开辟空间的。
3. 而malloc, calloc, realloc,开辟的空间都是在内存的堆里面开辟的。
4. 而常量字符串等常量数据是放在内存里面的只读数据区的

再次回顾static关键字

1. 实际上普通的局部变量是在栈区上分配空间的
2. 栈区的特点是在上面创建的变量出了作用域就会销毁。
3. 但是当被static修饰的时候,这个局部变量就不是放在栈区上了,而是放在数据段(静态区)
4. 数据段的特点就是再上面创建的变量,直到程序结束之后才会被销毁,被操作系统回收。生命周期变长了

柔性数组 

1. 如果在结构体当中,它允许里面最后一个成员未知大小的数组,这个成员(一个成员)就叫做柔性数组成员。
2. 柔性数组必须是结构体当中最后一个成员,如果是一个数组的话,可以不指定元素个数。
3. 在结构当中,如果最后一个成员是数组,现在我们已经知道是柔性数组了,那么在代码里面写的时候,这个[ ]里面可以什么都不写,也可以在里面写一个0,这两种写法代表的意思是一样的:数组大小未知,我没有指定大小。 

 

柔性数组特点

1. 结构体当中柔性数组成员之前必须至少拥有一个其他成员。
2. sizeof(结构体),返回的大小不包含柔性数组的内存。
3. 用malloc函数为包含柔性数组成员的结构体在堆区中开辟内存空间,分配的内存要大于结构体的大小(不能再malloc函数括号里面直接sizeof结构体就拍拍屁股走人了),比方说我希望在最后一个数组成员里面有10个整型的话:就malloc(sizeof(结构体)+10×sizeof (int))。这是为了适应柔性数组的预期大小,在强制类型转化的时候,整个被开辟的堆区空间还是相当于属于结构体的void*就强制类型转化为结构体指针就OK了。接下来这个结构体指针就正常访问成员就OK了。
4. 柔性数组也是一个数组罢了,这个空间不是一次性开辟好的,而是通过malloc的方式在sizeof(结构体)后面+的时候自己指定的,你想要多大你就自己指定。
5. 当你觉得空间不够的时候,就增容用realloc,括号里面还是与malloc一样的。
sizeof(结构体)+自己新指定元素个数。
sizeof(包含柔性数组成员的结构体)计算的时候,最末一个柔性数组成员是不包括进去的 

struct class
{
	char classname[20];
	int score[];
};
int main()
{
	char name[20] = { 0 };
	printf("请输入班级名称:");
	gets(name);
	//
	printf("请输入班级考试人数:");
	int number = 0;
	scanf("%d", &number);
	//
	struct class* p = (struct class*)malloc(sizeof(struct class) + number * sizeof(int));
	if (p == NULL)
	{
		perror("malloc");
	}
	strcpy(p->classname, name);
	//
	int i = 0;
	for (i = 0; i < number; i++)
	{
		printf("请输入第%d个考生的成绩:", i + 1);
		scanf("%d", &(p->score[i]));
	}
	printf("\n");
	//
	printf("是否需要补录(Y/N)");
	int STU_INC = 0;
	getchar();
	char Judge = 0;
	scanf("%c", &Judge);
	if (Judge == 'Y')
	{
		printf("请输入要增加的学生人数:");
		scanf("%d",&STU_INC);
		struct class* p1 = (struct class*)realloc(p, sizeof(struct class) + (number + STU_INC) * sizeof(int));
		if (p1 != NULL)
		{
			p = p1;
		}
		for (i = number; i < number + STU_INC; i++)
		{
			printf("请输入第%d个考生的成绩:", i + 1);
			scanf("%d", &(p->score[i]));
		}
		printf("\n");
	}
	printf("%s班的考试成绩如下:", p->classname);
	for (i = 0; i < number + STU_INC; i++)
	{
		printf("%d ", p->score[i]);
	}
	printf("\n");
	free(p);
	p = NULL;
	return 0;
}

柔性数组的优点:

1. 第一个好处是:方便内存释放
如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给
用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你
不能指望用户来发现这个事。所以,如果我们把结构体的内存以及其成员要的内存一次性分配好
了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。
2. 第二个好处是:这样有利于访问速度.
连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实,我个人觉得也没多高了,反正
你跑不了要用做偏移量的加法来寻址) 

柔性数组成员在用动态开辟内存的时候,参数与sizeof(这时候计算结构体大小不把柔性数组成员列入在内,它等会单独加上去)使用的时候需要稍微处理,但是返回的一个结构体指针指向的结构体还是包含着末尾的柔性数组成员的

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

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

相关文章

【论文精选】TPAMI2020 - PFENet_先验引导的特征富集网络_小样本语义分割

【论文精选】TPAMI2020 - PFENet_先验引导的特征富集网络_小样本语义分割 精选精析&#xff1a; 【论文原文】&#xff1a; Prior Guided Feature Enrichment Network for Few-Shot Segmentation (当前引用次数&#xff1a;184) 【论文代码】&#xff1a; https://github.co…

【爪洼岛冒险记】第5站:多图解,超详细讲解Java中的数组、二维数组--建议收藏

&#x1f331;博主简介&#xff1a;是瑶瑶子啦&#xff0c;一名大一计科生&#xff0c;目前在努力学习JavaSE。热爱写博客~正在努力成为一个厉害的开发程序媛&#xff01; &#x1f4dc;所属专栏&#xff1a;爪洼岛冒险记【从小白到大佬之路】 ✈往期博文回顾: 【爪洼岛冒险记】…

【GD32F427开发板试用】RT-THREAD标准版 移植使用

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;打盹的消防车 前言&#xff1a; 无意在微信看到了GD做活动&#xff0c;想到了第一时间体验一下&#xff0c;搭配RT-THREAD&#xff0c;也很方…

【Java】【系列篇】【Spring源码解析】【三】【体系】【BeanDefinition体系】

整体结构图 1. BeanDefinition 用于保存 Bean 的相关信息&#xff0c;包括属性、构造方法参数、依赖的 Bean 名称及是否单例、延迟加载等&#xff0c; 它是实例化 Bean 的原材料&#xff0c;Spring 就是根据 BeanDefinition 中的信息实例化 Bean。 2. 我们获取对象的方式一般有…

AioDnsBrute:一款功能强大的异步DNS爆破工具

关于AioDnsBrute AioDnsBrute是一款功能强大的异步DNS爆破工具&#xff0c;该工具基于Python 3.5开发&#xff0c;并使用了asyncio库以实现针对目标域名的异步爆破。 该工具的运行速度非常快&#xff0c;在一台小型VPS主机上&#xff0c;可以实现在1.5-2分钟之内处理大约10万…

【计数DP】P4933 大师

这道是洛谷官方题单的简单DP为啥我放上来呢&#xff0c;因为我因为各种各样的细节原因没做出来感觉计数的DP有点点难&#xff0c;得多写了P4933 大师 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)题意&#xff1a;思路&#xff1a;第一眼肯定是设dp[i][j]为以a[i]为结尾&…

8. 【Redisson源码】分布式信号量RSemaphore

目录 一、RSemaphore的使用 二、RSemaphore设置许可数量 三、RSemaphore的加锁流程 四、RSemaphore的解锁流程 【本篇文章基于redisson-3.17.6版本源码进行分析】 基于Redis的Redisson的分布式信号量RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。 …

从首个「数实融合」公益球场,看元宇宙奏响创新「三重奏」

作者 | 曾响铃 文 | 响铃说 2022年的元宇宙&#xff0c;一半是海水&#xff0c;一半是火焰。 一边是刮起元宇宙热潮的Roblox股价跌去大半&#xff0c;Meta也因元宇宙亏损深陷泥潭。另一边&#xff0c;经过2021年元宇宙概念落地和普及&#xff0c;2022年却也是元宇宙相关产业…

分享86个PHP源码,总有一款适合您

PHP源码 分享86个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 86个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1fsoGdkr_-wZUaJvVMOlihQ?pwdlhyo 提取码&#xff…

Java 泛型是什么?一文带你吃透泛型

文章目录1. Java 泛型2. 泛型类3. 泛型接口4. 泛型方法5. 泛型集合Java编程基础教程系列1. Java 泛型 Java 泛型是 JDK1.5 中引入的一个新特性&#xff0c;其本质是参数化类型&#xff0c;把类型作为参数传递。其主要的形式有泛型类&#xff0c;泛型接口和泛型方法。泛型概念的…

sqoop安装(linux)

一、前期准备安装好hadoop伪分布安装好MySQL下载sqoop压缩文件实验环境&#xff1a;实验环境版本CentOS 6.5MySQL5.7.37hadoop3.3.0sqoop1.4.7sqoop1.4.7 下载链接&#xff1a;https://pan.baidu.com/s/16AUdtBmSv7OG2PTyA1XcgQ?pwdqu7lmysql驱动包下载地址&#xff1a;https:…

易于设置的倒计时页面Easy countdown

今天开始放假了 什么是 Easy countdown &#xff1f; Easy countdown 是一个易于设置的倒计时页面。可以设置为倒计时或计时器。 先看看官方提供的动图 安装 在群晖上以 Docker 方式安装。 在注册表中搜索 easy-countdown &#xff0c;选择第一个 yooooomi/easy-countdown&am…

【前端学习指南】基础开发环境搭建

&#x1f36d; Hello&#xff0c;我是爱吃糖的范同学 邻近春节&#xff0c;虽然学校的事情已经处理的差不多了&#xff0c;又开始要忙着找实习......时间安排上还是有很多问题&#xff0c;希望大家多多包涵&#xff0c;我已经加班加点在写作了&#x1f602;&#x1f602;&…

高盐废水如何处理,离子交换树脂在高盐废水中的应用

什么是高盐废水&#xff1f; 高盐废水是工业废水中较常见的一种&#xff0c;它是指总含盐量(以NaCl计&#xff09;至少为1%的废水&#xff0c;属于难处理的废水之一。 高盐废水中的总溶解固体物TDS&#xff0c;多在10000-25000mg/L&#xff0c;含盐成分复杂&#xff0c;有Na、…

vue3中echarts组件的最佳封装形式

项目中经常用到echarts&#xff0c;不做封装直接拿来使用也行&#xff0c;但不可避免要写很多重复的配置代码&#xff0c;封装稍不注意又会过度封装&#xff0c;丢失了扩展性和可读性。始终没有找到一个好的实践&#xff0c;偶然看到一篇文章&#xff0c;给了灵感。找到了一个目…

【数据结构】并查集

目录1.概述2.代码实现3.应用本文参考&#xff1a; LABULADONG 的算法网站 《数据结构教程》&#xff08;第 5 版&#xff09;李春葆主编 1.概述 &#xff08;1&#xff09;并查集支持查找一个元素所属的集合以及两个元素各自所属的集合的合并运算。当给出两个元素的一个无序对…

氨氮废水如何处理,离子交换树脂在氨氮废水中的应用点

近几年来重点污染源考核结果及地表水监测结果表明&#xff0c;氨氮超标现象仍较严重。认清氨氮的来源&#xff0c;了解其危害&#xff0c;采取有效的处理措施成为保护水环境不被氨氮污染的必要环节。 北京科海思科技有限公司利用离子交换特种树脂可以做到有针对性的氨氮的去除…

vue2之生命周期

生命周期 生命周期是指组件从创建&#xff0c;运行到销毁的阶段。而生命周期函数&#xff08;也叫生命周期钩子&#xff09;是vue在关键的时刻帮我们调用的一些特殊名称的函数&#xff0c;会根据生命周期的阶段&#xff0c;依次执行。 beforeCreatecreatedbeforeMountmountedb…

基本的SELECT语句与显示表结构

文章目录基本的SELECT语句SELECT...SELECT ... FROM列的别名去除重复行空值参与运算着重号查询常数(查询同时添加常数字段)显示表结构过滤数据练习题基本的SELECT语句 SELECT… SELECT 11, 22;# 直接这样写相当于下面这句 SELECT 11, 22 FROM DUAL; # 这里DUAL&#xff1a;伪…

【云攻防系列】从攻击者视角聊聊K8S集群安全(上)

前言 作为云原生管理与编排系统的代表&#xff0c;Kubernetes&#xff08;简称K8S&#xff09;正受到越来越多的关注&#xff0c;有报告[1]显示&#xff0c;96% 的组织正在使用或评估 K8S&#xff0c;其在生产环境下的市场占有率可见一斑。 K8S 的功能十分强大&#xff0c;其…