堆栈¶
heap stack
堆栈都是一种数据项按序排列的 数据结构 ,只能在一端(称为栈顶(top))对数据项进行插入和删除。在单片机应用中,堆栈是个特殊的存储区,主要功能是暂时存放数据和地址,通常用来保护断点和现场。
堆heap:顺序随意,一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
栈stack:后进先出(Last-In/First-Out),由操作系统自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。通常,stack空间有限,heap的空间是很大的自由区。
堆栈对比¶
栈区由编译器自动分配,内纯的分配是连续的,堆区由程序员自行分配,需由程序员释放变量内存。
从申请方式,申请大小,申请效率简单比较:Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。Stack空间有限,Heap是很大的自由存储区。Stack申请效率高,Heap申请效率低。
堆是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程 初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。 栈是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立。每个函数都有自己的栈,栈被用来在函数之间传递参数。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。
栈空间不需要在高级语言里面显式的分配和释放。
C语言程序编译的内存分配,堆与栈的区别:
栈是由编译器自动分配释放,存放函数的参数值、局部变量的值等。操作方式类似于数据结构中的栈。
堆一般由程序员分配释放,若不释放,程序结束时可能由OS回收。注意这里说是可能,并非一定。
栈区(stack) :
//windows下,栈内存分配2M(确定的常数),超出了限制,提示stack overflow错误 //编译器自动分配释放,主要存放函数的参数值,局部变量值等; 堆区(heap):程序员手动分配释放,操作系统80%内存 全局区或静态区:存放全局变量和静态变量;程序结束时由系统释放,分为全局初始化区和全局未初始化区; 字符常量区:常量字符串放与此,程序结束时由系统释放; 程序代码区:存放函数体的二进制代码。
就算没有free(),main()结束后也是会自动释放malloc()的内存的,这里监控者是操作系统,设计严谨的操作系统会登记每一块给每一个应用程序分配的内存,这使得它能够在应用程序本身失控的情况下仍然做到有效地回收内存。你可以试一下在TaskManager里强行结束你的程序,这样显然是没有执行程序自身的free()操作的,但内存并没有发生泄漏。
free()的用处在于实时回收内存。如果你的程序很简单,那么你不写free()也没关系,在你的程序结束之前你不会用掉很多内存,不会降低系统性能;而你的程序结束之后,操作系统会替你完成这个工作。但你开始开发大型程序之后就会发现,不写free()的后果是很严重的。很可能你在程序中要重复10k次分配10M的内存,如果每次使用完内存后都用free()释放,你的程序只需要占用10M内存就能运行;但如果你不用free(),那么你的程序结束之前就会吃掉100G的内存。这其中当然包括绝大部分的虚拟内存,而由于虚拟内存的操作是要读写磁盘,因此极大地影响系统的性能。你的系统很可能因此而崩溃。
任何时候都为每一个malloc()写一个对应的free()是一个良好的编程习惯。这不但体现在处理大程序时的必要性上,更体现在程序的优良的风格和健壮性上。毕竟只有你自己的程序知道你为哪些操作分配了哪些内存以及什么时候不再需要这些内存。因此,这些内存当然最好由你自己的程序来回收。
堆栈区别¶
(1)申请方式
(2)操作系统的相应
(3)申请的大小限制
(4)申请速度
(5)堆和栈的存储内容
堆区的头部用一个字节存放堆区的大小,其他的内容由程序员自己安排;栈区在函数调用子函数的时候,首先进栈的是函数调用语句的下一条可执行语句的地址,然后是函数的各个参数进栈,在大多数C编译器中,函数的参数是从右向左一次进栈,接下类是局部变量进栈。当本次函数执行结束时候,首先出栈的是局部变量,其次是函数参数,最后是栈顶指向的可执行语句的地址。
Q: 局部变量能否和全局变量重名?
A: 能,局部会屏蔽全局。要用全局变量,需要使用"::"局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内
内存空间¶
对于一个C语言程序而言,内存空间主要由五个部分组成:代码段(.text)、数据段(.data)、静态区(.BSS)、堆和栈组成。
BSS段:BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量和静态变量 (这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,那就是不管什么类型都初始化为0,这种没有显示初始化的就是我们这里所说的未初始化。既然都是0那么就没必要把每个0都存储起来,从而节省磁盘空间,这是BSS的主要作用)的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。 BSS节不包含任何数据,只是简单的维护开始和结束的地址,即总大小,以便内存区能在运行时分配并被有效地清零。BSS节在应用程序的二进制映象文件中并不存在,即不占用磁盘空间 而只在运行的时候占用内存空间 ,所以如果全局变量和静态变量未初始化那么其可执行文件要小很多。
数据段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量和静态变量的一块内存区域。数据段属于静态内存分配,可以分为只读数据段和读写数据段。 字符串常量等,但一般都是放在只读数据段中 。
代码段:代码段(code segment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等,但一般都是放在只读数据段中 。
栈区:由系统自动分配,栈区的分配运算内置于处理器的指令集,当函数执行结束时由系统自动释放。存放局部变量。栈的缺点是:容量有限,当相应的区间被释放时,局部变量不可再使用。查询栈容量的命令:ulimits -s。栈是一块连续的区域,向高地址扩展,栈顶和容量是事先约定好的。
堆区:在程序的执行过程中才能分配,由程序员决定,编译器在编译时无法为他们分配空间,只有在程序运行时分配,所以被称为动态分配。堆是不连续的区域,向高地址扩展。由于系统用链表来描述空闲的地址空间,链表的遍历是由地地址向高地址的,故堆区是不连续的动态的存储空间。
初始化¶
全局变量在main函数第一次使用之前已经初始化,初始化可细分为:编译时初始化 和 加载时初始化,即static initialization 和 dynamic initialization。
静态初始化先于动态初始化。因为静态初始化发生在编译时期,直接写进.bss段和.data段,在程序执行时直接加载;而动态初始化则是在运行时期,由运行时库调用相应构造函数进行初始化,同样要写进.bss段或.data段。
编译时初始化¶
编译时初始化是针对于那些简单的、c++内部定义的数据结构(也称内置结构),如int/double/bool及数组的初始化,又可分为两种方式:
.bss段: 未初始化的变量,也就是我们没指定初值,编译器分配0值,编译时编译器将其分配在.bss段,不占用rom空间
.data段: 已初始化好的全局变量和静态变量,也就是我们指定了初值,编译器将其分配在.data段,占用rom空间
提示
bss段不占用rom空间,但是在内核加载到内存时,会保留相应的空间;在有些编译器中,初始化为0的静态变量和全局变量也放在.bss段
加载时初始化¶
全局类对象在main函数执行前,由加载程序完成其初始化,其无法在编译期初始化,由于那时候还无法调用类的构造函数。
同时,在加载期,是线程安全的。例如,饿汉方式的单例类:借助main执行前的加载期完成初始化,由于还在加载所以确保线程安全。
另外针对静态变量:ref:lan_c_static,若其是普通的具有本文可见性的普通静态变量,其可能在编译期(内置类型)初始化或者在加载期(类的静态成员)初始化。但针对函数内部的局部static变量,其在第一次被调用时初始化,并且只初始化一次。