外墙保温板价格是多少|四川苏洋建材有限公司

四川建材 2021-10-18 阅读:183

外墙保温板价格是多少

  了解有关内存安全和效率的更多信息。

  外墙保温板价格是多少|四川苏洋建材有限公司  外墙保温板价格是多少 第1张

  C 是一种高级语言,同时具有“接近金属close-to-the-metal”(LCTT 译注:即“接近人类思维方式”的反义词)的特性,这使得它有时看起来更像是一种可移植的汇编语言,而不像 Java 或 Python 这样的兄弟语言。内存管理作为上述特性之一,涵盖了正在执行的程序对内存的安全和高效使用。本文通过 C 语言代码示例,以及现代 C 语言编译器生成的汇编语言代码段,详细介绍了内存安全性和效率。

  尽管代码示例是用 C 语言编写的,但安全高效的内存管理指南对于 C++ 是同样适用的。这两种语言在很多细节上有所不同(例如,C++ 具有 C 所缺乏的面向对象特性和泛型),但在内存管理方面面临的挑战是一样的。

  执行中程序的内存概述

  对于正在执行的程序(又名 进程process),内存被划分为三个区域:栈stack、堆heap和静态区static area。下文会给出每个区域的概述,以及完整的代码示例。

  作为通用 CPU 寄存器的替补,栈为代码块(例如函数或循环体)中的局部变量提供暂存器存储。传递给函数的参数在此上下文中也视作局部变量。看一下下面这个简短的示例:

  通过的存储会在栈中,除非编译器可以找到通用寄存器。编译器倾向于优先将通用寄存器用作暂存器,因为 CPU 对这些寄存器的访问速度很快(一个时钟周期)。然而,这些寄存器在台式机、笔记本电脑和手持机器的标准架构上很少(大约 16 个)。在只有汇编语言程序员才能看到的实施层面,栈被组织为具有(删除)操作的 LIFO(后进先出)列表。指针可以作为偏移的基地址;这样,除了之外的栈位置也变得可访问了。例如,表达式指向堆栈的指针上方 16 个字节的位置,表达式指向指针下方 16 个字节的位置。因此,可以通过指针访问实现了暂存器存储的栈的位置。在标准的 ARM 或 Intel 架构中,栈从高内存地址增长到低内存地址;因此,减小某进程的就是增大其栈规模。

  使用栈结构就意味着轻松高效地使用内存。编译器(而非程序员)会编写管理栈的代码,管理过程通过分配和释放所需的暂存器存储来实现;程序员声明函数参数和局部变量,将实现过程交给编译器。此外,完全相同的栈存储可以在连续的函数调用和代码块(如循环)中重复使用。精心设计的模块化代码会将栈存储作为暂存器的首选内存选项,同时优化编译器要尽可能使用通用寄存器而不是栈。

  堆提供的存储是通过程序员代码显式分配的,堆分配的语法因语言而异。在 C 中,成功调用库函数等)会分配指定数量的字节(在 C++ 和 Java 等语言中,运算符具有相同的用途)。编程语言在如何释放堆分配的存储方面有着巨大的差异:

  在 Java、Go、Lisp 和 Python 等语言中,程序员不会显式释放动态分配的堆存储。

  例如,下面这个 Java 语句为一个字符串分配了堆存储,并将这个堆存储的地址存储在变量Java 有一个垃圾回收器,它是一个运行时实用程序,如果进程无法再访问自己分配的堆存储,回收器可以使其自动释放。因此,Java 堆释放是通过垃圾收集器自动进行的。在上面的示例中,垃圾收集器将在变量超出作用域后,释放字符串的堆存储。

  Rust 编译器会编写堆释放代码。这是 Rust 在不依赖垃圾回收器的情况下,使堆释放实现自动化的开创性努力,但这也会带来运行时复杂性和开销。向 Rust 的努力致敬!

  在 C(和 C++)中,堆释放是程序员的任务。程序员调用分配堆存储,然后负责相应地调用库函数来释放该存储空间(在 C++ 中,运算符分配堆存储,而和运算符释放此类存储)。下面是一个 C 语言代码示例:

  C 语言避免了垃圾回收器的成本和复杂性,但也不过是让程序员承担了堆释放的任务。

  内存的 静态区为可执行代码(例如 C 语言函数)、字符串文字(例如“Hello, world!”)和全局变量提供存储空间:

  该区域是静态的,因为它的大小从进程执行开始到结束都固定不变。由于静态区相当于进程固定大小的内存占用,因此经验法则是通过避免使用全局数组等方法来使该区域尽可能小。

  下文会结合代码示例对本节概述展开进一步讲解。

  栈存储想象一个有各种连续执行的任务的程序,任务包括了处理每隔几分钟通过网络下载并存储在本地文件中的数字数据。下面的程序简化了处理流程(仅是将奇数整数值转换为偶数),而将重点放在栈存储的好处上。

  底部的 函数首先调用函数,该函数会创建一个基于栈的数组,其大小由参数给定(当前示例中为 128,000)。因此,该数组占用个字节,在标准设备上达到了 512,000 字节(在这些设备上是四个字节)。然后数据会被读入数组(使用库函数),循环处理,并保存到本地文件(使用库函数)。

  当 函数返回到其调用者函数时,函数的大约 500MB 栈暂存器可供程序中的其他函数用作暂存器。在此示例中,函数接下来调用存根函数和。这三个函数在中依次调用,这意味着所有三个函数都可以使用相同的堆栈存储作为暂存器。因为编写栈管理代码的是编译器而不是程序员,所以这种方法对程序员来说既高效又容易。

  接下来使用若干代码示例凸显在 C 语言中使用堆存储的优点。在第一个示例中,使用了最优方案分配、使用和释放堆存储。第二个示例(在下一节中)将堆存储嵌套在了其他堆存储中,这会使其释放操作变得复杂。

  上面的程序有两个函数:函数使用参数(示例中为 100)调用函数,参数用来指定数组应该有多少个元素。因为堆分配可能会失败,函数会检查是否返回了;如果是,则表示失败。如果分配成功,将打印数组中的值,然后立即调用库函数来对堆存储解除分配。这就是最优的方案。函数以下列语句开头,该语句值得仔细研究一下:库函数及其变体函数针对字节进行操作;因此,的参数是类型元素所需的字节数(在标准现代设备上是四个字节)。函数返回所分配字节段的首地址,如果失败则返回如果成功调用,在现代台式机上其返回的地址大小为 64 位。在手持设备和早些时候的台式机上,该地址的大小可能是 32 位,或者甚至更小,具体取决于其年代。堆分配数组中的元素是类型,这是一个四字节的有符号整数。这些堆分配的的地址存储在基于栈的局部变量中。可以参考下图:一旦函数返回,指针变量的栈存储将自动回收——但动态数组的堆存储仍然存在,这就是函数返回这个地址(的副本)给函数的原因:它现在负责在打印数组的整数后,通过调用库函数函数不外墙保温板价格是多少会初始化堆分配的存储空间,因此里面是随机值。相比之下,其变体函数会将分配的存储初始化为零。这两个函数都返回来表示分配失败。在示例中,函数在调用后会立即返回,正在执行的程序会终止,这会让系统回收所有已分配的堆存储。尽管如此,程序员应该养成在不再需要时立即显式释放堆存储的习惯。嵌套堆分配

  下一个代码示例会更棘手一些。C 语言有很多返回指向堆存储的指针的库函数。下面是一个常见的使用情景:

  1、C 程序调用一个库函数,该函数返回一个指向基于堆的存储的指针,而指向的存储通常是一个聚合体,如数组或结构体:

  2、 然后程序使用所分配的存储。

  的简单调用是否会清理库函数分配的所有堆分配存储。例如,实例可能有指向堆分配存储的字段。一个特别麻烦的情况是动态分配的结构体数组,每个结构体有一个指向又一层动态分配的存储的字段。下面的代码示例说明了这个问题,并重点关注了如何设计一个可以安全地为客户端提供堆分配存储的库。上面的为中心,结构体中又有名为函数尝试为实例分配堆存储,这需要为字段指向的若干个变量分配堆存储。如果成功调用函数,并将指向堆分配结构体的指针以命名,其结果可以描述如下:在包括了字段的字节数(32 位机器上为 4,64 位机器上为 8),字段则是指向动态分配数组中的元素的指针。那么,问题关键在于为这个结构体传送了字节空间还是表示失败的;如果是,函数就也返回以通知调用者堆分配失败。第二步尝试堆分配的过程更复杂,因为在这一步,传递给函数的参数数组中应该有多少个元素。如果可以分配所需的若干个元素,则该函数在返回的堆地址之前会设置结构的和字段。 但是,如果尝试分配失败,则需要两个步骤来实现最优方案:1、 必须释放的存储以避免内存泄漏。对于调用的客户端函数而言,没有动态数组的可能就是没用的;因此,实例的字节空间应该显式释放,以便系统可以回收这些空间用于未来的堆分配。如果成功调用函数,那么释放堆存储也很棘手,因为它涉及要以正确顺序进行的两次操作。因此,该程序设计了一个函数,而不是要求程序员再去手动实现两步释放操作。回顾一下,检查完参数不是值后,函数首先释放数组,这步要求指针此时仍然是有效的。先释放的做法是错误的。一旦被释放,就可以释放了。如果被释放,但没有被释放,那么数组中的元素就会泄漏:仍然分配了字节空间,但无法被访问到——因此一定要记得释放。存储泄漏将一直持续,直到程序退出,系统回收泄漏的字节时为止。关于库函数的注意事项就是要有顺序。回想一下上面的调用示例:这些调用释放了分配的存储空间——但它们并 不是将它们的操作参数设置为函数会获取地址的副本作为参数;因此,将副本更改为并不会改变原地址上的参数值)。例如,在成功调用之后,指针仍然持有一些堆分配字节的堆地址,但是现在使用这个地址将会产生错误,因为对使用参数调用没有意义,但也没有什么坏处。而在非的地址上重复调用会导致不确定结果的错误:内存泄漏和堆碎片化

  “内存泄漏”是指动态分配的堆存储变得不再可访问。看一下相关的代码段:

  假如第一个成功,第二个会再将指针重置为(分配失败情况下)或是新分配的 25 个中第一个的地址。最初分配的 10 个元素的堆存储仍然处于被分配状态,但此时已无法再对其访问,因为指针要么指向别处,要么是。结果就是造成了 40 个字节()的泄漏。在第二次调用即使没有泄漏,堆也会随着时间的推移而碎片化,需要对系统进行碎片整理。例如,假设两个最大的堆块当前的大小分别为 200MB 和 100MB。然而,这两个堆块并不连续,进程此时又需要分配 250MB 的连续堆存储。在进行分配之前,系统可能要对堆进行碎片整理以给提供 250MB 连续存储空间。碎片整理很复杂,因此也很耗时。

  内存泄漏会创建处于已分配状态但不可访问的堆块,从而会加速碎片化。因此,释放不再需要的堆存储是程序员帮助减少碎片整理需求的一种方式。

  诊断内存泄漏的工具有很多工具可用于分析内存效率和安全性,其中我最喜欢的是 valgrind。为了说明该工具如何处理内存泄漏,这里给出函数,后者会试着从堆中32 个 4 字节的,然后初始化动态数组(如果成功)。初始化成功后,函数会调用函数。程序中并没有调用如果安装了工具箱,下面的命令会检查程序是否存在内存泄漏(绝大部分输出都在下面给出了。左边的数字 207683 是正在执行的程序的进程标识符。这份报告给出了泄漏发生位置的详细信息,本例中位置是在函数所调用的函数中对的调用处。如果把函数改成在对的调用之后,再加上一个对的调用,就会对程序给出一个干净的内存健康清单:静态区存储在正统的 C 语言中,函数必须在所有块之外定义。这是一些 C 编译器支持的特性,杜绝了在另一个函数体内定义一个函数的可能。我举的例子都是在所有块之外定义的函数。这样的函数要么是,即静态的,要么是C 语言中,以或修饰的函数和变量驻留在内存中所谓的静态区中,因为在程序执行期间该区域大小是固定不变的。这两个存储类型的语法非常复杂,我们应该回顾一下。在回顾之后,会有一个完整的代码示例来生动展示语法细节。在所有块之外定义的函数或变量默认为;因此,函数和变量要想存储类型为的区别在于作用域:修饰的函数或变量可以实现跨文件可见(需要声明)。相比之下,修饰的函数仅在定义该函数的文件中可见,而修饰的变量仅在定义该变量的文件(或文件中的块)中可见:如果在所有块之外定义了变量,例如上面的,该变量的作用域就是定义变量的文件。无论在何处定义函数或变量在给定文件中的所有块之外定义,但这样定义的函数或变量也可以在其他文件中声明。典型的做法是在头文件中声明这样的函数或变量,只要需要就可以包含进来。下面这些简短的例子阐述了这些棘手的问题。假设函数在中定义,有无关键字必须在其他文件(或其中的块)中使用显式的声明此函数才能使其可见。以下是使函数在文件中可见的声明语句:

  回想一下,函数声明没有用大括号括起来的主体,而函数定义会有这样的主体。

  为了便于查看,函数和变量声明通常会放在头文件中。准备好需要声明的源代码文件,然后就可以相关的头文件。下一节中的程序演示了这种方法。至于的变量,规则就变得更棘手了(很抱歉增加了难度!)。任何的对象——无论函数或变量——必须定义在所有块之外。此外,在所有块之外定义的变量默认为但是,只有在变量的 定义中显式初始化变量时,才能在变量的定义中显式修饰(LCTT 译注:换言之,如果下列代码中的行前加上,该行就由定义变成了声明):要使在中定义为的变量在另一个文件(例如)中可见,该变量必须在中显式声明为为了避免与变量混淆,经验是在声明中显式使用(必须),但不要在定义中使用(非必须且棘手)。对于函数,在定义中是可选使用的,但在声明中是必须使用的。下一节中的示例会把这些点整合到一个完整的程序中。程序由三个文件组成:两个 C 语言源文件(和)以及一个头文件(两个声明中的,一个用于数组,另一个用于函数,强调对象在别处(“外部”)定义:数组在文件中定义(没有显式的),函数在文件中定义(也没有显式的)。每个源文件都包含了头文件。文件定义了两个驻留在内存静态区域中的数组(和)。第二个数组有修饰,这将其作用域限制为定义数组的文件 ()。如前所述,修饰的则可以实现在多个文件中可见。下面的函数,该函数由(在文件中)调用;函数会给名为的数组中的元素赋值,该数组在文件中定义。使用两个文件的唯一目的是凸显变量或函数能够跨文件可见。现代 C 编译器能够处理 C 和汇编语言的任意组合。编译 C 源文件时,编译器首先将 C 代码翻译成汇编语言。这是对从上文文件生成的汇编语言进行保存的命令:生成的文件就是。这是文件顶部的一段代码,额外添加了行号以提高可读性:诸如(第 1 行)之类的汇编语言指令以句点开头。顾名思义,指令会指导汇编程序将汇编语言翻译成机器代码。指令(第 6 行)表示后面是只读对象,包括字符串常量(第 8 行),函数(第 12 行)会使用此字符串常量来实现格式化输出。作为标签引入(通过末尾的冒号实现)的在汇编语言中,标签就是地址。标签(第 12 行)标记了函数代码开始的地址,标签(第 3 行)和(第 4 行)数组的定义包含了两个数字:400 是每个数组中的总字节数,32 是每个数组(含 100 个元素)中每个元素的比特数。(第 5 行中的指令表示两个数组定义的不同之处在于被标记为(第 4 行),这意味着其作用域仅限于其所在文件。相比之下,数组就能在多个文件中实现可见,包括由和文件翻译成的汇编文件。最后,指令在汇编代码段中出现了两次(第 2 行和第 9 行)。术语“text”表示“只读”,但也会涵盖一些读/写变量,例如两个数组中的元素。尽管本文展示的汇编语言是针对 Intel 架构的,但 Arm6 汇编也非常相似。对于这两种架构,区域中的变量(本例中为两个数组中的元素)会自动初始化为零。总结

  C 语言中的内存高效和内存安全编程准则很容易说明,但可能会很难遵循,尤其是在调用设计不佳的库的时候。准则如下:

  尽可能使用栈存储,进而鼓励编译器将通用寄存器用作暂存器,实现优化。栈存储代表了高效的内存使用并促进了代码的整洁和模块化。永远不要返回指向基于栈的存储的指针。小心使用堆存储。C(和 C++)中的重难点是确保动态分配的存储尽快解除分配。良好的编程习惯和工具(如 )有助于攻关这些重难点。优先选用自身提供释放函数的库,例如代码示例中的释放函数。谨慎使用静态存储,因为这种存储会自始至终地影响进程的内存占用。特别是尽量避免使用 和数组。

  本文 C 语言代码示例可在我的网站(https://condor.depaul.edu/mkalin)上找到。

  via: https://opensource.com/article/21/8/memory-programming-c

  本文由 LCTT原创编译,Linux中国荣誉推出


金属外墙保温板 外墙一体保温板 一体化外墙保温板 外墙保温板设备 目前外墙保温岩棉保温板价格是多少一平方 外墙保温板厂家


这是外墙保温板外墙保温板施工 16:32:59)

评论(0)