
首先,让我们看一下程序内存分配的主要类型:
C / C ++编译器占用的内存分为以下几部分
1、堆栈区域(堆栈),由编译器自动分配和释放,用于存储函数参数值,局部变量值等。操作模式类似于数据结构中的堆栈。
2、堆(heap)(堆)—通常由程序员分配和释放,如果程序员不释放,则程序结束时OS可以将其回收。请注意,它与数据结构中的堆不同,并且分配方法类似于链表,哈哈。
3、全局区域(静态区域)(静态)-全局变量和静态变量存储在一起,初始化的全局变量和静态变量位于一个区域,未初始化的全局变量和未初始化的静态变量与另一个区域相邻。 -程序结束后由系统释放。 ->分别为数据区,bbs区
4、文本常量区域-常量字符串放在此处。程序结束后由系统释放->进入区域
5、程序代码区-存储函数体的二进制代码。 ->代码区
经典示例:

#includeint a=0; //全局初始化区 char *p1; //全局未初始化区 void main() { int b;//栈 char s[]="abc"; //栈 char *p2; //栈 char *p3="123456"; //123456\0在常量区,p3在栈上。 static int c=0; //全局(静态)初始化区 p1 = (char*)malloc(10); p2 = (char*)malloc(20); //分配得来的10和20字节的区域就在堆区。 strcpy(p1,"123456"); //123456\0放在常量区,编译器可能会将它与p3所向"123456\0"优化成一个地方。 }

一、堆和堆栈(堆和堆栈的理论知识)有什么区别?
2.1如何申请
堆栈:
由系统自动分配。例如,在函数中声明一个局部变量int b。系统会自动为堆栈中的b打开空间
堆:
需要自己申请程序员,并在c中指定大小,malloc函数
例如p1 =(char *)malloc(10);
在C ++中使用new运算符
例如p2 =(char *)malloc(10);
但是请注意,p 1、 p2本身在堆栈中。
2.2申请后的系统响应
堆栈:只要堆栈的剩余空间大于请求的空间,系统就会为程序提供内存,否则将报告异常以指示堆栈溢出。
堆:首先,您应该知道操作系统具有可用内存地址的链接列表。当系统收到程序请求时,它将遍历链接列表以查找第一个堆节点,该第一个堆节点的空间大于请求的空间,然后从“空闲节点”列表中删除该节点。删除空闲节点列表,并将该节点空间分配给程序。此外,对于大多数系统,分配大小将记录在该内存空间的第一个地址上,以便代码中的delete语句可以正确释放内存空间。另外,由于找到的堆节点的大小可能不完全等于应用程序的大小,因此系统会自动将多余的部分再次放入空闲列表中。
2.3应用程序大小限制
堆栈:在Windows中,堆栈是一个扩展到低位地址的数据结构,并且是一个连续的内存区域。这句话意味着堆栈的最高地址和堆栈的最大容量是由系统预先定义的。在WINDOWS下,堆栈大小为2M(或简而言之为1M,在编译时为常数)。如果应用程序空间超过了堆栈的剩余空间,则会提示溢出。因此,可以从堆栈中获得更少的空间。
堆:堆是一种扩展到高地址的数据结构,并且是一个不连续的内存区域。这是因为系统使用链表存储空闲内存地址,该地址自然是不连续的,并且链表的遍历方向是从低地址到高地址。堆大小受计算机系统中有效虚拟内存的限制。可以看出,堆获得的空间更加灵活,更大。
2.4应用效率比较:
系统自动分配堆栈,速度更快。但是程序员是无法控制的。
堆是通过new分配内存的,这通常很慢,并且容易出现内存碎片,但是使用起来最方便。
此外,在WINDOWS下,最好的方法是使用VirtualAlloc分配内存。尽管使用起来最不方便,但它既不在堆中,也不直接保留在堆栈的进程地址空间中。但这是最快,最灵活的。
2.5堆中的堆和存储内容
堆栈:调用函数时,第一次压入堆栈是主函数中下一条指令的地址(函数调用语句的下一个可执行语句),然后是函数的参数。在大多数C编译器中,参数It从右到左堆叠,然后是函数中的局部变量。请注意,静态变量未堆叠。
此函数调用结束时,局部变量首先从堆栈中弹出,然后是参数,最后堆栈的顶部指针指向初始存储地址,这是主函数中的下一条指令,以及该程序从此时开始继续运行。
堆:通常,一个字节用于将堆大小存储在堆的开头。堆中的特定内容由程序员安排。

2.6访问效率比较
char s1[] = "aaaaaaaaaaaaaaa"; char *s2 = "bbbbbbbbbbbbbbbbb";
aaaaaaaaaaaaa在运行时分配;
bbbbbbbbbbb是在编译时确定的;
但是,在后续访问中,堆栈上的数组比指针所指向的字符串(例如堆)要快。
例如:

#include
void main()
{
char a = 1;
char c[] = "1234567890";
char *p ="1234567890";
a = c[1];
a = p[1];
return;
}
相应的汇编代码

10: a = c[1]; 00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 0040106A 88 4D FC mov byte ptr [ebp-4],cl 11: a = p[1]; 0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 00401070 8A 42 01 mov al,byte ptr [edx+1] 00401073 88 45 FC mov byte ptr [ebp-4],al

第一种在读取时将字符串中的元素直接读取到寄存器cl中,而第二种首先将指针值读取到edx中。根据edx读取字符显然很慢。
2.7摘要:
堆和栈之间的区别可以通过以下隐喻看出:
使用堆栈就像去餐馆吃饭。只需点餐(发出申请),付款和吃饭(使用),然后在吃饱的时候离开即可。您不必担心切菜,洗菜和洗碗等准备工作。他的优点是可以快速清洗锅和其他整理工作,但是他的自由度很小。
使用堆就像制作自己喜欢的菜肴,这比较麻烦,但是更符合您自己的口味,并且具有很大的自由度。
全局变量或静态变量,它们都在堆中
局部变量放在堆栈中
堆区,也称为空闲存储区。
为什么说在堆上分配内存比在堆栈上分配内存慢?
1.堆空间开发需要使用系统函数,并且指针直接在堆栈上修改。
2.堆空间管理需要系统核算。堆栈上的空间可以由编译器管理或存储在处理器寄存器中。
3.释放堆空间需要系统管理,并且可以直接丢弃堆栈上的释放。堆空间需要由堆栈上的指针间接引用,因此访问速度很慢
请记住,apue2中有一段关于线程的内容。一般意义是线程具有自己的堆栈,并且可以在堆栈上分配内存,例如结构。如果线程调用pthread_exit()返回此值,请在结构指针时间之后格外小心,因为此结构中的成员值很可能会改变。这是可以理解的,因为同一进程中的所有线程资源都是共享的。当该线程退出时,先前使用的堆栈的一部分可能是“其他线程”占用的,但是与此同时,它表示如果malloc不会出现此类问题,
例如,只要esp-4足够,就可以在堆栈上划分一个整数,
在堆上,系统应记录分配的内存信息以释放它
顺便说一句:
堆栈井然有序
堆失灵了
--------------------------------
共有三种内存分配方式:
1.是从静态存储区分配的。在编译程序时分配内存,并且该内存在程序的整个运行期间都存在。例如,全局变量,静态变量。
2.在堆栈上创建。执行函数时,可以在堆栈上创建函数中的局部变量存储单元,并且在函数执行结束时会自动释放这些存储单元。堆栈内存分配操作内置在处理器指令集中,虽然效率很高,但是分配的内存容量有限。

3.从堆分配,也称为动态内存分配。程序运行时,使用malloc或new申请任何数量的内存,程序员负责何时使用free或delete释放内存。动态内存的寿命由我们决定。它使用起来非常灵活,但是问题最多。
-------------------------------------
通常来说,堆栈(stack)通常是指堆栈。先入后出,它是一个内存区域。用于存储程序局部变量,临时变量,函数参数,返回地址等。在该区域中,变量分配和释放由系统自动执行。不需要用户参与。
堆上的空间(先进先出)由用户分配并由用户释放。
二、函数调用和堆栈
1)编译器通常使用堆栈来存储函数参数,并使用局部变量来实现函数调用。有时函数具有嵌套调用。这时,堆栈中将有多个功能信息,并且每个功能都占据一个连续的区域。函数占用的区域称为框架()。同时,堆栈是与线程无关的,并且每个线程都有自己的堆栈。例如,以下简单函数调用:

此外,函数堆栈的清除方法确定在函数调用结束时,调用函数或被调用函数将清除函数框架。有两种方法可以清除VC中的函数堆栈:
参数传输顺序
谁负责清理参数占用的堆栈
__ stdcall
从右到左
被调用函数
__ cdecl
从右到左
来电者
2)基于以上知识,我们来仔细看看调用函数时堆栈的变化:
代码如下:

int Add(int x, int y)
{
return x + y;
}
void main()
{
int *pi = new int(10);
int *pj = new int(20);
int result = 0;
result = Add(*pi,*pj);
delete pi;
delete pj;
}
对于以上代码,我们将其分为四个步骤。当然,我们只画了代码对堆栈的影响。对于其他人,我们认为它们不存在,哈哈!
首先,int * pi = newint(10); int * pj = newint(20); intresult = 0;)堆栈更改如下:

第二,添加(* pi,* pj);堆栈如下(将函数参数放入堆栈:从右到左):

第三,将Add的结果提供给result,堆栈如下:

第四,deletepi; deletepj;堆栈如下:

第五,当main()退出时,堆栈如下所示,相当于在执行主程序之前哈哈!

示例1 --------------------------------------------- -------------------------------

【参见】http://blog.csdn.net/wudaijun/article/details/8135205 #includeusing namespace std; int main() { char p[] ="123456"; // char s[10]; // 正常复制: 123456 -- 123456 char s[4]; // 栈溢出(目标栈空间不够大), output: 56 -- 123456 char *ptr = p + 3; strcpy(s, p); cout<< p << " -- " << s << " --- " << ptr << endl; return 0; } // 栈 内存分配方式 (地址:高(左)->低(右); 数据写入方向:低(右)->高(左)) // /0 6 5 4(ptr) 3 2 1(p) (s) // char p[] ="123456";//char s[4]; // /0 6 5 4(ptr) /0 6 5(p) 4 3 2 1(s) //strcpy(s, p); // output : 56 -- 123456 --- 456 // 解析:在这里我们可以知道p=s+4; 然后我们对s进行写入"123456" s所在的四个字节不够用 所以"56"(包括后面的/0)均被写入了p地址 因此输出p将输出56


结果讨论:
关于堆栈:三个功能:
1.功能堆栈存储器与一个地址或四个字节对齐。
2.地址从高地址分配到低地址。
3.数据从低地址写入高地址。
示例2 --------------------------------------------- -------------------------------

#includeusing namespace std; int main() { char p[] ="123456"; int c=0; char s[4]; strcpy(s, p); cout< 123456
13877
请按任意键继续...
内存跟踪:
在strcpy之前:
strcpy之后:
结果讨论:
这里的区别是在p和s之间插入了c。他们的地址大小顺序为:s
那么,如何获得c的值?跟踪地址可以知道c最初表示为:00 00 0000。将s写入“ 123456”后,c的值可以为:35 36 00 00
由于操作系统的小尾数表示形式,在c的四个字节中,高地址(右)存储值的高位,而低地址(左)存储地址的低位,所以c的实际值放置为
00 00 36 35转换为十进制以获得13877
示例3 --------------------------------------------- -------------------------------
堆的分配规则与堆栈不同。堆的地址从小到大分配,并且两个连续内存块的起始地址肯定不同。
写在前面:关于内存的所谓空闲区域值:
经常编写代码而忘记初始化的人们可能会注意到,每次定义整数变量而不进行初始化时,每次都会得到很大的负数。这不是说未分配的内存是随机的吗?看这个例子:
int main() { int a; int* pb = new int; cout<<"a is from stack, b is from heap"<
vc ++6.0输出结果:
我相信这里出现的两个单词是每个人都熟悉的,甚至上面的两个负整数也应该非常熟悉。接下来,让我们找到答案:
默认情况下将初始化堆栈和堆的空闲区域(被覆盖之前),将堆栈内存初始化为CC,并将堆初始化为CD。乍一看,了解这一点没有多大意义,但这是下面描述的某些事情的基础。
示例2:
int main() { int*a = new int; int*b = new int; int*c = new int; delete a; delete b; delete c; return 0; }
调试并获取
a = 00382a48 b = 00382a90 c = 00382ad8内存跟踪:
它包含很多信息
1. b指针(00382a90))所指向的地址默认情况下初始化为CD(在未覆盖堆之前)
2.在空闲区域的两端有四个字节的定界符。这是FD FD FD FD
3.继续查找00382a70,地址为00382a28
4.下一个地址00382a74的地址为00383ab8
在这里我们还可以假设这两个地址是分配的内存的下一个和上一个块
接下来,我们将跟踪00382a28:
在地址下面不远处,我们找到了00382a48,它是指针a指向的地址。所以在这里我们可以看到:
堆中的每个内存块都连接到一个链表,每个空闲区域都封装在一个结构中
此结构至少应包含:
1.数据块的前继和后继
2.数据块大小
3.数据块定界符
4.数据块本身
在这里我们可以看到放置在数据块前面的数据区域的大小= 00382a48-00382a28 = 32字节
(
00382a48-00382a28 = 32字节
48(H)= 0100 1000(B)= 72(D)
28(高)= 0010 1000(B)= 40(深)
72-40 = 32
)
同时分析另一部分数据,初步猜测:
由00382a38分配的元素类型占用的字节数。这是sizeof(int)。
00382a3c分配此类型的元素数。
在这里您还可以理解为什么数组动态分配int * p = new int [5]只需要删除[] p即可,因为该结构节省了元素数量
对于CD,CC仅用于未使用的内存,例如堆栈等动态收缩的结构。相同的内存将在不同的时间存储不同的变量,并且仅在使用后回收顶部指针数据时才更改它。仍然不是CC。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-324340-1.html
说下感受吧