【C语言进阶】动态内存管理

📚作者简介:爱编程的小马,正在学习C/C++,Linux及MySQL..

📚以后会将数据结构收录为一个系列,敬请期待

● 本期内容会给大家讲解如何开辟动态内存空间,以及c/c++内存是如何分配的,最后还会给大家讲解柔性数组


目录

1. 为什么存在动态内存分配

2. 动态内存开辟的要学习的四个函数

2.1 malloc函数

2.2 free函数

2.3 calloc函数

2.4 realloc函数 

3. c/c++程序的内存开辟 

4.柔性数组 

 4.1什么是柔性数组

4.2 柔性数组的优点 

总结



1. 为什么存在动态内存分配

我们已经掌握的内存开辟的方式有

int a = 20; //开辟4个字节的空间存储a
char arr[20] ={0} ;//连续开辟20个字节的空间 

这些开辟方式都是在栈区上开辟的,但是这种开辟方式有两个问题:

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

2、数组在声明的时候,必须指定开辟的长度

那么动态内存开辟就比较简单了,顾名思义,就是你开辟内存是不固定的,不够了可以加,多了可以减,这就是动态内存开辟

2. 动态内存开辟的要学习的四个函数

2.1 malloc函数

C语言给程序员提供了一个动态内存开辟的函数malloc

void *malloc( size_t size );

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

● 如果开辟成功,会返回这块空间的起始地址

● 如果开辟失败,会返回空指针NULL,所以在使用时一定要对malloc返回值进行检查

● 返回值类型的void*,具体接收类型由程序员决定

如果size是0,这个是标准为定义的,也是没有意义的,具体由编译器决定

2.2 free函数

C语言专门设置了一个函数用于释放动态内存空间

void free( void *memblock );

 一定要注意:free函数是用来释放动态开辟的内存空间

如果memblock指针指向的不是动态开辟的内存,则free函数的行为是未定义的

如果memblock指针指向的是NULL空指针,则free函数什么都不做

我们一起来开辟一块动态内存空间:

#include<stdio.h>
#include<stdlib.h>int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; i++){printf("%d\n", *(p + i));}free(p);p = NULL;return 0;
}

 上述代码的运行结果是:

可以看到malloc函数给我们开辟了一块40个字节的空间,但是打印的全是随机值,说明malloc函数并不会给你初始化那块空间。如果要初始化空间,就需要接下来要介绍的calloc函数可以满足需求。

2.3 calloc函数

C语言给程序员提供了一个动态内存开辟的函数calloc

void *calloc( size_t num, size_t size );

这个函数的功能是给num个大小为size元素开辟一块空间,并且把空间初始化为0

● 如果开辟失败,会返回空指针NULL,所以在使用时一定要对calloc返回值进行检查

举个例子:

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)calloc(10, 4);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;
}

看看这段代码输出的效果:

其实无论是malloc还是calloc,好像只能为我们申请动态内存空间,而不能灵活的调节咱们的动态内存空间,这时候realloc函数就出现了,它可以很好的为我们管理malloc,calloc申请的动态内存空间。

2.4 realloc函数 

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

memblock是要调整的内存地址

● size是调整后的新大小

返回调整后的内存地址

如果内存不够,realloc函数会在其他地方找一块足够的空间,同时拷贝原内存中数据至新地址,返回这块空间的地址

 使用realloc函数:

1、如果原有空间之后有足够大的空间

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; i++){p[i] = i + 1;}//40个字节空间不够,我们要80个字节int * ptr = (int *)realloc(p, 60);if (ptr == NULL){perror("realloc");return 1;}p = ptr;ptr = NULL;for (i = 0; i < 15; i++){printf("%d\n", p[i]);}free(p);p = NULL;return 0;
}

是如何判断是否原空间之后是否有足够空间呢?看realloc的返回值,如果返回的是和p一样的,说明没有新开辟空间,如图

2、如果原空间没有足够大的空间

#include<stdio.h>
#include<stdlib.h>
int main()
{int* p = (int*)malloc(40);if (p == NULL){perror("malloc");return 1;}int i = 0;for (i = 0; i < 10; i++){p[i] = i + 1;}//40个字节空间不够,我们要80个字节int * ptr = (int *)realloc(p, 800);if (ptr == NULL){perror("realloc");return 1;}p = ptr;ptr = NULL;for (i = 0; i < 20; i++){printf("%d\n", p[i]);}free(p);p = NULL;return 0;
}

这个时候p和ptr的值不同了,说明会开辟一块新的空间,那我们来看下原来的数据会不会丢了呢?

看看运行结果:

可以看到,原来的内存空间中存放的数据没有丢,而是被realloc拷贝过来了。这就是realloc开辟新空间的操作:

1、拷贝原空间的数据到新空间

2、释放原来的空间

3、返回新空间的起始地址 

3. c/c++程序的内存开辟 

首先,c/c++的内存分为内核空间区,栈区,堆区,数据段以及代码段。

栈区主要是存放形式参数,局部变量;堆区,主要是动态内存开辟;数据段,主要是存放全局变量和静态数据(static修饰的变量);代码段主要是存放只读常量以及可执行代码

看这个图片

4.柔性数组 

 4.1什么是柔性数组

在C99规定中,柔性数组是结构体最后一个元素允许是未知大小的数组,这就叫做柔性数组成员

例如

struct s 
{int age;int a[];//柔性数组
};
int main()
{return 0;
}

柔性数组的特点: 

1、结构中柔性数组成员前必须至少有一个其他成员

2、sizeof求结构体大小是不包含柔性数组大小

3、包含柔性数组结构体成员在malloc分配时,应分配的内存大于结构大小,以适应柔性数组大小

#include<stdio.h>
#include<stdlib.h>
struct s 
{int i;int a[];//柔性数组
};
int main()
{struct s* ps = (struct s*)malloc(sizeof(struct s) + 100 * sizeof(int));ps->i = 100;int j = 0;for (j = 0; j < 100; j++){ps->a[j] = j; //柔性数组就获得了连续的400个字节的空间}free(ps);ps = NULL;return 0;
}

那么其实我同样可以用指针来完成上述代码 

#include<stdio.h>
#include<stdlib.h>
struct s
{int i;int* p;
};int main()
{struct s* ps =(struct s*) malloc(sizeof(struct s));ps->i = 100;ps->p = (int*)malloc((ps->i) * sizeof(int));int j = 0;for (j = 0; j < 100; j++){ps->p[j] = j; //柔性数组就获得了连续的400个字节的空间}for (j = 0; j < 100; j++){printf("%d ",ps->p[j]); //柔性数组就获得了连续的400个字节的空间}free(ps->p);ps->p = NULL;free(ps);ps = NULL;return 0;
}

4.2 柔性数组的优点 

1、方便内存释放,因为我们就malloc了一次,而用指针的方法,需要malloc两次,可能会忘记释放

2、有利于加快访问速度 


总结

上文就是动态内存管理的详细讲解,下一节会给大家更新动态内存常见的错误。

如果这份博客对大家有帮助,希望各位给小马一个大大的点赞鼓励一下,如果喜欢,请收藏一下,谢谢大家!!!
制作不易,如果大家有什么疑问或给小马的意见,欢迎评论区留言。

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

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

相关文章

浏览器打不开DevTools?

随笔记录下某网站禁止通过F12, CtrlShiftI等快捷键来打开开发者工具&#xff0c;其次通过浏览器宽高定时来重定向。 目标网站 aHR0cHM6Ly93d3cuemhpaHUuY29tL21hcmtldC9wYWlkX2NvbHVtbi8xNjkxOTY2MTg4MzY2ODYwMjg4L3NlY3Rpb24vMTc1MjcyNjI3MjMzMjIxMDE3Nw问题描述 当我们通过…

MySQL事务--隔离级别

1 事务的隔离级别 事务的隔离级别是数据库管理系统&#xff08;DBMS&#xff09;提供的一种机制&#xff0c;用于控制并发事务之间的相互影响程度。隔离级别决定了一个事务对于其他事务所做的修改是否可见&#xff0c;以及在多个事务并发执行时可能出现的各种问题的程度。 常…

瀑布VS敏捷,看看哪种研发管理模式更适合你的团队

软件开发是一个复杂且极具挑战性的过程&#xff0c;需要有合适的研发管理模式。瀑布模型和敏捷开发是两种常见的研发管理模式&#xff0c;它们在项目管理和团队合作方面有着截然不同的理念和实践方式。本文将介绍这两种开发模式的特点、优缺点及对比&#xff0c;提供如何选择适…

react之初识state

第二章 - 添加交互 State: 组件的记忆 组件通常需要根据交互更改屏幕上显示的内容。输入表单应该更新输入字段&#xff0c;单击轮播图上的“下一个”应该更改显示的图片&#xff0c;单击“购买”应该将商品放入购物车。组件需要“记住”某些东西&#xff1a;当前输入值、当前…

智能合约语言(eDSL)—— 并行化方案

到目前为止&#xff0c;一个简单的合约语言与合约虚拟机已经完成了&#xff1b;接下来&#xff0c;我们让我们的整个系统可以并行化&#xff1b; 为什么要并行&#xff1f; 性能&#xff0c;如果我们用现在的互联网应用去衡量dapp&#xff0c;dapp无法支撑这种量级的访问&…

使用mybatis的时候报错,ora 00942 表或视图不存在

因为我的Spring cloud项目&#xff0c;数据库有多个数据源&#xff0c;在当前类中使用的com.baomidou.dynamic.datasource.annotation的DS(“a”),a其中一个数据源&#xff0c;但是我用的方法中&#xff0c;用到了其他的数据源b的查询&#xff0c;这就会报错ora 00942 表或视图…

nvm安装及使用(mac)

安装 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash# orwget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash这步会自动在你的文件中添加nvm配置文件. 如果你用的是zsh, 那就是 ~/.zshrc. 如果你用的 bas…

解决在 Python 数据分析中遇到的 Matplotlib 字体警告问题

当在 Python 数据分析中遇到类似以下警告时&#xff1a; D:\anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 24037 missing from current font.font.set_text(s, 0.0, flagsflags) D:\anaconda3\lib\site-packages\matplotlib\ba…

美国签证被拒签后怎么申诉?

美国签证被拒签后该如何申诉&#xff1f;这是许多申请者心中的疑问。美国签证被拒签可能会给申请者带来困扰和挫折感&#xff0c;但并非没有解决的办法。下面将介绍一些申诉的常见步骤和注意事项。 首先&#xff0c;需要理解拒签的原因。美国签证被拒签可能是因为申请材料不全、…

Linux 网络操作命令FTP

FTP命令 引言 文件传输协议&#xff08;FTP&#xff09;是一种用于在网络上进行文件传输的协议。在Linux系统中&#xff0c;FTP可以作为一个非常有用的工具来上传、下载和管理文件。本文将介绍如何在Linux系统中安装FTP服务器&#xff0c;以及如何使用FTP客户端进行文件传输。…

vue:使用:element 中弹框中获取table高度无效

场景&#xff1a;dialog 弹框中想要获取里面table的高度&#xff0c;但是直接用 tableRef.value?.$el.offsetHeight 获取无效。 原因&#xff1a;dialog中有个弹框打开的加载动画。需要用监听 opeined 【Dialog 打开动画结束时的回调】等动画加载后然后进行高度获取 一、dem…

Go语言 Channel

基本介绍 Channel 是 Go 中的一个核心类型&#xff0c;可以把它看成一个管道。 利用通道我们可以在多个 goroutine 之间传递数据。 如果说 Goroutine 是 Go 程序并发的执行体&#xff0c;Channel 就是它们之间的连接。 Channel 是可以让一个 Goroutine 发送特定值到另一个 Gor…

动态活码二维码怎么制作?在线二维码生成器的使用技巧

二维码是如何生成的呢&#xff1f;现在二维码与我们的工作和生活息息相关&#xff0c;越来越多的场景都会有不同类型的二维码&#xff0c;比如常见的有视频、图片、文件、问卷、文本等等类型的内容。面对不同用途需求来制作二维码来为其他人提供内容展示&#xff0c;提升用户获…

Linux的DNS域名解析服务

目录 1.DNS 1.1定义 1.2作用/功能 1.3域名结构 1.4两种查询方式 1.5DNS域名解析工作原理 1.6DNS系统类型 2.正向解析实验​ 2.1安装bind服务&#xff0c;查看配置文件 2.2配置文件配置及文件内容说明 3.反向解析实验 4.配置主从DNS服务器 1.DNS 1.1定义 DNS域名系…

centos7搭建maven私服nexus

1.nexus Nexus Repository Manager&#xff08;通常简称 Nexus 或 Nexus RM&#xff09;是由Sonatype公司开发的一款开源的、强大的软件仓库管理工具&#xff0c;主要用于企业级的二进制组件&#xff08;如Java库、Node.js模块、Python包等&#xff09;存储、管理和分发。 官方…

使用 GORM 自定义类型:解决问题与技巧分享

引言 在使用 Go 语言的 ORM 库 GORM 进行数据模型操作时&#xff0c;开发者经常会遇到如何处理自定义数据类型的问题。本文将基于一个具体的例子 —— SliceString 类型&#xff0c;分享如何在 GORM 中处理自定义类型的字段&#xff0c;并通过设置 GORM 标签来指定类型&#x…

库存数据可视化分析按这个做,赚大了!

今天我们来看一张库存数据可视化分析驾驶舱&#xff0c;全面了解库存资金占用情况&#xff0c;物料周转情况&#xff0c;库存趋势情况、以及占库存金额最高的商品有哪些等。 为更好地实现以上效果&#xff0c;并且增强报表的可读性、易读性&#xff0c;我们采用了按分析场景选…

果断收藏|项目中有哪些风险是难以避免的?

我们在做项目的时候&#xff0c;总能遇到各种各样的风险。 为了尽可能规避风险或者减轻风险对项目造成的影响&#xff0c;我们会通过一些特定的方法对风险进行管理。 所谓风险管理&#xff0c;就是通过对风险的认识、衡量和分析&#xff0c;选择最有效的方式&#xff0c;主动…

TypeError: Unknown file extension “.ts“

报错 ts-node effect.ts ts-node 是一个 Node.js 的 TypeScript 执行器&#xff0c;它可以实时将 TypeScript 代码编译成 JavaScript 并执行。你可以通过 npm install -g ts-node 安装 ts-node&#xff08;全局安装&#xff09;&#xff0c;然后使用 ts-node 命令来执行 .ts 文…

数据被“锁”?别急,教你如何解锁被“rmallox”勒索病毒加密的文件

在当今数字化时代&#xff0c;网络安全问题日益凸显。其中&#xff0c;勒索病毒成为了一种常见的网络威胁&#xff0c;而rmallox勒索病毒则是其中的一种典型代表。本文将从病毒特性、传播途径、防范策略、紧急措施以及从中得到的社会启示等多个角度&#xff0c;深入探讨rmallox…