b5笔记本多大(b5笔记本多大尺寸)
b5笔记本多大(b5笔记本多大尺寸)
了解有关内存安全性和效率的更多信息。
c是一种高级语言,具有“接近金属,接近金属”(LCTT译:“接近人类思维的反义词”)的特点,这使得它看起来是一种封闭而轻松的语言,而不是Java或Python兄弟。内存管理作为上述功能之一,内存管理涵盖了程序执行的安全性和有效性。本文通过C语言生成的编译语言代码段,详细描述了内存安全和效率语言代码以及现代C语言编译器的实例。
虽然代码示例是用C语言编写的,但是《安全有效的内存管理指南》同样适用于C语言,两种语言的细节不同(比如C缺乏面向对象的属性和泛型),但是内存管理的挑战是相同的。
存储器概述执行
执行程序(也称进程),内存分为栈的栈和静电区三个方面。接下来,我们将给出每个领域的概述和一个完整的代码示例。
堆不是一般的CPU寄存器,而是为代码块中的局部变量(例如,函数或riders)提供临时存储。在这种情况下,传递给函数的参数也被视为局部变量。看看这个最短的例子:
void some_func(int a,int b){ int n;......................编译器倾向于优先使用通用寄存器作为远程控制,因为CPU拥有这些寄存器的快速访问速度(时钟周期)。但是,这些寄存器对于台式机、笔记本电脑和手持机(大约16个)来说很少(大约16个)。
在实现层,只有汇编语言程序员才能看到堆栈,堆栈组织成push(插入)和pop music(删除)LIFO list(后退先出)列表。指针可以用作偏移基址;这除了是最好的外,在堆叠位置之外也是可见的。比如最高表达式是16。堆栈中的16字节指针,表达式中的个16字节指针比它低16个字节。因此,它可以通过指针访问电信托管存储堆栈的位置。在标准的arm或Intel架构中,堆栈从高内存地址向低内存地址增长;所以最好减少一个进程,也就是增加其栈的比例。
使用堆栈结构意味着简单有效地使用内存。编译器(不是程序员)会在管理栈中写代码,管理过程是通过分配和释放所需的终端存储来实现的;程序员声明函数参数和局部变量将交给编译器。此外,相同的堆栈存储可以在连续的函数调用和代码块(如循环)中重用。设计良好的模块化代码将存储栈作为Teleconcape的内存选项,并优化编译器尽可能使用通用寄存器而不是栈。
堆程序员代码显式分配存储的存储,堆的语法因语言而异。在C语言中,库函数Malloc。(或其变体)Calloc。被成功调用以等待并分配指定的字节数(在C和Java等语言中,new运算符的作用相同)。编程语言在如何释放分配的存储空间方面有很大的不同:
在Java、Go、Lisp和Python中,程序员不会显式释放动态分配的堆栈存储。
例如,下面的Java语句为字符串分配堆栈存储,并将地址存储在变量中。中间问候:
字符串问候=新字符串(“你好,世界!”);Java有一个垃圾收集器,这是一个运行时实用程序。如果这个进程不能访问你分配的栈存储,恢复可以自动释放。因此,Java堆释放是通过垃圾收集器自动执行的。在上面的例子中,当变量中的问候语超出范围后,垃圾收集器将释放字符串的堆栈存储。
Rust编译器将编写堆发布代码。这是铁锈与地面无关的地方。会带来堆释放实现自动化的开创性努力,但也会带来时空复杂度和开销。向铁锈致敬!
在C(和C)中,堆释放是程序员的任务。电话程序员Malloc。分配堆栈存储,然后卷曲库函数相应地自由释放存储空间(C中new运算符分配一堆存储,delete和delete[]运算符释放这样的存储)。下面是一个C语言代码的例子:
char * greetings=malloc(14);/* 14堆字节*/strcpy(问候,“你好,世界!”);/*将问候语到bytes */Pross(问候语);/*打印问候语*/免费(问候语);/* Free pat byte */C语言避免了垃圾收集器的成本和复杂性,但程序员只需要释放释放任务。
静态内存为可执行代码(如C语言函数)、字符串文本(如“Hello,World!”和全局变量:
(同Internationalorganizations)组织
n; / *全局变量* /int main {/ * function * / char * msg =“没有评论”; / * 字符串字面量 * / ......}该区域是静态的,因为它的大小从过程开始到结束时。由于静态区域等同于过程固定大小的存储器占用,因此经验规则是通过避免使用全局阵列来使该区域尽可能小。
本节摘要将与代码示例进行进一步解释。
堆栈存储想象一下,具有各种连续任务的程序,任务包括在网络上每隔几分钟下载并存储在本地文件中的数字数据。以下 堆程序简化了处理过程(仅将奇数整数值转换为偶数),并且将焦点放在堆栈存储器上。
#包括 - ; / *使* / } fclose(输入); / *关闭输入文件* / fwrite(nums,n,sizeof(int),输出 fclose(产出);}int main { process_data(Infile,Outfile,Intcount); / **现在执行其他任务** / Other_Task1; / *自动发布堆栈存储可用* / Other_Task2; / * ditto * / 返回0;}底部 主要的函数调用处理数据函数,此函数创建基于堆栈的阵列,其中尺寸为参数N.给定(当前示例中的128,000)。因此,阵列占据128000 * sizeof(int)字节,标准设备达到512,000字节(㈡它是这些设备上的四个字节)。然后将读取数据(使用库函数)吓唬),循环处理,并保存到本地文件outgoing.dat.(使用库函数FWRITE.。
什么时候 处理数据函数返回其呼叫者主要的当功能是处理数据可提供大约500MB的堆栈张力堆程序中的其他功能用作临时存款。在这个例子中,主要的功能下一个调用定位函数Other_Task1.和oother_task2.。这三个功能是主要的呼叫时,这意味着所有三个功能都可以使用相同的堆栈存储存储作为电信托管。因为编写堆栈管理代码是编译器而不是程序员,所以这种方法是高效的,更容易编程。
在C语言中,块内定义的任何变量(例如,函数或骑行者)都有一个 汽车存储,这意味着变量基于堆栈。店铺登记它现在已经过时了,因为C编译器将主动尝试尽可能多地使用CPU寄存器。只有在块中定义的变量也可以是登记如果没有可用的CPU寄存器,则编译器将更改为汽车。基于堆栈的编程可能是一个很好的优选方式,但这种风格确实有一些具有挑战性的。以下坏斗士该程序说明了这一点。
#包括坏斗士程序中的控制流程很简单。主要的功能使用16(LCTT翻译:原始128,应该是作者笔错误)作为参数呼叫函数get_array.然后,调用函数使用传入参数来创建相应大小的本地阵列。get_array.该函数将初始化数组并将其返回给主要的数组标识符arr.。arr.是一个指针常量,个数组㈡元素地址。
当然,本地阵列 arr.允许get_array.在函数中访问,但一次get_array.返回,您无法访问数组。尽管如此,主要的功能将尝试使用功能get_array.返回堆栈地址arr.要根据堆栈打印数组。现代编译器将警告错误。例如,以下是GNU编译器的警告:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /一般规则是,如果使用堆栈存储实现局部变量,则应仅在变量所在的代码块内访问基于堆栈的存储(在此示例中,数组指针“ arr.和循环柜台一世所有这些都是这样的部分变量)。因此,该函数永远不会返回指向堆栈存储的指针。
堆栈存储接下来,使用多个代码示例突出显示使用C语言中的堆栈存储的优势。在个示例中,使用调度分配,使用和发布堆栈存储。第二个示例(在下一节中)将堆栈存储在其他堆栈存储中,这使得通过释放操作使其复杂。
#包括更多 堆有两个功能:主要的功能使用参数(示例中的100)调用get_heap_array功能,应指定多少个参数?㈡元素。因为分配可能会失败,主要的功能将检查get_heap_array它会回来吗?空值如果是,则表示失败。如果分配成功,主要的将打印一个数组㈡值,然后立即调用库函数自由释放堆栈存储。这是解决方案。
get_heap_array以下函数下面的函数,声明值得仔细研究:
int * heap_nums = malloc(sizeof(int)* n); / *堆分配* /Malloc.库函数及其变体功能为字节;Malloc.参数是N.地区㈡类型元素所需的字节数(sizeof(int)它是标准现代设备的四个字节)。Malloc.该函数返回分配的单词段的个地址,如果失败,则返回空值。
如果成功 Malloc.在现代桌面上返回的地址大小是64位。在手持设备和更早的桌面上,此地址的大小可以是32位,甚更小,具体取决于其时代。分配阵列中的元素是㈡类型,这是一个四字节符号整数。这些堆栈被分配㈡存储在基于堆栈的局部变量中的地址heap_nums.中间。您可以参考下图:
堆基础 堆叠/ \ +----+----+ +----+ 堆号---> | INT1 | INT2 | ... | INTN | + - + ---- + + - + +一次 get_heap_array函数返回,指针变量heap_nums.堆栈存储将自动回收 - 但动态㈡数组堆栈存储仍然存在,这是get_heap_array函数返回此地址()主要的函数的原因:它现在负责打印阵列的整数,通过调用库函数自由显式发布堆栈存储:
免费(Heap_nums); / *免费堆存储* /Malloc.该函数不初始化分配的存储空间,因此它是一个随机值。相比之下,它的变体功能Calloc.将分配的商店初始化为零。两种函数都返回空值表示失败的分发。
存在 堆在该示例中,主要的函数被调用自由它将立即返回它后,正在执行的程序将终止,这将允许系统回收所有分配的堆栈存储。尽管如此,程序员应该在不需要时立即立即明确释放存储堆栈的习惯。
嵌套反应堆分配下一个代码示例将更加困难。 C语言有很多库函数,返回堆栈存储的指针。这是一个常见的用途:
如图1所示,C程序调用返回基于堆栈的存储的指针的库函数,并且存储指向通常是聚物,例如阵列或结构:
somesture * ptr = lib_function; / *返回指向堆存储的指针* /2,然后程序使用分配的存储。
3.要清洁,问题是 自由简单的呼叫将清除库函数分配的所有堆栈分配存储。例如,沉闷示例可能具有指向REAP的字段。特殊问题是动态分配的结构阵列,每个结构都有一个存储的字段,它再次指向并动态分配。以下代码示例说明了此问题,并侧重于如何设计一个安全地保护客户端以分配存储的库。
#包括 ); Free_all(HS); / *免费动态分配的存储* / 返回0;}更多 筑巢示例示例堆以居中为中心,是着名的heap_nums.指针字段:
typedef struct { 无符号ID; 无符号; float * heap_nums; / **指针** /堆;功能 get_heap_struct.struct.试图堆实例分配堆栈存储,这需要一个字段heap_nums.几个指示漂浮可变分配堆栈存储。如果成功get_heap_struct.struct.函数,并将指向指向拼接结构的指针HS.名称,结果可以描述如下:
#包括 0存在 get_heap_struct.struct.在函数中,个堆分配过程很简单:
#包括 1sizeof(堆积)包括heap_nums.字段的字节数(在32位机器上为4,64位机器,heap_nums.该字段指向动态分配阵列。漂浮元素指针。所以,关键是Malloc.为此结构传输字节空间或意味着失败空值;如果空值那get_heap_struct.struct.该功能也返回空值通知呼叫者分配失败。
尝试分配过程,第二步更复杂,因为在此步骤中。堆已分配堆栈存储:
#包括 2传递给 get_heap_struct.struct.功能参数N.动态分配heap_nums.阵列应该有多少人?漂浮元素。如果您可以分配一些必需的漂浮元素,然后返回该函数堆堆栈的结构ID和Len.场地。但是,如果您尝试分配失败,则需要两个步骤来实现解决方案:
1,必须释放 堆存储以避免内存泄漏。称呼get_heap_struct.struct.客户函数,没有动态heap_nums.大批堆也许这是无用的;所以,堆应明确发布实例的字节空间,以便系统可以回收这些空间以供将来的堆分配。
2,回归 空值未能识别。
如果成功 get_heap_struct.struct.函数,然后释放堆栈存储也很棘手,因为它涉及两次以正确的顺序自由操作。因此,该程序是设计的Free_all.功能,而不是要求程序员手动实现两步发布操作。审查,Free_all.该函数是这样的:
#包括 3检查参数 heap_struct.不空值值后,首先发布该功能。heap_nums.阵列,这一步heap_struct.指针仍然有效。发布heap_struct.练习是错误的。一次heap_nums.发布,heap_struct.它可以释放。如果heap_struct.发布,但是heap_nums.没有发布,然后在阵列中漂浮元素将泄漏:仍然分配字节空间,但无法访问 - 所以请务必记住heap_nums.。储存泄漏将持续到筑巢程序退出和系统回收泄漏字节。
关于 自由图书馆函数的注意事项将是按顺序的。回忆上面的呼叫例子:
#包括 4这些调用释放分配的存储 - 但它们 不将其操作参数设置为空值(自由该功能将获得地址的副本作为参数;因此,将副本更改为空值未更改原始地址上的参数值)。例如,成功呼叫自由之后,指针heap_struct.仍然持有分配字节的一些堆地址,但现在使用此地址将生成错误,因为自由呼叫使系统有权回收和重用这些分配的字节。
采用 空值参数呼叫自由没有意义,但没有伤害。不是空值在地址上重复呼叫自由它将导致不确定结果错误:
#包括 5内存泄漏和散装图形“内存泄漏”表示不再可访问动态分配的堆栈存储。查看相关的代码段:
#包括 6如果是个 Malloc.成功,第二Malloc.愿意的数字指针重置空值(在分配故障的情况下)或新分配的25漂浮个地址。 10最初分发漂浮一堆元素仍处于指定状态,但它不再访问它,因为数字指针要么指出,要么空值。结果是40字节(sizeof(float)* 10泄漏。
在第二个电话中 Malloc.以前,初始分配的存储空间应释放:
#包括 7即使没有泄漏,堆会随着时间的推移分段,并且系统需要分散。例如,假设两个堆叠的当前大小分别为200MB和100MB。但是,这两个堆栈不是连续的,过程 P.此时,还需要分配250MB的连续堆叠存储器。在执行分发之前,系统可能必须执行堆。碎片化给P.提供250MB的连续存储空间。碎片复杂,所以它也耗时。
内存泄漏创建已分配但不适的块,从而加速碎片。因此,不再需要的释放是一种有助于减少碎片要求的方法。
用于诊断内存泄漏的工具有许多工具可用于分析内存效率和安全性,我喜欢Valgrind。为了说明工具如何处理内存泄漏,给出了这里泄漏示例程序:
#包括主要的函数调用get_ints.功能,后者将尝试堆Malloc.32 4字节㈡然后初始化动态阵列(如果Malloc.成功)。初始化成功后,主要的功能将呼叫print_ints.功能。程序中没有呼叫。自由对应Malloc.手术;因此,内存泄漏。
如果已安装 valgrind.工具箱,以下令将检查泄漏程序是否具有内存泄漏(%是令行提示符):
#包括 9大多数输出??如下所述。正在实现左边的207683号码。 泄漏程序的过程标识符。此报告提供了泄漏位置的详细信息,此示例在主要的该函数调用get_ints.在功能中Malloc.打电话给电话。
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /0如果 主要的功能变化print_ints.电话后,加一对自由称呼,valgrind.是对的泄漏该程序提供了清洁的内存运行状况列表:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /1静电区域存储在正统C语言中,必须在所有块之外定义该函数。这是C编译器支持的一些功能,消除了在另一个功能中定义函数的可能性。我的例子都在所有块之外定义。此功能要么 静止的,即静态,也是如此外部外观,在哪里外部这是默认值。
C语言, 静止的要么外部修改后的函数和变量驻留在内存中所谓的。静态因为在执行程序期间区域大小是固定的。这两个存储类型的语法非常复杂,我们应该回顾。审查后,将有一个完整的代码示例vIvid Display Syntax详细信息。在所有块之外定义的函数或变量默认外部因此,函数和变量想要存储类型静止的它必须明确指定:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /2外部和静止的差异是该领域的范围:外部修改后的函数或变量可以跨文件(需要语句)可见。相比之下,静止的修改的功能仅限定义它在此功能的文件中可见,静止的仅修改的变量只是定义可以在文件中看到此变量(或文件中的块):
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /3如果它在所有块之外定义 静止的变量,如上面n1.此变量的范围是定义变量的文件。它在哪里定义?静止的变量,存储变量存储在内存静态区域中。
外部函数或变量在给定文件中的所有块之外定义,但此类定义的函数或变量也可以在其他文件中声明。典型的方法是标题文件陈述只要需要,可以包括此类功能或变量。在这里,这些最短的例子描述了这些棘手的问题。
认为 外部功能Foo存在file1.c.中间定义你有关键字吗?外部效果是相同的:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /4必须在其他文件(或块)中使用显着显着性 外部陈述此功能可以使其可见。以下是外部功能Foo在文件中file2.c.可见声明:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /5回想一下,该函数声明括号中没有主体,并且功能定义具有这样的主体。
为了便于查看,函数和变量声明通常被放置在头文件中。准备好声明源代码文件,然后你可以 #包括相关标题文件。下一节staticprog.该程序演示了这种方法。
直到 外部变量,规则变得更加困难(我很抱歉增加难度!)。任何外部对象 - 无论函数还是变量 - 必须定义在所有块中。此外,在所有块之外定义的变量默认外部:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /6但是,只有在变量中 定义当变量在中国初始化时,外部可以在变量中定义观察到修改(LCTT翻译:换句话说,如果以下代码int n1;加外部这条线是定义变成了陈述):
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /7当 file1.c.在定义中外部另一个文件中的变量(例如file2.c.它可以看出,变量必须是file2.c.积压陈述为了外部而不是初始化(初始化会将声明转换为定义):
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /8为了避免 外部变量很困惑,经验是陈述明显使用外部(必须),但不要定义使用(非必须和棘手)。对于函数,外部它在定义中是可选的,但在声明中是必需的。下一节staticprog.示例将这些点集成到完整的程序中。
staticprog示例staticprog.该程序包含三个文件:两个C语言源文件(static1.c.和static2.c.)和标题文件(static.h.),标题文件包含两个声明:
Badstack.c:在功能'get_array'中:badstack.c:9:10:警告:函数返回local变量的地址[-wreturn-local-addr]返回arr; / ** 错误 ** /9两个陈述 外部,一个用于阵列,另一个用于功能,强调其他地方的物体(“外部”)定义: 大批global_nums.在文件中static1.c.中心定义(没有明确)外部),功能Fill_Array.在文件中static2.c.定义(无明确)外部。每个源文件包含头文件static.h.。static1.c.该文件定义了驻留在内存静态区域中的两个数组。global_nums.和more_nums.。第二个阵列有静止的修改,这限制了其范围来定义文件文件(static1.c.。如前面提到的,外部装饰性的global_nums.它可以在多个文件中实现。
#包括 0以下 static2.c.在文件中定义Fill_Array.功能,此功能是主要的(存在static1.c.文件中的文档;Fill_Array.该函数将提供名称global_nums.的外部阵列中的元素分配值,文件中的此数组static1.c.定义定义。使用两个文件的目的是突出显示外部变量或函数可以跨文件可见。
#包括 1staticprog.该程序可以编译如下:
#包括 2查看汇编语言的更多详细信息现代C编译器可以处理C和汇编语言的任何组。编译C源文件时,编译器首先将C代码转换为汇编语言。这是对从上面的 static1.c.保存由文件生成的汇编语言:
#包括 3生成的文件是 static1.s.。这是文件顶部的代码,添加了一个行号以提高可读性:
#包括 4如 。文件汇编语言指令(如第1行)从句子开始。顾名思义,指令将指导汇编程序将汇编语言转换为机器代码。。罗迪塔令(行6)表示以下是只读对象,包括字符串常量。"%4i\t%4i\n"(第8行),主要的该功能(第12行)使用此字符串常量来实现格式输出。作为标签介绍(通过结肠实现)主要的功能(第12行),也只读。
在汇编语言中,标签是地址。标签 主要的:(12号线)标记主要的功能代码的地址开始,标签.lc0:(链7)格式化字符串的地址开始。
global_nums.(第3行)和more_nums.(第4章)阵列的定义包含两个数字:400是每个阵列中的总字节数,32个是每个数组(包括100㈡元素中的每个元素的位数。 (在第5行.comm.指示表示共同名称,可以忽略。的)
两个数组定义之间的差异是 more_nums.标记为。当地的(第4行),这意味着其范围仅限于其文件static1.s.。相比之下,global_nums.数组可以在多个文件中实现可见,包括static1.c.和static2.c.编译文件转换为文档。
,。文本说明在汇编代码段(第2行和9号)现了两次。术语“文本”表示“只读”,但它还涵盖了一些读/写变量,例如两个数组中的元素。虽然本文中所示的汇编语言适用于英特尔架构,但ARM6装配非常相似。对于这两个架构,。文本区域中的变量(此示例中的两个数组中的元素)将自动初始化为零。
总结C语言中的记忆有效和内存安全程序指南很容易解释,但可能难以遵循,特别是在调用精心设计的库时。指南如下:
尽可能使用堆栈存储,然后鼓励编译器使用通用寄存器作为远程控制,实现优化。堆栈存储表示有效的内存使用情况,并促进代码的整洁和模块化。永远不要返回指向基于堆栈的存储的指针。仔细使用堆栈存储。 C(和C ++)中的主要硬点是确保尽快释放动态分配的存储。良好的编程习惯和工具(如 valgrind.)有助于直接向这些武术点缀。例如,优先考虑提供释放功能的库,例如筑巢代码示例Free_all.释放功能。谨慎使用静态存储,因为此内存会影响到开始到结束的过程的内存。特别是,尽量避免使用 外部和静止的大批。本文可以在我的网站(https://condor.depaul.edu/mkalin)上找到。
Via:https://opensourc网站站点" rel="nofollow" />