动态内存管理(c语言)


我们通常开辟空间的方式

int val =20; //大小为4个字节
char arr[10] ={0} //开辟出一块连续的空间且大小为10

但是上面开辟空间方式的特点

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

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

但是以上的方式不能满足所有情况,有时候我们需要空间的大小在程序运行的时候才能知道,

那数组编译的方式就不能满足了,你那就可以使用动态内存

一.堆区,栈区,静态区

1.栈区:

1)遵循自动分配与释放原则

当函数被调用时,函数内的局部变量,函数的参数灯会在栈上分配空间,函数执行完毕后,这些内存会自动被释放

2)后进先出原则

比如说嵌套函数的时候,内层函数的局部变量会先入栈,当内存函数返回后,其局部变量先出栈,外层函数才能继续执行并访问其自身的局部变量

3)空间有限。如果在栈上分配了过多的内存,可能会导致栈溢出,程序出现错误甚至崩溃

2.堆区

动态分配与释放:堆区的内存分配和释放由程序员手动控制,通过函数malloc,calloc,realloc等函数可以在栈上申请空间,使用完毕后,必须通过free函数释放内存,否则会导致内存泄漏,会一直占用系统资源,知道程序借宿

内存空间较大:堆区的内存空间相对栈区来说要大得多,其大小通常只限于计算机的物理内存和虚拟内存的大小。这使得堆区适用于存储大量的数据或动态生成的数据结构,如数组,链表,树等

分配灵活:

根据程序的实际需求动态申请任意大小的内存块

碎片化问题:

由于堆区的内存分配和释放时动态,经过多次申请和释放后,可能会导致内存碎片化

,内存利用率降低

3.静态区:

全局生命周期:

静态区存储的是全局变量和静态变量,这些变量在程序整个生命周期都有效,直到程序结束

初始化规则

未初始化的全局变量和静态变量会自动初始化为0和空指针,初始化的变量就按照初始化的进行

作用域的限制

全局变量作用域是整个程序,可以在如何函数中访问和修改;而静态局部变量的作用域仅限于定义它的函数内部,但它在函数多次调用后会保持其值不变

二.动态内存函数

1.malloc函数

头文件 #include<stdlib.h>

这个函数向内存申请一块连续可用的空间,并指向这块空间的指针

如果开辟成功,则返回一个指向该块空间的指针

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

返回值为void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候由操作者自己决定

函数格式

函数参数的意思:size表示要分配的内存块的大小,单位是字节

void*malloc(size_t size)

2.free函数

头文件 #include<stdlib.h>

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

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

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

void free (void* ptr);

malloc函数和free函数使用如下

#include<string.h>
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
int main()
{//向内存申请空间,且未初始化int* p = (int*)malloc(20);//判断空间是否开辟成功if (p == NULL){//判断错误的原因//strerror函数是用来判断错误的原有printf("%s\n", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i < 5; i++){//这俩种形式等价//*(p + i) = i + 1;//printf("%d ", *(p + i));//p[i] = i + 1;//printf("%d ", p[i]);}//释放free(p);p = NULL;return 0;
}

注意:

1.把p被free释放后,p仍然指向某一个地址,需要对其设置为空指针,避免出现空指针

2.若malloc函数开辟空间失败,则会返回空指针,所以需要对malloc函数的返回值进行检查

如果malloc函数开辟的空间太大,则会开辟失败

3.calloc函数

头文件:#include<stdio.h>

calloc也是用来开辟动态空间的,但它会把这块空间每个字节初始化为0

参数1:num表示要分配的元素个数,参数2:表示每个元素的大小

void* calloc (size_t num, size_t size);

calloc函数和free函数的使用

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>int main()
{//开辟动态空间int* p = (int*) calloc(10, sizeof(int));//判断是否开辟成功if (p == NULL){printf("%s", strerror(errno));return 1;}//使用int i = 0;//进行空间的遍历for (i = 0; i < 10; i++){printf("%d ", p[i]);}return 0;//释放内存free(p);p = NULL;
}

程序运行


4.realloc函数

头文件:#include<stdlib.h>

realloc函数开始实现对动态开辟空间大小的调整

格式

参数1:ptr是一个指向先前通过malloccallocrealloc函数分配的内存块的指针,也就是要调整内存的地址

参数2:size表示重新分配后内存块的大小

返回值:realloc函数返回后,返回值为调整之后的空间起始地址

void* realloc (void* ptr, size_t size);
realloc函数的应用
#include<stdio.h>
#include<stdlib.h>
%include<errno.h>int main()
{//开辟动态空间int* p = (int*) calloc(10, sizeof(int));//判断是否开辟成功if (p == NULL){printf("%s", strerror(errno));return 1;}
//重新分配内存使其能容纳80个字节的空间,成功使用
//realloc函数后,原来由calloc函数开辟的空间会被回收
//所以不需要对p进行释放,如果释放,则会导致程序崩溃int* pa = realloc(p, 80);if (pa == NULL){printf("%s", strerror(errno));return 1;}//使用int i = 0;for (i = 0; i < 20; i++){*(pa+i) = i + 1;printf("%d ",*(pa+i));}//释放free(pa);pa = NULL;
}

程序运行:

realloc在调整内存空间有俩种情况

1.原有空间之后有足够大的空间

2.原有空间之后没有足够大的空间

若为情况1

使用realloc函数后,直接在原有的空间后直接扩展,最终成为一块连续空间,且原有的空间数据不发生变化

若为情况2

原有空间之后没有足够的空间,如果要扩展的话,在原有的堆空间另找一个合适大小的连续空间来使用,这时函数返回的是一个新空间的地址

情况1:在原来的空间直接进行扩展

情况2:没有找到足够的空间,在另一处又开辟了一个空间

三.动态内存中常见的错误

void test(){int *p = (int *)malloc(INT_MAX/4);*p = 20;free(p);}

这段代码有什么问题呢👆

没有对malloc函数是否成功开辟空间进行判断,如果p为NULL,对NULL的解引用是错误的

void test(){int i = 0;int *p = (int *)malloc(10*sizeof(int));if(NULL == p){exit(EXIT_FAILURE);}for(i=0; i<=10; i++){*(p+i) = i;}free(p);
}

这段代码有什么问题呢👆

对动态开辟的空间进行越界访问,且最后p没有置为NULL

void test(){int a = 10;int *p = &a;free(p);//ok?}

对非动态开辟的内存进行free,free之后也要把指针p指向的地址设置为NULL

void test(){int *p = (int *)malloc(100);p++;free(p);}

因为p++,所以p并没有在动态内存的起始地址,所以free仅释放了一部分动态内存

void test(){int *p = (int *)malloc(100);free(p);free(p);、}

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

void test(){int *p = (int *)malloc(100);if(NULL != p){*p = 20;}}int main(){test();while(1);}

使用过后并没有对malloc开辟的动态空间进行free,free之后,要把指针p指向的空间设置为NULL


四.经典的笔试题

void GetMemory(char *p){p = (char *)malloc(100);}void Test(void){char *str = NULL;GetMemory(str);strcpy(str, "hello world");printf(str);}

请问这段代码错误出在哪👆

1.调用GetMemory的时候,str的传参为值传递,p是str临时变量,所以再GetMemory函数内部将动态内存的地址存放在p中,不会影响str。所以在GetMemory函数返回之后

,str中依然是空指针。strcpy函数就会调用失败原因是对NULL的解引用操作,程序会崩溃。

2.malloc是否成功申请空间没有检验

3.GetMemory函数内容malloc申请的空间没有机会释放

,造成了内存泄漏

char *GetMemory(void){char p[] = "hello world";return p;}void Test(void){char *str = NULL;str = GetMemory();printf(str);}

请问这段代码错误出在哪👆

 这是一个栈空间的问题GetMemory函数内部创建的数组是临时的,虽然返回了数组的起始地址给了str,但是数组的内存出了GetMemory函数就被回收了,而str依然保存了数组的起始地址,这时如果使用str,str就是野指针,也就是说访问值是随机的

void GetMemory(char **p, int num){*p = (char *)malloc(num);}void Test(void){char *str = NULL;GetMemory(&str, 100);strcpy(str, "hello");printf(str);}

请问这段代码错误出在哪👆

1.没有用free释放malloc开辟的空间,造成内存泄漏

2.并没有对malloc是否开辟出空间进行检验

void Test(void){char *str = (char *) malloc(100);strcpy(str, "hello");free(str);if(str != NULL){strcpy(str, "world");printf(str);}}

请问这段代码错误出在哪👆

把free把指针所指向的空间释放后,并没有把指针置为NULL,形成了悬空指针

因为虽然 str 指针本身没有被赋值为 NULL,但其指向的内存已经被释放,再使用这个指针会导致未定义行为,如果继续执行,可能会导致程序崩溃产生不可预料的结果

五.柔性数组

在c99中,结构体中最后一个元素允许是未知大小的数组,这就是柔性数组

柔性数组的创建

第一种方式👇

typedef struct st_type{int i;int a[0];//柔性数组成员
}type_a;

第二种方式👇

typedef struct st_type{int i;int a[];//柔性数组成员
}type_a;

柔性数组的特点

1.结构体柔性数组成员前必须至少一个其它成员

2.sizeof返回的大小不包含柔性数组的大小

3.包含柔性数组成员的结构体应该用malloc函数进行内存的动态分配,并且分配的大小应该大于结构体的大小,以适应柔性数组的预期大小

1.sizeof返回的大小不包含柔性数组的大小

#include<stdio.h>
struct s
{int i;int a[0];//柔性数组成员
};int main()
{printf("%d ", sizeof(struct s));return 0;
}

程序运行

3.使用malloc函数进行柔性数组的空间分配

struct S
{int n;char c;int arr[];};int main()
{//柔性数组创建空间,+号前头的是不包含柔性数组大小的空间,后边是创建了//元素个数为10的柔性数组struct S* ps = (struct S*)malloc(sizeof(struct S) + 10 * sizeof(int));if (ps == NULL){printf("%p\n", strerror(errno));return 1;}//柔性数组的使用int i = 0;for (i = 0; i < 10; i++){ps->arr[i] = i + 1;printf("%d ", ps->arr[i]);}//释放free(ps);ps = NULL;
}

代码运行

结语:

限于水平,本篇文章不足之处在所难免,还望读者见谅,如有问题,请大家指正下。

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

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

相关文章

【从零开始的LeetCode-算法】3270. 求出数字答案

给你三个 正 整数 num1 &#xff0c;num2 和 num3 。 数字 num1 &#xff0c;num2 和 num3 的数字答案 key 是一个四位数&#xff0c;定义如下&#xff1a; 一开始&#xff0c;如果有数字 少于 四位数&#xff0c;给它补 前导 0 。答案 key 的第 i 个数位&#xff08;1 < …

STM32+AI语音识别智能家居系统

基于 STM32 和 AI 语音识别的智能家居系统的详细硬件和软件设计&#xff0c;包括各个模块的详细描述和代码示例。 一、硬件设计 1. 微控制器&#xff08;STM32&#xff09;&#xff1a; 选择 STM32F7 系列或更高性能的芯片&#xff0c;如 STM32F767ZIT6&#xff0c;以满足处理…

信息收集—JS框架识别泄露提取API接口泄露FUZZ爬虫插件项目

前言 免杀结束了&#xff0c;我们开个新的篇章——信息收集。为什么我一开始先写信息收集的文章呢&#xff0c;是因为现在我才发现我的信息收集能力其实有点弱的&#xff0c;所以呢开始知不足&#xff0c;而后进。 什么是JS JS就是JavaScript的简称&#xff0c;它和Java是没…

智能化护士排班系统的设计与实现(文末附源码)

自动排班-护士(分白班|夜班) 当服务器启动时检测需要自动排班,自动开始排班的算法执行 获得本周的所有日期,例如2023-01-29.....2023-02-04依次对每个科室&#xff0c;从第一天开始,逐天进行排班&#xff0c;分别设置两个二个数组&#xff0c;day[7];night[7]分别记忆一周内每…

【原创】java+ssm+mysql社区疫情防控管理系统设计与实现

个人主页&#xff1a;程序猿小小杨 个人简介&#xff1a;从事开发多年&#xff0c;Java、Php、Python、前端开发均有涉猎 博客内容&#xff1a;Java项目实战、项目演示、技术分享 文末有作者名片&#xff0c;希望和大家一起共同进步&#xff0c;你只管努力&#xff0c;剩下的交…

Flink Source 详解

Flink Source 详解 原文 flip-27 FLIP-27 介绍了新版本Source 接口定义及架构 相比于SourceFunction&#xff0c;新版本的Source更具灵活性&#xff0c;原因是将“splits数据获取”与真“正数据获取”逻辑进行了分离 重要部件 Source 作为工厂类&#xff0c;会创建以下两…

CSS回顾-基础知识详解

一、引言 在前端开发领域&#xff0c;CSS 曾是构建网页视觉效果的关键&#xff0c;与 HTML、JavaScript 一起打造精彩的网络世界。但随着组件库的大量涌现&#xff0c;我们亲手书写 CSS 样式的情况越来越少&#xff0c;CSS 基础知识也逐渐被我们遗忘。 现在&#xff0c;这种遗…

11.08-10.14谷粒商城

谷粒商城--品牌管理 前端表单校验 品牌新增 品牌修改 校验规则 dataRule: {name: [{ required: true, message: "品牌名不能为空", trigger: "blur" }],logo: [{ required: true, message: "品牌logo地址不能为空", trigger: "blur"…

无插件H5播放器EasyPlayer.js网页web无插件播放器选择全屏时,视频区域并没有全屏问题的解决方案

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、MP3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

基于Spring Boot的电子商务系统设计

5 系统实现 系统实现部分就是将系统分析&#xff0c;系统设计部分的内容通过编码进行功能实现&#xff0c;以一个实际应用系统的形式展示系统分析与系统设计的结果。前面提到的系统分析&#xff0c;系统设计最主要还是进行功能&#xff0c;系统操作逻辑的设计&#xff0c;也包括…

CSP-X2024山东小学组T2:消灭怪兽

题目链接 题目名称 题目描述 怪兽入侵了地球&#xff01; 为了抵抗入侵&#xff0c;人类设计出了按顺序排列好的 n n n 件武器&#xff0c;其中第 i i i 件武器的攻击力为 a i a_i ai​&#xff0c;可以造成 a i a_i ai​ 的伤害。 武器已经排列好了&#xff0c;因此不…

游戏引擎学习第九天

视频参考:https://www.bilibili.com/video/BV1ouUPYAErK/ 修改之前的方波数据&#xff0c;改播放正弦波 下面主要讲关于浮点数 1. char&#xff08;字符类型&#xff09; 大小&#xff1a;1 字节&#xff08;8 位&#xff09;表示方式&#xff1a;char 存储的是一个字符的 A…

JWTUtil工具类

写一个Jwt工具类 导入如下pom.xml依赖 <!--fastjson依赖--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.33</version></dependency><!--jwt依赖--><dependenc…

使用React和Vite构建一个AirBnb Experiences克隆网站

这一篇文章中&#xff0c;我会教你如何做一个AirBnb Experiences的克隆网站。主要涵盖React中Props的使用。 克隆网站最终呈现的效果&#xff1a; 1. 使用vite构建基础框架 npm create vitelatestcd airbnb-project npm install npm run dev2. 构建网站的3个部分 网站从上…

K8S containerd拉取harbor镜像

前言 接前面的环境 K8S 1.24以后开始启用docker作为CRI&#xff0c;这里用containerd拉取 参考文档 正文 vim /etc/containerd/config.toml #修改内容如下 #sandbox_image "registry.aliyuncs.com/google_containers/pause:3.10" systemd_cgroup true [plugins.…

unity小:shaderGraph不规则涟漪、波纹效果

实现概述 在本项目中&#xff0c;我们通过结合 Sine、Polar Coordinates 和 Time 节点&#xff0c;实现了动态波纹效果。以下是实现细节&#xff1a; 核心实现 Sine 波形生成&#xff1a; 使用 Sine 节点生成基本的波形。该节点能够创建周期性变化&#xff0c;为波纹效果提供…

设计模式(四)装饰器模式与命令模式

一、装饰器模式 1、意图 动态增加功能&#xff0c;相比于继承更加灵活 2、类图 Component(VisualComponent)&#xff1a;定义一个对象接口&#xff0c;可以给这些对象动态地添加职责。ConcreteComponent(TextView)&#xff1a;定义一个对象&#xff0c;可以给这个对象添加一…

(附项目源码)nodejs开发语言,212 个性化音乐推荐系统的设计与实现,计算机毕设程序开发+文案(LW+PPT)

摘要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0c;应用软件的工作规…

面试时问到软件开发原则,我emo了

今天去一个小公司面试&#xff0c;面试官是公司的软件总监&#xff0c;眼镜老花到看笔记本电脑困难&#xff0c;用win7的IE打开leetcode网页半天打不开&#xff0c;公司的wifi连接不上&#xff0c;用自己手机热点&#xff0c;却在笔记本电脑上找不到。还是我用自己的手机做热点…

数字IC后端低功耗设计实现案例分享(3个power domain,2个voltage domain)

下图所示为咱们社区T12nm A55低功耗实现项目。其实这个项目还可以根据产品的需求做一些改进。改进后项目实现的难度会大大增加。也希望通过今天的这个项目案例分享&#xff0c;帮助到今年IC秋招的同学。 芯片低功耗设计实现upf编写指南&#xff08;附低功耗项目案例&#xff0…