动态内存管理(malloc,calloc,realloc,free)+经典笔试题

2024-06-07 15:36

本文主要是介绍动态内存管理(malloc,calloc,realloc,free)+经典笔试题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

动态内存管理

  • 一. malloc 和 free
    • 1. malloc
    • 2. free
  • 二. calloc
  • 三. realloc
  • 四.动态内存的错误
    • 1.对NULL指针的解引用操作
    • 2.对动态开辟空间的越界访问
    • 3.对非动态开辟内存使用free释放
    • 4.使用free释放一块动态开辟内存的一部分
    • 5.对同一块动态内存多次释放
    • 6.动态开辟内存忘记释放(内存泄漏)
  • 五.动态内存经典笔试题分析

前言:

  1. 当我们要开辟一块连续的内存空间时,我们第一时间想到的可能是数组。但是一但开辟了数组,数组的大小就确定了,无法调整数组的大小。
  2. 有时候我们需要的空间大小在程序运行的时候才能知道,那数组的编译时开辟空间的方式就不能满足了。
  3. 于是动态内存开辟函数(malloc,calloc,realloc,free)应运而生,下文带您一一了解其中的奥秘。

一. malloc 和 free

1. malloc

void* malloc(size_t size);

解释:在堆区中开辟一块大小为 size 个字节的空间,返回指向这块空间的起始地址(泛型指针void*)

因为这块空间存放的数据类型不知(由程序员自己确定),所以用泛型指针接收该地址,在使用的时候记得养成一个好习惯:强制类型转换为自己需要的数据类型。

  1. 如果开辟成功,则返回一个指向开辟好空间的指针。

  2. 如果开辟失败,则返回一个 NULL 指针,因此malloc的返回值一定要做检查。

  3. 返回值的类型是 void* ,所以malloc函数并不知道开辟空间的类型,具体在使用的时候程序员自己来决定。

  4. 如果参数 size 为0,malloc的行为是标准是未定义的,取决于编译器。

2. free

void free(void* ptr);

解释:free是用来对动态内存的释放和回收的。free 对指针 ptr 指向的内容释放掉,但是指针仍然指向这块空间,若后面不再使用,及时将 ptr 置为 NULL,否则产生野指针。

  1. 如果参数 ptr 指向的空间不是动态开辟的,那free函数的行为是未定义的。

  2. 如果参数 ptr 是NULL指针,则函数什么事都不做。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{//在堆区申请10个整形空间int* p=(int*)malloc(10*sizeof(int));if (p == NULL){//开辟空间失败perror("malloc");//打印错误信息//printf("%s\n", strerror(errno));//也是打印错误信息return 1;}//使用这块空间int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;}//打印这块空间for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放这块空间free(p);//将这块空间还给了操作系统,我们已经没有权限再使用这块空间了//但是p仍然指向那块空间p = NULL;//若不将p置为NULL,那么p就是野指针return 0;
}

在这里插入图片描述

总结:

  1. 动态内存开辟的函数头文件都是 stdlib.h。
  2. 如果不释放的话,程序结束的时候也会被操作系统自动释放。
  3. 但是为了防止内存泄漏,将其置为NULL。这是一个好习惯。

二. calloc

void* calloc(size_t num, size_t size);

解释:在堆区中开辟一块大小为 num * size 个字节的空间,返回指向这块空间的起始地址,其中 num 为数据的个数,size 为单个数据的字节数,同时把申请的空间的每个字节初始化为全为0。


#include<stdio.h>
#include<stdlib.h>
int main()
{//在堆区申请10个整形空间int* p = (int*)calloc(10, sizeof(int));if (p == NULL){perror("calloc");return 1;}//使用空间int i = 0;for (i = 0; i < 10; i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

在这里插入图片描述

三. realloc

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

解释:调整动态内存开辟的空间,ptr 是那块空间的起始地址,size 是调整后的那块空间的字节的个数,返回指向这块空间的起始地址。

#include<stdio.h>
#include<stdlib.h>
int main()
{//在堆区申请10个整形空间int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//调整空间——变成20个整形空间int* ptr = (int*)realloc(p, 20 * sizeof(int));//注意:要用新的指针来接收if (ptr != NULL){p = ptr;}else{//开辟失败return 1;}int i = 0;for (i = 0; i < 20; i++){*(p + i) = i + 1;}for (i = 0; i < 20; i++){printf("%d ", *(p + i));}//释放free(p);p = NULL;return 0;
}

在这里插入图片描述

注意:也许有些人有疑问为什么要用新的指针接收返回的地址,直接用原来的指针接收不行吗?答案是不行的,在realloc调整动态内存开辟的空间有3中情况,代码如下:

int main()
{int* p = (int*)malloc(10);//...if (p != NULL){int* ptr = (int*)realloc(p, 20);//...}return 0;
}

情况1:

  1. 开辟的空间后面有足够且连续的空间,只需返回空间的起始地址即可。
    在这里插入图片描述

情况2:

  1. 如果后续的空间不够,realloc 函数直接在堆区找一块新的满足大小的空间,将旧的地址,拷贝到新的地址。
  2. 自动释放旧的地址指向的空间,不需要手动 free,返回新的空间的起始地址。

在这里插入图片描述

情况3:

  1. 堆区已经没有满足情况的连续空间了,返回NULL。
    在这里插入图片描述

realloc函数也能开辟空间,代码如下:

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)realloc(NULL, 10 * sizeof(int));//等价于malloc(40)if (p == NULL){//...}return 0;
}

四.动态内存的错误

1.对NULL指针的解引用操作

  1. 如果将一个空指针(NULL)进行解引用操作,程序会遇到未定义行为,会导致程序崩溃。这是因为空指针并不指向任何有效的内存地址,尝试解引用它会导致访问非法内存,从而导致程序崩溃。
  2. 因此,在解引用指针之前,应该始终先检查指针是否为空。

错误代码如下:

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));//可能会开辟失败导致,p等于NULL//if (p == NULL)//{//	  perror("malloc");//	  return 1;//}//使用int i = 0;for (i = 0; i < 10; i++){*(p + i) = i + 1;//如果p等于NULL,对其进行解引用操作,程序会崩溃}free(p);p = NULL;return 0;
}

2.对动态开辟空间的越界访问

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//使用int i = 0;for (i = 0; i < 40; i++)//越界访问,程序崩溃{*(p + i) = i + 1;}free(p);p = NULL;return 0;
}

3.对非动态开辟内存使用free释放

#include<stdio.h>
int main()
{int a = 10;int* p = &a;//...free(p);//程序崩溃p = NULL;return 0;
}

4.使用free释放一块动态开辟内存的一部分

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//使用int i = 0;for (i = 0; i < 5; i++){*p = i;p++;//修改了指针p}free(p);//free释放一块动态开辟内存的一部分,程序崩溃p = NULL;return 0;
}

5.对同一块动态内存多次释放

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//使用free(p);//p为野指针//...free(p);//对同一块动态内存多次释放,程序崩溃p = NULL;return 0;
}

6.动态开辟内存忘记释放(内存泄漏)

  • 内存泄漏:在程序执行过程中,动态分配的内存空间在程序不再需要时没有被正确释放的情况。这会导致程序在运行过程中持续耗费内存空间而不释放,最终可能导致系统性能下降,甚至导致程序崩溃。
#include<stdio.h>
#include<stdlib.h>
void test()
{int flag = 1;int* p = (int*)malloc(10 * sizeof(int));if (p == NULL)return;//使用if (flag)return;//未释放,函数提前结束,就找不到那块空间,导致内存泄漏free(p);p = NULL;
}
int main()
{test();//......//只有程序结束了,空间才被释放return 0;
}
  1. 在一些服务器上(腾讯,阿里…),可能7*7=49天一直在运行,若一直申请内存而不释放,内存迟早有一天会耗尽的,这会造成巨大损失。
  2. 动态内存管理是一把双刃剑:提供灵活的内存管理方式,但是会带来风险。
  3. 切记:动态开辟的空间一定要释放,并且正确释放。

五.动态内存经典笔试题分析

例题1:

在这里插入图片描述
解决办法:

  1. 传递 str 的地址通过地址修改 str ,同时可以释放动态内存开辟的空间。
  2. 返回动态内存开辟的空间的地址,可以释放动态内存开辟的空间。
    在这里插入图片描述

例题2:

在这里插入图片描述

例题3:

在这里插入图片描述

解决办法:
在这里插入图片描述

创作不易,如果能帮到你的话能赏个三连吗?感谢啦!!!

这篇关于动态内存管理(malloc,calloc,realloc,free)+经典笔试题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1039579

相关文章

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

软考系统规划与管理师考试证书含金量高吗?

2024年软考系统规划与管理师考试报名时间节点: 报名时间:2024年上半年软考将于3月中旬陆续开始报名 考试时间:上半年5月25日到28日,下半年11月9日到12日 分数线:所有科目成绩均须达到45分以上(包括45分)方可通过考试 成绩查询:可在“中国计算机技术职业资格网”上查询软考成绩 出成绩时间:预计在11月左右 证书领取时间:一般在考试成绩公布后3~4个月,各地领取时间有所不同

安全管理体系化的智慧油站开源了。

AI视频监控平台简介 AI视频监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上进行简单的操作,就可以实现全视频的接入及布控。摄像头管理模块用于多种终端设备、智能设备的接入及管理。平台支持包括摄像头等终端感知设备接入,为整个平台提

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Sentinel 高可用流量管理框架

Sentinel 是面向分布式服务架构的高可用流量防护组件,主要以流量为切入点,从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 Sentinel 具有以下特性: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应

HotSpot虚拟机的经典垃圾收集器

读《深入理解Java虚拟机》第三版笔记。 关系 Serial、ParNew、Parallel Scavenge、Parallel Old、Serial Old(MSC)、Concurrent Mark Sweep (CMS)、Garbage First(G1)收集器。 如图: 1、Serial 和 Serial Old 收集器 2、ParNew 收集器 3、Parallel Sc

STL经典案例(四)——实验室预约综合管理系统(项目涉及知识点很全面,内容有点多,耐心看完会有收获的!)

项目干货满满,内容有点过多,看起来可能会有点卡。系统提示读完超过俩小时,建议分多篇发布,我觉得分篇就不完整了,失去了这个项目的灵魂 一、需求分析 高校实验室预约管理系统包括三种不同身份:管理员、实验室教师、学生 管理员:给学生和实验室教师创建账号并分发 实验室教师:审核学生的预约申请 学生:申请使用实验室 高校实验室包括:超景深实验室(可容纳10人)、大数据实验室(可容纳20人)、物联网实验

NGINX轻松管理10万长连接 --- 基于2GB内存的CentOS 6.5 x86-64

转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=190176&id=4234854 一 前言 当管理大量连接时,特别是只有少量活跃连接,NGINX有比较好的CPU和RAM利用率,如今是多终端保持在线的时代,更能让NGINX发挥这个优点。本文做一个简单测试,NGINX在一个普通PC虚拟机上维护100k的HTTP

PMBOK® 第六版 规划进度管理

目录 读后感—PMBOK第六版 目录 规划进度管理主要关注为整个项目期间的进度管理提供指南和方向。以下是两个案例,展示了进度管理中的复杂性和潜在的冲突: 案例一:近期,一个长期合作的客户因政策要求,急需我们为多家医院升级一个小功能。在这个过程中出现了三个主要问题: 在双方确认接口协议后,客户私自修改接口并未通知我们,直到催进度时才发现这个问题关于UI设计的部分,后台开发人员未将其传递给

PHP原理之内存管理中难懂的几个点

PHP的内存管理, 分为俩大部分, 第一部分是PHP自身的内存管理, 这部分主要的内容就是引用计数, 写时复制, 等等面向应用的层面的管理. 而第二部分就是今天我要介绍的, zend_alloc中描写的关于PHP自身的内存管理, 包括它是如何管理可用内存, 如何分配内存等. 另外, 为什么要写这个呢, 因为之前并没有任何资料来介绍PHP内存管理中使用的策略, 数据结构, 或者算法. 而在我们