嵌入式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
相关文章
|
3天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
19 9
|
2天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
18 6
|
3天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
17 5
|
1天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
14 4
|
3天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
1天前
|
存储 网络协议 安全
30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场
本文精选了 30 道初级网络工程师面试题,涵盖 OSI 模型、TCP/IP 协议栈、IP 地址、子网掩码、VLAN、STP、DHCP、DNS、防火墙、NAT、VPN 等基础知识和技术,帮助小白们充分准备面试,顺利踏入职场。
9 2
|
3天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
3天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
17 2
|
3月前
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
357 0
|
19天前
|
存储 C语言
数据在内存中的存储方式
本文介绍了计算机中整数和浮点数的存储方式,包括整数的原码、反码、补码,以及浮点数的IEEE754标准存储格式。同时,探讨了大小端字节序的概念及其判断方法,通过实例代码展示了这些概念的实际应用。
41 1

热门文章

最新文章

相关产品

  • 物联网平台