嵌入式Linux与物联网软件开发——C语言内核深度解析》一1.7 内存管理之栈(stack)

简介:

本节书摘来自异步社区《嵌入式Linux与物联网软件开发——C语言内核深度解析》一书中的第1章,第1.7节,作者朱有鹏 , 张先凤,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.7 内存管理之栈(stack)

1.7.1 什么是栈

我们常常听人说堆栈,但大家一定要明确区分:堆就是堆,栈就是栈。我们平常说的堆栈一般是指栈。那栈的本质是什么?栈是一种数据结构,C语言中使用栈来保存局部变量(注意,下文中不强调的情况下,局部变量均指非静态局部变量)。栈是被发明出来管理内存的,是一种维护内存的机制,这就是栈的本质。

1.7.2 栈管理内存的特点(小内存、自动化)

栈的特点是入口即出口,只有一个口,另一个口是堵死的。所以先进去的后出来,也就是先进后出,而与栈特点相对的就是先进先出,也就是队列。队列的特点是入口和出口都有,必须从入口进去,从出口出来,所以先进去的必须先出来,否则就堵住后面的。

先进后出FILO   first in last out      栈
 先进先出FIFO   first in first out     队列

为了大家更形象地理解,我们给出两张图。栈就好比往一个容器里面放东西,只有一个口,先放进去的只能后来拿出,后来放进去的,可以最先拿出。而队列就好比一列火车或者排着队列的军队。走在最前面的也是最早通过隧道的,先进先出。


4f62e81ca7a8a4c32e324dec28fa5ffe3e970572


053958af2b0fd8c0bbf5cdf8aa7d70b74d047c17

队列

1.7.3 栈的应用举例:局部变量和函数调用

C语言中的局部变量是用栈来实现的。

我们在C语言中定义一个局部变量时(int a),编译器会在栈中分配一段空间(4字节)给这个局部变量用。分配时栈顶指针会移动出4个字节空间给局部变量a用的意思就是,将这4字节的栈内存的内存地址和我们定义的局部变量名a关联起来,对应的栈操作为入栈,就是将数据存入变量a中。需要注意的是,这里栈指针的移动和内存分配是自动完成的,不需要程序参与。

然后等我们函数退出的时候,局部变量就会被释放。对应的操作是弹栈(出栈)。出栈时也是栈顶指针移动,将栈空间中与a关联的那4个字节空间释放,这个动作也是自动的,不需要人为干预。所以我们在写代码的时候,一定不要从被调函数返回一个局部变量的地址给主调函数,因为在函数执行完后局部变量就释放了,这个地址里面的内容有可能被新的内容填充。这时如果你在主调函数里面使用它,就很有可能造成数据错误。

除了保存局部变量,栈对于函数调用来说也是至关重要的,栈保存着函数调用所需的所有维护信息。我们都知道,函数调用时,程序跳到被调函数内部,执行完后返回当前位置接着执行。这就好比你去一个陌生的地方探险,我们的目的是去寻求刺激,但是我们最后还是希望可以平安回家。在旅行前就必须做一些准备(如多带水、带指南针,等等)。我们的函数在调用时,跳到被调函数之前也是要做诸多准备的。对于函数来说,最起码它要找到回家的路,也就是被调函数的下一行代码的地址(为返回做准备),以及当前相关的局部变量值、寄存器的值等重要信息。之所以保存这些信息是怕在被调函数运行时,由于子函数会用到与主调函数一样的寄存器,而造成主调函数正在使用的寄存器数据破坏,在函数返回时,栈里面的数据弹出,即使寄存器被用过也没关系,弹出数据会使寄存器里面的值覆盖为调用以前,从而复原调用以前的现场。这些值保存在哪里呢?就保存在栈里。在函数调用时,将这些东西压入栈,在被调函数执行完,栈再弹出这些值。

以栈方式管理内存,好处是方便,分配和最后回收都不用程序员操心,C语言(背后的运行时系统)会自动完成。

分析一个细节:在C语言中,定义局部变量时如果未初始化,则值是随机的。为什么?

定义局部变量,其实就是在栈中通过移动栈指针,来给程序提供一个内存空间和这个局部变量名绑定。因为这段内存空间在栈上,而栈内存是反复使用的(脏的,上次用完没清零的),所以说使用栈来实现的局部变量定义时如果不初始化,里面的值就是一个垃圾值。由此我们扩展一下,其实不仅仅是局部变量,所有的变量在定义时只是在内存中分配一块空间,并没有对这块空间进行任何的初始化。如果这块内存以前被用过,里面的数据还在,那它对于我们来说是没有任何意义的垃圾值。而且有时候这些数据会对我们的编程造成错误。所以我们一定要初始化变量,也就是用新的、有用的数据覆盖掉以前的数据。可能你会有个问题,那些以前用过的内存经过操作系统回收后,为什么里面还有数据。其实操作系统仅仅是回收这些内存,告诉其他程序可以用了,但并不删除这些内存里面的数据。

C语言通过一个小手段来实现局部变量的初始化。

int a = 15;    // 局部变量定义时初始化

C语言编译器会自动把这行转成:

int a;         // 局部变量定义
  a = 15;        // 普通的赋值语句

1.7.4 栈的约束(预定栈大小不灵活,怕溢出)

首先,栈是有大小的。而且栈的大小是可以设置的,只是栈内存大小不好选择。如果太小怕溢出,太大怕浪费内存(这个缺点有点像数组)。

其次,栈的溢出危害很大,一定要避免。所以,在C语言中定义局部变量时不能定义太多或者太大(如不能定义局部变量时int a[10000];使用递归来解决问题时一定要注意递归收敛)。

相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
相关文章
|
20天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
105 14
|
24天前
|
存储 算法 C语言
【C语言】深入浅出:C语言链表的全面解析
链表是一种重要的基础数据结构,适用于频繁的插入和删除操作。通过本篇详细讲解了单链表、双向链表和循环链表的概念和实现,以及各类常用操作的示例代码。掌握链表的使用对于理解更复杂的数据结构和算法具有重要意义。
260 6
|
24天前
|
存储 网络协议 算法
【C语言】进制转换无难事:二进制、十进制、八进制与十六进制的全解析与实例
进制转换是计算机编程中常见的操作。在C语言中,了解如何在不同进制之间转换数据对于处理和显示数据非常重要。本文将详细介绍如何在二进制、十进制、八进制和十六进制之间进行转换。
34 5
|
24天前
|
C语言 开发者
【C语言】断言函数 -《深入解析C语言调试利器 !》
断言(assert)是一种调试工具,用于在程序运行时检查某些条件是否成立。如果条件不成立,断言会触发错误,并通常会终止程序的执行。断言有助于在开发和测试阶段捕捉逻辑错误。
35 5
|
1月前
|
Linux 网络安全 数据安全/隐私保护
Linux 超级强大的十六进制 dump 工具:XXD 命令,我教你应该如何使用!
在 Linux 系统中,xxd 命令是一个强大的十六进制 dump 工具,可以将文件或数据以十六进制和 ASCII 字符形式显示,帮助用户深入了解和分析数据。本文详细介绍了 xxd 命令的基本用法、高级功能及实际应用案例,包括查看文件内容、指定输出格式、写入文件、数据比较、数据提取、数据转换和数据加密解密等。通过掌握这些技巧,用户可以更高效地处理各种数据问题。
110 8
|
1月前
|
监控 Linux
如何检查 Linux 内存使用量是否耗尽?这 5 个命令堪称绝了!
本文介绍了在Linux系统中检查内存使用情况的5个常用命令:`free`、`top`、`vmstat`、`pidstat` 和 `/proc/meminfo` 文件,帮助用户准确监控内存状态,确保系统稳定运行。
399 6
|
1月前
|
Linux
在 Linux 系统中,“cd”命令用于切换当前工作目录
在 Linux 系统中,“cd”命令用于切换当前工作目录。本文详细介绍了“cd”命令的基本用法和常见技巧,包括使用“.”、“..”、“~”、绝对路径和相对路径,以及快速切换到上一次工作目录等。此外,还探讨了高级技巧,如使用通配符、结合其他命令、在脚本中使用,以及实际应用案例,帮助读者提高工作效率。
88 3
|
1月前
|
监控 安全 Linux
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景
在 Linux 系统中,网络管理是重要任务。本文介绍了常用的网络命令及其适用场景,包括 ping(测试连通性)、traceroute(跟踪路由路径)、netstat(显示网络连接信息)、nmap(网络扫描)、ifconfig 和 ip(网络接口配置)。掌握这些命令有助于高效诊断和解决网络问题,保障网络稳定运行。
80 2
|
20天前
|
Linux Shell
Linux 10 个“who”命令示例
Linux 10 个“who”命令示例
50 14
Linux 10 个“who”命令示例

相关产品

  • 物联网平台