C语言既然可以自动为变量分配内存,为什么还要用动态分配内存呢?

2023-05-16

已剪辑自: https://mp.weixin.qq.com/s/NRyM5KAm_jrBImmkA2WYSA

不知道大家在学习C语言动态分配内存的时候,有没有过这样的疑问:既然系统可以自动帮我们分配内存,为什么还需要我们程序员自己去分配内存呢?

图片如果想要弄清楚这些问题,我们首先就要了解静态内存和动态内存有什么区别,只有了解了他们两个的区别我们才能弄懂(理解)为什么需要动态分配内存!

今天的文章会用到以下知识点,大家可以作为了解内容去学习:静态内存、动态内存、堆、栈、全局变量、指针等。

既然要学习内存的相关知识,那我们就先从计算机的内存开始本篇的讲解吧!在计算机内存一共可以分为五个区域,其中有个区域是用来存储代码的,我们就不再进行讨论了。我们首先对这四个区域进行一个简单的了解,方便我们后面对于内存分配的理解。

我们首先看一张内存的组成图:图片从上面的图我们可以看出内存区域大概可以分为五个部分:堆、栈、全局/静态存储区和常量存储区、文字常量区。下面我们对这几个名词进行一下简单的讲解,心里先有个概念。

栈: 栈又叫堆栈,该区域是由编译器自动分配自动回收的变量的存储区。通常是用来存储局部变量的值、函数参数值等,是向下增长的。所谓向下生长的就是,先调用的栈帧的地址比后调用的地址大,栈一般大小有几个M左右。

堆: 就是那些由程序员通过malloc函数申请到的内存块,一般我们申请的内存空间系统是不会帮我们释放的(当然有些也会由系统释放掉),由我们的应用程序去控制,一般一个malloc就要对应一个delete/free,由程序员主动释放。

全局区(静态区): 全局变量和静态变量都存储在这块区域,与其余变量的明显区别就是生命周期不一样,在程序结束时,系统会释放掉。

文字常量区 : 这个区域主要用来储存一些我们定义的常量,例如下面的定义就会被存储在文字常量区:char* p = "hello word!";。该部分也是由系统控制,程序结束后由系统释放掉。

代码区: 该区域主要用来存放程序代码,程序结束后由系统释放。

通过上面的基本概念我们已经知道了内存中的几个区域,以及哪些区域是我们程序员可以手动释放的,哪些区域是由系统为我们自动释放的。

我们今天主要需要用到的是,因为我们今天要讨论的动态内存和静态内存和堆栈是密切相关的。动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存。 这里也给大家贴出一张网上的图片,便于大家理解上面的知识。

了解完堆栈之后我们还有个知识需要了解就是指针,由于我对于指针的理解还不是特别透彻,所以有哪些说的不对的地方大家可以在评论区指出来,我会即时进行修改。

明明我们今天要讨论的是动态内存和静态内存,为什么要了解指针呢?如果你有这样的疑问说明你对于内存或者指针的理解还不是特别到位。指针和内存的联系非常紧密,没有内存指针也将失去意义,我们对指针进行的操作实际上就是在间接的操作内存。但是,大家需要注意指针也是有类型的,他的数据类型取决于它所指向的内存空间的数据类型。关于指针和内存的关系我们后面会进行详细的讲解。

有了上面基础知识的加持,我们现在就可以回归我们今天的主题来讨论为什么我们需要动态内存了!我这里先说一下我的理解,我对这个问题的答案总结出以下几点,当然这绝不是全部的原因,鄙人也是能力有限,只能理解到这种程度,更多的理解欢迎大家在评论区进行讨论!

  1. 节省资源:用多少申请多少,不需要了及时进行释放,这样可以避免资源的浪费。
  2. 方便储存大型对象:大家需要注意栈区不是无限大的,对于大型项目如果说有的变量都储存在栈区,很可能会造成栈区内存不够用。
  3. 方便对象的调用 :对于较大的对象我们使用动态内存存储时我们只需要通过指针将变量首地址传递出去即可,而不用将整个对象都进行传递。

对于上面说的三点我可以给大家举个简单的例子,方便大家理解:

对于第一点大家应该很好理解,我用多少就申请多少,节省资源,但是后面两点可能就不是很好理解了,这里给大家举个简单的例子:

你是一个开超市的,栈区就相当于你的超市,但是你会发现如果你如果把商品都放到超市,可能你的超市会装不下那么多货物。于是仓库就出现了,堆区就相当于你的仓库。这些仓库和你的超市是分离的,如果你发现你进了一些商品,这些商品短时间内也不会被完全卖出去,那你就可以把这些货物放到你的仓库里,而你只需要记住你仓库的地址即可。

这样就可以保证你的超市不会因为堆积太多商品而显得拥挤,如果有人要买这些商品,你可以把仓库地址告诉他,他就会直接去你仓库拿货。

听过这个故事你可能更迷糊了,我下面给你梳理一下,相信你会豁然开朗!

动态申请空间,能动态确定对象所需要的内存。

我需要多大的空间,就用多大的仓库存放该商品。

对于大型对象的存储,栈区容不下。

我有大量的商品,都放超市太占地方。可以放仓库中,记住仓库地址就行。

传递指针比传递整个对象更高效。

别人要买该商品,告诉别人我仓库地址,不用把整个仓库搬过去。

(感觉这个故事我还是没有讲好,表达能力欠佳)

知道了动态分配内存的好处后我们就可以更好的理解我们为什么要使用动态分配内存以及何时应该使用动态分配了,所以如果你进了几包方便面(建了个很小的对象)那你就没必要把方便面放到仓库了,直接放到超市货架上就可以了。

如果你超市比较小(代码量比较小)那你也没必要把东西放到仓库了,直接放到柜台上就可以了。所以很多问出为什么要使用动态分配内存的主要原因是因为他现在还没接触过大型项目,或者特别大的对象,如果你做过底层驱动开发或者上位机开发的话相信你对于动态申请内存并不会陌生的。

通过上面的故事我们大概也已经知道什么时候我们需要使用动态分配内存了,这里再简单的给大家做一个总结。

1、当你的代码量很大,需要用到很大的数据块来存储对象时。2、当你的程序中用到大数组时,你就需要用动态分配内存。3、需要数组长度根据程序进行变化。4、想让一个变量储存的内容不会因为函数的结束而被收回(有点像全局变量)

这里就不得不来讨论一下“传统数组”的缺点了,传统数组”就是前面所使用的数组,与动态内存分配相比,传统数组主要有以下几个缺点:

  1. 数组的长度必须事先指定,而且只能是常量,不能是变量。比如像下面这么写就是对的:
int a[5];
而像下面这么写就是错的:
int length = 5;
int a[length];  //错误
  1. 因为数组长度只能是常量,所以它的长度不能在函数运行的过程当中动态地扩充和缩小。
  2. 对于数组所占内存空间程序员无法手动编程释放,只能在函数运行结束后由系统自动释放,所以在一个函数中定义的数组只能在该函数运行期间被其他函数使用。

而动态内存就不存在这个问题,因为动态内存是由程序员手动编程释的,所以想什么时候释放就什么时候释放。只要程序员不手动编程释放,就算函数运行结束,动态分配的内存空间也不会被释放,其他函数仍可继续使用它。除非是整个程序运行结束,这时系统为该程序分配的所有内存空间都会被释放。

所谓“传统数组”的问题,实际上就是静态内存的问题。我们讲传统数组的缺陷实际上就是以传统数组为例讲静态内存的缺陷。本质上讲的是以前所有的内存分配的缺陷。正因为它有这么多缺陷,所以动态内存就变得很重要。动态数组能很好地解决传统数组的这几个缺陷。

知道了我们为什么要动态分配内存之后我们一起来学习以下C语言中如何进行动态分配内存。在C语言中动态分配内存使用的是函数malloc进行分配的。

malloc 是一个系统函数,它是 memory allocate 的缩写。其中memory内存的意思,allocate分配的意思。顾名思义 malloc 函数的功能就是分配内存。要调用它必须要包含头文件 <stdlib.h>,它的原型为:

# include <stdlib.h>
void *malloc(unsigned long size);

由上面的函数原型我们可以看出malloc 函数只需要一个形参,并且该形参是整形的。函数返回值为一个指向所分配的连续空间的首地址的指针。当函数未能成功分配存储空间时(如内存不足)则返回一个NULL指针。所以malloc 函数的返回值为一个指针。

由于堆区内存也是有限的,不能无限制地分配下去,所以秉持着尽量节省资源,我们应该在分配的内存区域不用时,及时释放它,以便其他的变量或程序使用。

释放malloc 函数分配内存的函数是free函数,free函数和malloc 总是成对出现的。free函数的原型如下所示:

# include <stdlib.h>
void free(void *p);

由上面的函数原型可以看出free函数需要一个形参,且形参的类型是一个指针。free 函数无返回值,它的功能是释放指针变量 p 所指向的内存单元。此时 p 所指向的那块内存单元将会被释放并还给操作系统,不再归它使用。操作系统可以重新将它分配给其他变量使用。

知道了申请和释放要用到哪些函数后我们来一起看一下我们该如何使用这些函数来申请和释放内存。

我们这里直接贴出malloc 函数动态分配内存的使用语句:

int *p = (int *)malloc(4);

它的意思是:请求系统分配 4 字节的内存空间,并返回第一字节的地址,然后赋给指针变量 p。当用 malloc 分配动态内存之后,上面这个指针变量 p 就被初始化了。

需要注意的是,函数 malloc 的返回值类型为 void* 型,而指针变量 p 的类型是 int* 型,即两个类型不一样,那么可以相互赋值吗?

答案是可以的,原因如下:上面语句是将 void* 型被强制类型转换 成 int*型,但事实上可以不用转换。C 语言中,void* 型可以不经转换(系统自动转换)地直接赋给任何类型的指针变量(函数指针变量除外)。

所以int*p = (int*)malloc(4);就可以写成 int*p=malloc(4);。此句执行完之后指针变量 p 就指向动态分配内存的首地址了。

我们知道如何申请一块内存了,也知道何时需要申请内存了,下面我们就来学习一下free函数的使用。

五、如何将动态分配内存free掉

在讲解之前有一点需要提醒一下大家,free函数只能释放堆区的空间,其他区域的空间无法使用free函数的。

下面给大家贴出一段动态申请内存的程序,来给大家讲解一下free的使用。

# include <stdio.h>
# include <stdlib.h>
int main(void)
{
    int *p = malloc(sizeof*p);
    *p = 10;
    printf("p = %p\n", p);
    free(p);
    printf("p = %p\n", p);
    return 0;
}

输出结果是:

p = 002C2ED0
p = 002C2ED0

上面的代码和结果可以看到释放前后,p 所指向的内存空间是一样的。所以释放后 p 所指向的仍然是那块内存空间。

既然指向的仍然是那块内存空间,那么就仍然可以往里面写数据。可是释放后该内存空间已经不属于它了,该内存空间可能会被分配给其他变量使用。如果其他变量在里面存放了值,而你现在用 p 往里面写入数据就会把那个值给覆盖,这样就会造成其他程序错误,所以当指针变量被释放后,要立刻把它的指向改为 NULL

那么,当指针变量被释放后,它所指向的内存空间中的数据会怎样呢?free 的标准行为只是表示这块内存可以被再分配,至于它里面的数据是否被清空并没有强制要求。

对于动态分配内存今天就给大家介绍到这里,自己水平也是有限,文中可能存在表述不正确的地方,希望大家发现后及时在评论区指出,谢谢!

  1. 郝斌C语言教程【年代虽已远,精华却不减】
  2. 动态内存分配,C语言动态内存分配详解
  3. 为什么要动态分配内存?什么时候需要动态分配内存?
  4. 一文读懂 Linux 动态内存分配机制
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言既然可以自动为变量分配内存,为什么还要用动态分配内存呢? 的相关文章

  • 基于UDP的服务器端和客户端

    文章目录 再谈UDP和TCP基于UDP的服务器端和客户端UDP中的服务器端和客户端没有连接UDP服务器端和客户端均只需1个套接字基于UDP的接收和发送函数基于UDP的回声服务器端 客户端 再谈UDP和TCP 已剪辑自 http c bian
  • Windows下的socket演示程序及加载DLL

    Windows下的socket演示程序 已剪辑自 http c biancheng net view 2129 html 上节演示了 Linux 下的 socket 程序 xff0c 这节来看一下 Windows 下的 socket 程序
  • QT文件操作

    文章目录 QFile文件操作QFile 43 QTextStreamQFile 43 QDataStream 已剪辑自 http c biancheng net view 9430 html 很多应用程序都需要具备操作文件的能力 xff0c
  • C语言文件操作

    文章目录 C语言文件操作C语言中的文件是什么 xff1f 文件流 C语言fopen函数的用法 xff0c C语言打开文件详解fopen 函数的返回值判断文件是否打开成功 fopen 函数的打开方式关闭文件实例演示 文本文件和二进制文件到底有
  • Chapter-3_概率分布

    Chapter 3 概率分布 本文内容摘自 xff1a https seeing theory brown edu probability distributions cn html 概率分布描述了随机变量取值的规律 1 随机变量 随机变量
  • C++文件操作

    文章目录 计算机文件到底是什么 xff08 通俗易懂 xff09 xff1f C 43 43 文件类 xff08 文件流类 xff09 及用法详解C 43 43 open 打开文件 xff08 含打开模式一览表 xff09 使用 open
  • C语言文件操作、C++文件操作、QT文件操作汇总及对比分析

    前面对C语言文件操作 C 43 43 文件操作 QT文件操作进行了比较详细的说明 xff0c 文章链接如下 C语言文件操作 C 43 43 文件操作 QT文件操作 通过大致的了解 xff0c 我们知道C语言文件操作 C 43 43 文件操作
  • 破茧化蝶,从Ring Bus到Mesh网络,CPU片内总线的进化之路

    文章目录 为什么需要片内总线 xff1f 星型连接 环形总线 xff08 Ring Bus xff09 Mesh网络 结论 转载于 xff1a https zhuanlan zhihu com p 32216294 在大多数普通用户眼里 x
  • 片内总线在cpu扮演什么角色?他为什么能实现高效,不同的CPU为什么采用不同的总线协议?

    文章目录 各种新型片上总线维度 xff08 Degree xff09 跳 xff08 Hop xff09 和跳数 xff08 Hop Count xff0c HC xff09 直连拓扑和路由器 Intel的Ring和Mesh 43 总线双R
  • 如何一步一步成为一个技术领域专家

    已剪辑自 https mp weixin qq com s biz 61 Mzg4NjIxODk4Mg 61 61 amp mid 61 2247497216 amp idx 61 1 amp sn 61 12c8b2449baa2c236
  • 电子电气架构设计需要考虑哪些方面?

    当前车辆日益复杂 xff0c 车联网功能在各个细分领域都在不断增加 xff0c 而更强大的智能功能也逐步增加 所有这些高级功能都依赖于线束和控制器才能发挥作用 然而面对日益增加的车辆复杂性和产品开发周期缩短的压力 xff0c 导致汽车制造商
  • 深度解读汽车域控制器

    已剪辑自 https mp weixin qq com s biz 61 Mzg4NjIxODk4Mg 61 61 amp mid 61 2247496089 amp idx 61 1 amp sn 61 db5c08f97342decfa
  • 单片机硬件和软件延时、RTOS相对延时和绝对延时

    已剪辑自 https mp weixin qq com s RPLQn4KO9Aqu1fpfZeOKA 前不久有个读者在问关于延时的问题 xff0c 大概就是问 xff1a 软件延时和硬件延时是啥意思 xff1f 做项目时他俩有什么区别 x
  • 计算机端口的安全知识大全,整的明明白白!

    已剪辑自 https cloud tencent com developer article 1406267 看了网安众安的这篇计算机端口文章 xff0c 你要是学不会你来打我 鲁迅没有说过 端口是计算机的大门 xff0c 计算机的安全应该
  • 自然语言处理概述

    文章目录 本文系书稿选登 2 1 自然语言处理概述2 1 1 什么是自然语言处理2 1 2 自然语言处理的任务 已剪辑自 https mp weixin qq com s hSTkA2ffa1YWCoZOgQdrBQ 本文系书稿选登 2 1
  • Ubuntu安装JLink_Linux_V434a & eclipse for c/c++

    按照网上说法 xff1a 由于ubuntu电脑使用usb口来连接Jlink 因此首先要安装usb的库 使用apt get install libusb命令无法定位到libusb软件 xff0c 于是 下载并安装 http sourcefor
  • 详解C语言二级指针三种内存模型

    已剪辑自 https mp weixin qq com s LRMjzIYIVPs6x6ja5ffp w 二级指针相对于一级指针 xff0c 显得更难 xff0c 难在于指针和数组的混合 xff0c 定义不同类型的二级指针 xff0c 在使
  • 可参考的通信数据接收解析方法

    已剪辑自 https mp weixin qq com s ZwAlQv1wV4M3ivCv4wKakw 前阵子一朋友使用单片机与某外设进行通信时 xff0c 外设返回的是一堆格式如下的数据 xff1a AA AA 04 80 02 00
  • 嵌入式软件测试怎么实现自动化测试?

    我是做嵌入式自动化测试的 xff0c 我来回答下吧 题主你的表达有点混乱 xff0c 我挑出来三个问题 xff1a 1 希望做测试做的高大上 2 不用手工这么累 3 测试做出能看到前景 我也做测试 xff0c 虽然自以为也不很高大上 第一个

随机推荐

  • 嵌入式软件自动化测试方法

  • STM32 | hex文件、bin文件、axf文件的区别?

    已剪辑自 https mp weixin qq com s 1EQRooYYpDeKvHpqguik6w 在STM32开发中 xff0c 经常会碰到hex文件 bin文件与axf文件 xff0c 这些都是可以烧写到板子里运行的文件 这三个文
  • 嵌入式 C 语言进阶小技巧,弱符号和弱引用

    已剪辑自 https mp weixin qq com s 7gBn4CB PwiJBfM0v57 xA 我是老温 xff0c 一名热爱学习的嵌入式工程师 关注我 xff0c 一起变得更加优秀 xff01 工程师老温 xff
  • 单片机开发中,传感器的数据处理算法

    已剪辑自 https mp weixin qq com s Yy7ysMXoeokW2g yaqsw3w 在传感器使用中 xff0c 我们常常需要对传感器数据进行各种整理 xff0c 让应用获得更好的效果 xff0c 以下介绍几种常用的简单
  • 功能安全软件架构

    已剪辑自 https mp weixin qq com s pCenGTqg2Xi t7b8ebNHMA 1 E GAS 安全架构思想 汽车功能安全旨在把电子电气系统失效而导致的人身危害风险控制在合理范围内 下图是常见的电子电气系统硬件构成
  • 代码是如何控制硬件的?

    简单来说 xff0c 就是软件指令通过操作寄存器 xff0c 控制与 或 非门搭建的芯片电路 xff0c 产生 保存高低电平信号 xff0c 实现相应的逻辑 xff0c 最终通过IO 串口等输出 要想更清楚的了解软件控制硬件的原理 xff0
  • Windows下的TCP/UDP网络调试工具-NetAssist以及Linux下的nc网络调试工具

    已剪辑自 https blog csdn net ccf19881030 article details 109370384 一 Windows下的网络调试工具 NetAssist 1 TCP服务端和客户端测试2 UDP服务端和客户端测试
  • 软件产品化

    1 产品化定义 xff1a 软件产品化是指客户无需为软件添加或调整代码和语句即能完成软件的安装配置 应用初始化 系统管理 用户使用的全过程 xff0c 并且软件至少能满足80 以上的用户某一组应用需求 微软Office或杀毒软件就是产品化软
  • 使用Dev C++进行Windows socket网络编程,需链接lws2_32库

    背景 在我们使用Dev C 43 43 进行C语言编程时 xff0c 如果我们引入的库是C语言标准库 xff0c 那我们是不要在编译器选项中进行额外的设置的 xff0c 但是如果我们使用的是一些不是C语言标准库 xff0c 那我们可能就需要
  • Windows下C语言程序和网络调试助手通信

    网络调试助手之间进行UDP通信 前面一篇文章介绍了Windows下的网络调试助手 xff0c 文章链接如下 xff1a Windows下的TCP UDP网络调试工具 NetAssist以及Linux下的nc网络调试工具 下面我们简单介绍一下
  • 为什么char a[]的a不能用a=“hello”来赋值?

    问题 char b里面的b是一个指向char的指针 xff0c 而b可以用b 61 hello 来复制 同样 xff0c a代表char数组的第一个元素的指针 xff0c 类型应该也是char xff0c 为什么b可以直接用赋值符号而a不可
  • Windows下使用C语言创建定时器并周期和网络调试助手通信

    在Windows C下采用timeSetEvent函数来设置定时器 关于timeSetEvent的函数原型及注释如下所示 xff1a MMRESULT span class token function timeSetEvent span
  • GCC编译程序如何减少堆栈空间的大小?

    1 静态堆空间的大小是编译期可以根据你的全局变量 静态变量尺寸算出来的 因此减少后者的长度可以减少你使用堆空间 2 MCU裸片程序 xff0c 很多环境是不方便 xff0c 不支持 xff0c 不鼓励使用malloc函数的 xff0c 裸环
  • 腾讯毕业十多年了

    已剪辑自 https mp weixin qq com s rb5aeIDQ5 qtifeoeIrqZw 昨天一个腾讯前同事加了我的微信找我聊天 xff0c 说是在网上看见我写的文章想起我了 这个同事印象中很沉默寡言的 xff0c 在腾讯除
  • ARM9和STM32什么关系?

    已剪辑自 https mp weixin qq com s QHARY D2SwFoQbFsJoCNlg 有小伙伴问 xff1a ARM9和STM32什么关系 xff1f 如果时间倒退10年 xff0c ARM9 s3c2410还算是比较流
  • 汽车ECU通信相关验证项有哪些?

    已剪辑自 https mp weixin qq com s fIAXkS37r6jvnuA7yIQDA 汽车电子的高速发展决定了基础软件所面临的要求将会更加严格 xff0c 其要求会覆盖软件的安全性 稳定性 可扩展性等方方面面 为了提高软件
  • 不可忽视的 C 语言陷阱!

    已剪辑自 https mp weixin qq com s bdGuOjz1Cg3YXHwlVh5ZlA 语言是编程的基石 xff0c C语言诡异且有种种陷阱和缺陷 xff0c 需要程序员多年历练才能达到较为完善的地步 虽然有众多书籍 杂志
  • 程序员能纯靠技术渡过中年危机吗?

    首先说答案 xff0c 能 xff01 程序员可以靠技术渡过中年危机 xff0c 但是要转变线性思维 先说说个人情况 xff0c 80后 xff0c 从事电机控制软件开发十余年 xff0c 属于制造业 xff0c 算嵌入式的一个小分支 xf
  • STM32学习(一)

    单片机基础 CISC和RISC举例 冯 诺依曼结构VS哈佛结构 冯 诺依曼结构数据和程序存储在同一存储器中 xff0c 访问数据时不能访问程序 xff0c 访问程序时不能访问数据 xff0c 属于分时复用 xff0c 同一时间只能读取其中一
  • C语言既然可以自动为变量分配内存,为什么还要用动态分配内存呢?

    已剪辑自 https mp weixin qq com s NRyM5KAm jrBImmkA2WYSA 不知道大家在学习C语言动态分配内存的时候 xff0c 有没有过这样的疑问 xff1a 既然系统可以自动帮我们分配内存 xff0c 为什