【C语言__动态内存管理__复习篇6】

目录

前言

一、动态内存管理

二、动态内存函数

        2.1 malloc 

        2.2 free

        2.3 calloc

        2.4 realloc

三、动态内存常见的6个使用错误

        3.1 接收malloc/calloc返回的参数后未及时检查是否为NULL

        3.2 越界访问动态内存空间

        3.3 对非动态开辟的内存使用free释放

        3.4 使用free只释放了动态内存的一部分

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

        3.6 忘记释放动态内存造成内存泄露

四、动态内存经典笔试题

        4.1 题目1

        4.2 题目2

        4.3 题目3

        4.4 题目4

五、柔性数组

六、C/C++中程序内存区域划分


前言

本篇主要讨论以下问题:

动态内存基础知识点:

1. 为什么会出现动态内存的概念

2. 与动态内存有关的4个函数它们分别的作用、函数的参数、返回类型以及要注意的地方是什么

3. 理解动态开辟的内存是在堆区开辟空间的,如果不主动使用free释放开辟的动态内存空间,那么只能等程序结束后由操作系统释放了。(不可取,一定要记得自己用free去释放)

动态内存避坑指南:

4. 动态内存开辟和使用过程中常见有哪6大错误

5. 动态内存经典笔试题,找出代码中错误的点

柔性数组:

6. 怎样的数组才能被称为柔性数组,含柔性数组的结构体如何开辟内存空间,如何找到结构体变量的成员变量的

了解内存有哪几个重要的区域:

7.内存有哪几个重要的区域

 一、动态内存管理

1. 在没有学习动态内存管理前 ,我们知道的为变量开辟内存的方式有两种:① 一次申请一个变量的空间 ② 一次申请一块连续的空间,但这两种申请内存空间的方式有一个明显的缺点,就是申请的空间大小一旦确定就无法更改了,于是C语言中引入了动态内存的概念,我们可以利用4个与动态内存有关的函数,实现动态开辟内存空间的效果。

二、动态内存函数

1. 4个与动态内存有关的函数所需的头文件都是<stdlib.h>

2.1 malloc 

1. malloc函数的作用:开辟size字节的动态内存空间。

2. 语法形式:void* malloc (size_t size)

    size_t size:表示向堆区一次性申请几个比特位的空间

    void*:返回类型之所以是void*(void*可接收任意类型的地址)是因为malloc只知道向内存的堆区申请几个字节的空间,并不知道将会存放什么类型的数据,但是程序员是知道的,所以在用malloc开辟动态内存后,一般会立即强制类型转换,直接用其他类型的指针变量接收

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

    如果malloc开辟内存失败,则返回一个NULL指针,因此在用指针接收malloc返回的地址后,需要检查指针内的地址是否为NULL

4. 如果malloc函数的参数size为0,这是标准未定义的,完全取决于编译器如何处理。

#include <stdio.h>
#include <stdlib.h>
int main()
{//开辟动态内存int* ps = (int*)malloc(10 * sizeof(int));//强制转换后再接收if (ps == NULL)//判断是否开辟失败{perror("malloc");return 1;//异常返回,返回非0}return 0;
}

2.2 free

1. free函数的作用:用于回收malloc/calloc/realloc开辟的动态内存空间

2. 语法形式:void free (void* ptr)

    void* ptr:表示指向malloc/calloc/realloc开辟的动态内存空间的指针。

3. free函数只是回收了ptr所指向的动态内存空间,但此时ptr中仍然存放着指向刚刚已被回收的动态内存空间的地址,因此在使用free函数后,应立即在后面一行中写上将ptr置NULL的操作,防止ptr野指针被使用。

4. 如果free函数的参数 ptr 指向的空间不是动态开辟的内存空间,这是标准未定义的,完全取决于编译器如何处理。

    如果free函数的参数是NULL,则free函数将什么都不会做。

#include <stdio.h>
#include <stdlib.h>
int main()
{//开辟动态内存int* ps = (int*)malloc(10 * sizeof(int));//强制转换后再接收if (ps == NULL)//判断是否开辟失败{perror("malloc");return 1;//异常返回,返回非0}//使用动态内存//释放动态内存free(ps);ps = NULL;return 0;
}

2.3 calloc

1. calloc函数的作用:为num个大小为size字节的元素开辟一块动态内存空间, 与malloc不同的是calloc会对开辟的空间每个字节都初始化为0

2. 语法形式:void* calloc (size_t num, size_t size)

    size_t mun:表示要分配的元素个数。

    size_t size:表示每个元素的大小,单位是比特位

    void*:和malloc函数的原因一致,在用calloc开辟动态内存后,一般会立即强制类型转换,直接用其他类型的指针变量接收

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

    如果calloc开辟内存失败,则返回一个NULL指针,因此在用指针接收malloc返回的地址后,需要检查指针内的地址是否为NULL

#include <stdio.h>
#include <stdlib.h>
int main()
{//开辟动态内存int* ps = (int*)calloc(10, sizeof(int));//强制转换后再接收if (ps == NULL)//判断是否开辟失败{perror("calloc");return 1;//异常返回,返回非0}//使用动态内存,看看calloc默认初始化的值for (int i = 0; i < 10; i++){printf("%d ", *(ps + i));//屏幕上打印出了10个0}//释放动态内存free(ps);ps = NULL;return 0;
}

2.4 realloc

1. realloc函数的作用:调整用malloc/calloc/realloc开辟过的动态内存空间大小

2. 语法形式:void* realloc (void* ptr, size_t size)

    void* ptr:表示指向malloc/calloc/realloc开辟的动态内存空间的指针。

    size_t size:表示想要重新调整到的动态内存空间的大小,单位是比特位

    void*:和malloc函数的原因一致,在用calloc开辟动态内存后,一般会立即强制类型转换,直接用其他类型的指针变量接收

3. 如果realloc调整内存大小成功,则返回非NULL指针。

    如果realloc调整内存大小失败,则返回一个NULL指针,此时如果直接用ptr接收realloc返回的地址,会使得ptr连原本的动态内存空间都无法找到,也无法将原本的动态内存空间释放,所以在用realloc调整动态内存空间后,不会直接用ptr接收,而是会建立一个临时指针去接收,临时的指针接收后,判断如果临时指针不为NULL,则把临时指针内的地址赋值给ptr,并把临时的指针赋值为NULL;如果为NULL,则进行报错

4. 关于realloc函数返回的地址是什么的问题:

    情况一:原有空间之后有⾜够⼤的空间可以扩充,此时要扩展内存就直接会在原有内存之后直接追加空间,原来空间的数据不发⽣变化,realloc返回的地址值与ptr内的值一致。

    情况二:原有空间之后没有⾜够⼤的空间可以扩充,扩展的⽅法是,在堆空间上另找⼀个合适⼤⼩的连续空间来使⽤,同时把旧数据拷贝到新的空间,并释放旧的空间,这样函数返回的是⼀个新的内存地址。

5. realloc函数的隐藏技能:如果realloc函数的参数void* ptr部分传参为NULL,效果等同于malloc函数。

#include <stdio.h>
#include <stdlib.h>
int main()
{//开辟动态空间int* ps = (int*)malloc(10 * sizeof(int));if (ps == NULL){perror("malloc");return 1;}//调整动态空间int* ps2 = (int*)realloc(ps, 20 * sizeof(int));if (ps2 != NULL){ps = ps2;ps2 = NULL;}else{perror("realloc");return 1;}//释放动态内存free(ps);ps = NULL;return 0;
}

三、动态内存常见的6个使用错误

3.1 接收malloc/calloc返回的参数后未及时检查是否为NULL

#include <stdio.h>
#include <stdlib.h>
int main()
{int* p = (int*)malloc(10 * sizeof(int));*p = 20;//errreturn 0;
}

3.2 越界访问动态内存空间

#include <stdio.h>
#include <stdlib.h>
int main()
{//开辟动态内存空间int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//使用for (int i = 0; i < 12; i++){*(p + i) = i;//err}//释放动态内存空间free(p);p = NULL;return 0;
}

3.3 对非动态开辟的内存使用free释放

#include <stdio.h>
#include <stdlib.h>
int main()
{int a = 10;int* pa = &a;free(pa);//errpa = NULL;return 0;
}

3.4 使用free只释放了动态内存的一部分

#include <stdio.h>
#include <stdlib.h>
int main()
{//开辟动态内存空间int* p = (int*)malloc(10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//使用动态空间for (int i = 0; i < 5; i++){*p = i;p++;}//释放动态内存空间free(p);//errp = NULL;return 0;
}

3.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 = NULL;//释放动态内存空间free(p);//errp = NULL;return 0;
}

3.6 忘记释放动态内存造成内存泄露

#include <stdio.h>
#include <stdlib.h>void Test()
{int* p = (int*)malloc(100);if (p == NULL){perror("malloc");return 1;}else{*p = 20;}//err,未释放动态内存
}int main()
{test();return 0;
}

四、动态内存经典笔试题

4.1 题目1

题目分析:代码的目的是想让字符串拷贝到动态开辟的内存中。

错误分析:① 采用了传值调用,改变形参,实参不受影响。

                  ② NULL不能被使用。

                  ③ 没有free动态内存。

#include <stdio.h>
#include <stdlib.h>void GetMemory(char* p)
{p = (char*)malloc(100);
}void Test(void)
{char* str = NULL;GetMemory(str);//传值调用strcpy(str, "hello world");//str==NULL,errprintf(str);
}int main()
{Test();return 0;
}
//修改代码
#include <stdio.h>
#include <stdlib.h>void GetMemory(char** p)
{*p = (char*)malloc(100);if (*p == NULL)//{perror("malloc");}
}void Test(void)
{char* str = NULL;GetMemory(&str);//strcpy(str, "hello world");printf(str);free(str);//str = NULL;//
}int main()
{Test();return 0;
}

4.2 题目2

错误分析:① 自定义函数返回了局部变量的地址

#include <stdio.h>
#include <stdlib.h>char* GetMemory(void)
{char p[] = "hello world";//err//static char p[] = "hello world";//修改代码:将上一行替换return p;
}void Test(void)
{char* str = NULL;str = GetMemory();printf(str);
}int main()
{Test();return 0;
}

4.3 题目3

错误分析:① 没有判断malloc是否返回的NULL

                  ② 没有用free释放动态内存空间

#include <stdio.h>
#include <stdlib.h>void GetMemory(char** p, int num)
{*p = (char*)malloc(num);
}void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);
}int main()
{Test();return 0;
}
//修改代码
#include <stdio.h>
#include <stdlib.h>void GetMemory(char** p, int num)
{*p = (char*)malloc(num);if (*p == NULL)//{perror("malloc");}
}void Test(void)
{char* str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);//free(str);//str = NULL;
}int main()
{Test();return 0;
}

4.4 题目4

错误分析:① 没有判断malloc是否返回的NULL

                  ② free后没有及时将str置NULL

#include <stdio.h>
#include <stdlib.h>void Test(void)
{char* str = (char*)malloc(100);strcpy(str, "hello");free(str);if (str != NULL){strcpy(str, "world");printf("str");}
}int main()
{Test();return 0;
}
//修改代码
#include <stdio.h>
#include <stdlib.h>void Test(void)
{char* str = (char*)malloc(100);if (str == NULL){perror("malloc");return 1;}strcpy(str, "hello");free(str);str = NULL;//无意义代码if (str != NULL){strcpy(str, "world");printf("str");}
}int main()
{Test();return 0;
}

五、柔性数组

1. C99中,结构体至少2个成员变量,若最后一个成员变量是一个未知大小的数组,则这个数组叫做柔性数组。( 未知大小的数组写法:① int arr[]; ② int arr[0];

2. 柔性数组的特点:

① 结构体中柔性数组成员前必须至少有1个其他成员变量。

② sizeof计算含柔性数组的结构体大小时,计算的结果不包含柔性数组的大小,因为柔性数组的大小是未知的。

③ 包含柔性数组的结构体在申请内存空间时要采用动态内存开辟的方式,并且开辟的动态内存空间应大于结构体内存的大小,以适应柔性数组的预期大小(总之,在创建有柔性数组的结构体变量时,不要采用struct St s;传统的方式,这样创建的结构体变量中柔性数组成员变量是没有大小的,我们应采用

struct St* p = (struct St*) malloc(sizeof(struct St)+10* sizeof(int);类似的方式创建结构体变量,找结构体成员变量时直接用(->)操作符即可,这种写法下相当于不同的结构体指针变量,代表着不同的结构体变量)

3. 使用动态内存为含柔性数组的结构体开辟空间的方式,而不用平常为结构体变量开辟空间的方式的好处:① 方便内存释放 ② 访问数组相对较快 ③ 利于减少内存碎片(动态内存之间未利用到的内存称为内存碎片)

#include <stdio.h>
#include <stdlib.h>struct St
{float a;int arr[0];
};int main()
{struct St* p = (struct St*)malloc(sizeof(struct St) + 10 * sizeof(int));if (p == NULL){perror("malloc");return 1;}//使用scanf("%f", &(p->a));printf("%f", p->a);for (int i = 0; i < 10; i++){scanf("%d", &(p->arr[i]));printf("%d ", p->arr[i]);}//释放free(p);p = NULL;return 0;
}

 六、C/C++中程序内存区域划分

 

本篇文章已完结,谢谢支持!!! 

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

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

相关文章

AI时代,我要如何学习,才能跟上步伐

在21世纪这个被数据驱动的时代&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到我们生活的方方面面。无论是智能手机中的语音助手、在线客服的聊天机器人&#xff0c;还是自动驾驶汽车&#xff0c;AI的应用都在告诉我们一个信息&#xff1a;未来已来。因此&#xff0…

1.微服务介绍

完整的微服务架构图 注册中心 配置中心 服务集群 服务网关 分布式缓存 分布式搜索 数据库集群 消息队列 分布式日志服务 系统监控链路追踪 Jenkins docker k8s 技术栈 微服务治理&#xff1a; 注册发现、远程调用、负载均衡、配置管理、网关路由、系统保护、流量…

企业单位IPTV数字电视直播与点播系统-中国卫通怀来地球站IPTV数字电视直播与点播系统应用浅析

企业单位IPTV数字电视直播与点播系统-中国卫通怀来地球站IPTV数字电视直播与点播系统应用浅析 由北京海特伟业科技有限公司任洪卓发布于2024年4月19日 一、运营商光猫接入企业/单位IPTV数字电视直播与点播系统建设概述 中国卫通怀来地球站&#xff0c;位于怀来县土木镇&#xf…

小球反弹(蓝桥杯)

文章目录 小球反弹【问题描述】答案&#xff1a;1100325199.77解题思路模拟 小球反弹 【问题描述】 有一长方形&#xff0c;长为 343720 单位长度&#xff0c;宽为 233333 单位长度。在其内部左上角顶点有一小球&#xff08;无视其体积&#xff09;&#xff0c;其初速度如图所…

CentOS 7静默安装Oracle 11g(记一次最小化CentOS 7安装Oracle 11g的经历)

# [pdf在线免费转word文档](https://orcc.online/pdf) https://orcc.online/pdf 1.最小化安装CentOS 7后首先设置一下固定IP 可以先查询一下自己的网卡设备的名称&#xff0c;是ens33&#xff0c;所以网卡配置文件名称就是ifcfg-ens33&#xff08;前面的ifcfg-不用管&#xf…

HCIP-Datacom-ARST必选题库_01_ACL【7道题】

一、单选 1.下面是一台路由器的部分配置,关于该配置描述正确的是&#xff1a; 源地址为1.1.1.1的数据包匹配第一条ACL语句rule 0,匹配规则为允许 源地址为1.1.1.3的数据包匹配第三条ACL语句rule 2,匹配规则为拒绝 源地址为1.1.1.4的数据包匹配第四条ACL语句rule 3,匹配规则为允…

【Python】函数(纯干货版)

目录 什么是函数 函数定义 函数的文档说明 局部变量和全局变量 综合案例&#xff1a;模拟实现ATM界面 什么是函数 函数是组织好的&#xff0c;可重复使用的&#xff0c;用于实现特定功能的代码段&#xff0c;将功能封装在函数内&#xff0c;可供随时随地重复利用&#xff…

加固平板电脑加速国产化!应用场景大科普

随着时代的发展&#xff0c;国产化平板电脑已经开始慢慢的实现了&#xff0c;国产芯片、国产操作系统、国产 GPU、国产 GNSS 等众多关键技术的集成与应用。全新的平台&#xff0c;全新的突破&#xff0c;全新的应用&#xff0c;让国产加固型移动计算机系列往前进了一步。那么加…

如何用 AI 工具做数据分析与可视化?

&#xff08;注&#xff1a;本文为小报童精选文章。已订阅小报童或加入知识星球「玉树芝兰」用户请勿重复付费&#xff09; 万字长文&#xff0c;助力你用 AI 提升科研效率。 2024 年 4 月 14 日&#xff0c;应武汉大学信息管理学院的邀请&#xff0c;我和北京大学步一老师给几…

python 头文件怎么写

本文主要以python2为例。首先介绍一下Python头文件的编程风格&#xff0c;然后再给大家详细介绍import部分的基本用法。这两个部分就是Python中头文件的组成模块。 编程风格 #!/usr/bin/env python #在文件头部 ( 第一行 ) 加上 设置 Python 解释器 # -*- coding: utf…

【开源】使用Python+Flask+Mysql快速开发一个用户增删改查系统

项目演示 项目本身很简单&#xff0c;增删改查是几乎所有系统的骨架。正所谓万丈高楼平地起&#xff0c;学会了增删改查&#xff0c;航母就指日可待了&#xff1a;&#xff09;&#xff0c;光速入门&#xff0c;直接看演示图&#xff1a; 项目地址 https://github.com/mudf…

设计模式代码实战-责任链模式

1、问题描述 小明所在的公司请假需要在OA系统上发布申请&#xff0c;整个请求流程包括多个处理者&#xff0c;每个处理者负责处理不同范围的请假天数&#xff0c;如果一个处理者不能处理请求&#xff0c;就会将请求传递给下一个处理者&#xff0c;请你实现责任链模式&#xff…

大华相机C#学习之IDevice类

获取方式 Enumerator.GetDeviceByGigeIP() 通过IP地址获取设备对象。 private void test_Click(object sender, EventArgs e) {devicesEnumerator.EnumerateDevices();device Enumerator.GetDeviceByGigeIP("192.168.0.11"); } 常用属性 DeviceInfo 获取设备的信…

uniapp_微信小程序_预约时间组件的使用

一、官方文档 DatetimePicker 选择器 | uView 2.0 - 全面兼容 nvue 的 uni-app 生态框架 - uni-app UI 框架 (uviewui.com) 二、完成的效果 之前使用的是Calendar 日历 这个太耗性能了&#xff0c;直接页面卡顿&#xff0c;所以就换成以上选择器了 三、代码 <u-datetime-p…

短信登录session-redis

1.流程 1.1 发送验证码 模拟路径 http://127.0.0.1:8080/api/user/code?phone1335566 Request Method:POSTcontroller层 /*** 发送手机验证码*/PostMapping("code")public Result sendCode(RequestParam("phone") String phone, HttpSession session) {…

C#自定义窗体更换皮肤的方法:创建特殊窗体

目录 1.窗体更换皮肤 2.实例 &#xff08;1&#xff09;图片资源管理器Resources.Designer.cs设计 &#xff08;2&#xff09;Form1.Designer.cs设计 &#xff08;3&#xff09;Form1.cs设计 &#xff08;4&#xff09; 生成效果 &#xff08;5&#xff09;一个遗憾 1.窗…

【银角大王——Django课程Day1】

Django框架第一课 安装Django框架方式一&#xff08;命令行的形式创建Django项目&#xff09;方式二&#xff08;适合企业版的pycharm&#xff09;默认文件介绍app文件介绍快速上手我的导包一直爆红是因为我没使用解释器&#xff0c;没导入包&#xff0c;去设置里面导入包即可—…

在 Node.js 中配置代理 IP 采集文章

不说废话&#xff0c;直接上代码&#xff1a; const http require(http); const https require(https);// 之后可以使用 http 或 https 模块发起请求&#xff0c;它们将自动使用配置的代理 // 代理ip&#xff1a;https://www.kuaidaili.com/?refrg3jlsko0ymg const proxy …

OpenHarmony实战开发-如何视频弹幕功能。

介绍 本示例介绍如何使用ohos.danmakuflamemaster和ohos.gsyvideoplayer开发支持视频弹幕的播放器。可以自定义弹幕样式、占据屏幕宽度&#xff0c;发送弹幕&#xff0c;开关弹幕视图。 效果图预览 使用说明 点击播放按钮&#xff0c;进行视频播放&#xff0c;弹幕自动开启点…

【位运算】Leetcode 只出现一次的数字 ||

题目解析 137. 只出现一次的数字 II 算法讲解 nums中要么一个数字出现三次&#xff0c;一个数字出现一次&#xff0c;按照比特位来说只可能出现上面的四种情况&#xff1a; 3n个0 0 或者 3n个0 1 或者 3n个1 0 或者 3n个1 1&#xff0c;它们相加的结果依次是0&#xff0c;…