一、内存分配方式:
静态分配方式:是指在程序编译和链接的时候分配固定的存储空间的方式。
动态分配方式:是在程序运行期间根据需要进行动态的分配存储空间的方式。
静态内存分配是在程序执行之前进行的因而效率比较高,而动态内存分配则可以灵活的处理未知数目的。
静态与动态内存分配的主要区别如下:
静态对象是有名字的变量,可以直接对其进行操作;动态对象是没有名字的变量,需要通过指针间接地对它进行操作。
静态对象的分配与释放由编译器自动处理;动态对象的分配与释放必须由程序员显式地管理,它通过malloc,calloc和free等函数(C++中为new和delete运算符)来完成
扩展:分配内存的函数:(以下函数使用时都是需要包含库头文件:在C中#include<stdlib.h> / c++ 中 #include<cstdlib>)
1).分配内存空间函数malloc
调用形式: (类型说明符*) malloc (size) 功能:在内存的动态存储区中分配一块长度为"size" 字节的连续区域。函数的返回值为该区域的首地址。 “类型说明符”表示把该区域用于何种数据类型。(类型说明符*)表示把返回值强制转换为该类型指针。“size”是一个无符号数。例如: pc=(char *) malloc (100); 表示分配100个字节的内存空间,并强制转换为字符数组类型, 函数的返回值为指向该字符数组的指针, 把该指针赋予指针变量pc。malloc申请的内存位于堆中。调用malloc()函数之后,还需调用函数memset初始化这部分内存空间
使用方式如下:
-
int *p1;
-
p1=(int *)malloc(10*sizeof(int));
2).分配内存空间函数 calloc
calloc 也用于分配内存空间。调用形式: (类型说明符*)calloc(n,size)功能:在内存动态存储区中分配n块长度为“size”字节的连续区域。函数的返回值为该区域的首地址。(类型说明符*)用于强制类型转换。calloc函数与malloc 函数的区别仅在于一次可以分配n块区域。例如: ps=(struet stu*) calloc(2,sizeof (struct stu)); 其中的sizeof(struct stu)是求stu的结构长度。因此该语句的意思是:按stu的长度分配2块连续区域,强制转换为stu类型,并把其首地址赋予指针变量ps。动态分配的内存进行初始化,全部清零。
使用方式如下:
ptr=(struct data *)calloc (count,sizeof(strunt data)) //申请并初始化空间
3).分配内存空间函数 alloca
alloca函数用来在栈中分配size个字节的内存空间,因此函数返回时会自动释放掉空间。返回值:若分配成功返回指针,失败则返回NULL。
alloca是向栈申请内存,无需释放,alloca会将这部分内存空间初始化为0。
4).分配内存空间函数 realloc
realloc函数用来从堆上分配内存,当需要扩大一块内存空间时,realloc()试图直接从堆上当前内存段后面的字节中获得更多的内存空间,如果能够满足,则返回原指针;如果当前内存段后面的空闲字节不够,那么就使用堆上第一个能够满足这一要求的内存块,将目前的数据复制到新的位置,而将原来的数据块释放掉。如果内存不足,重新申请空间失败,则返回NULL。此函数定义如下:
void *realloc(void *ptr,size_t size)
参数ptr为先前由malloc、calloc和realloc所返回的内存指针,而参数size为新配置的内存大小。
使用如下:
-
type * ptr;
-
ptr=realloc(ptr,new_amount);
5).释放内存空间函数free
调用形式: free(void*ptr); 功能:释放ptr所指向的一块内存空间,ptr 是一个任意类型的指针变量,它指向被释放区域的首地址。被释放区应是由malloc或calloc,alloca函数所分配的区域。
在C语言中,存储区可以分成全局区,常量区,代码区,栈,堆;如下图所示(图片借别人的,有助于理解):
二、内存存储区分配结构:
程序运行时内存的结构:
1.全局区(静态区)(static):全局变量和静态变量的存储是放在这里的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。它是在程序编译时就已经分配好的,在整个运行期间都存在。程序结束后由系统释放 。在程序执行过程中它们占据固定的存储单元,而不动态地进行分配和释放。
2. 文字常量区 : 常量存储,常量字符串就是放在这里的。程序结束后由系统释放 。
3. 程序代码区 :存放函数体的二进制代码。
4.堆(heap)区:堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减,且向上增长。一般由程序员手动分配释放,如果没手动释放,在程序结束时可能由OS释放。当进程调用malloc等函数或者new运算符分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数或者delete运算符释放内存时,被释放的内存从堆中被剔除(堆被缩减)。(注意:new/delete是运算符,会调用构造和析构函数,而malloc/free是库函数,不会调用构造和析构函数。)注意它与数据结构中的堆是两回事,分配方式类似于链表。
5.栈(stack)区:栈又称堆栈,由编译器自动分配释放。用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在全局区中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。注意栈是向下增长的。使用起来也是和数据结构的栈类似。栈区属于静态分配内存,预编译阶段就确定好了大小。
补充:
栈和堆的主要区别有以下几点:
(1)管理方式不同。
栈编译器自动管理,无需程序员手工控制;而堆空间的申请释放工作由程序员控制,容易产生内存泄漏。
(2)空间大小不同。
栈是向低地址扩展的数据结构,是一块连续的内存区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,当申请的空间超过栈的剩余空间时,将提示溢出。因此,用户能从栈获得的空间较小。
堆是向高地址扩展的数据结构,是不连续的内存区域。因为系统是用链表来存储空闲内存地址的,且链表的遍历方向是由低地址向高地址。由此可见,堆获得的空间较灵活,也较大。栈中元素都是一一对应的,不会存在一个内存块从栈中间弹出的情况。
(3)是否产生碎片。
对于堆来讲,频繁的malloc/free(new/delete)势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低(虽然程序在退出后操作系统会对内存进行回收管理)。对于栈来讲,则不会存在这个问题。
(4)增长方向不同。
堆的增长方向是向上的,即向着内存地址增加的方向;栈的增长方向是向下的,即向着内存地址减小的方向。
(5)分配方式不同。
堆都是程序中由malloc()函数动态申请分配并由free()函数释放的;栈的分配和释放是由编译器完成的,栈的动态分配由alloca()函数完成,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行申请和释放的,无需手工实现。
(6)分配效率不同。
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行。堆则是C函数库提供的,它的机制很复杂,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大的空间,如果没有足够大的空间(可能是由于内存碎片太多),就有需要操作系统来重新整理内存空间,这样就有机会分到足够大小的内存,然后返回。显然,堆的效率比栈要低得多。
程序在存储时的分区:
.bss段:bss段(bss segment)通常是指用来存放程序中未初始化的或者被初始化为0的全局变量/静态变量的一块内存区域。BSS是英文BlockStarted by Symbol的简称。BSS段属于静态内存分配。其特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。bss并不给该段的数据分配空间,只是记录数据所需空间的大小.
.data段:数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。数据段为数据分配空间,数据保存在目标文件中。BSS段的大小从可执行文件中得到,然后链接器得到这个大小的内存块,紧跟在数据段后面。当这个内存区进入程序的地址空间后全部清零。包含数据段和BSS段的整个区段此时通常称为数据区。
.text段:代码段(codesegment/text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读,某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。
代码段属于程序指令,是可执行语句编译成机器代码后存在这里,而.bss 和.data段属于程序数据。程序中数据和指令是分开的。
转载至 https://blog.csdn.net/zcyzsy/article/details/69788884