首先看一下动态分配的代码。
1 |
int *pi= new int(0);//在栈内存中存放一个指向一块堆内存的指针pi |
在程序会先确定在堆中分配内存的大小,然后调用 operator new 分配内存,然后返回这块内存的首地址,放入栈中。
1 2 3 4 5 6 7 8 |
pc = new char[n]; 00825EFA mov eax,dword ptr [n] 00825EFD push eax 00825EFE call operator new[] (082118Bh) 00825F03 add esp,4 00825F06 mov dword ptr [ebp-0FCh],eax 00825F0C mov ecx,dword ptr [ebp-0FCh] 00825F12 mov dword ptr [pc],ecx |
补充知识:
4个数据寄存器(EAX、EBX、ECX和EDX)
2个变址和指针寄存器(ESI和EDI)
2个指针寄存器(ESP和EBP)
eax:保存所有API函数的返回值。寄存器AX和AL通常称为累加(Accumulator),用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高;
ebx:基地址寄存器(Base Register)。它可作为存储器指针来使用;
ecx:称为计数寄存器(Count Register)。在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数;
edx:称为数据寄存器(Data Register)。在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。
esp:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
ebp:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
栈的默认大小是1MB。VS中选择 项目->属性->链接器->系统->堆栈保留大小,然后输入你想要的栈大小即可。
栈与堆的区别
碎片问题:
在windows中,占用态的堆块被使用它的程序索引,而堆表只索引所有空闲态的堆块。重要的堆表有两种:空闲双向链表FreeList(空表) 和 快速单向链表Lookaside(快表)。对于堆来讲,频繁的 new/delete 势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。
对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出。
显然,堆的分配效率比栈要低得多。
生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向,(用链表来存储的空闲内存地址的,而链表的遍历方向是由低地址向高地址);对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。(定义一个栈底--这是高地址,每次压一个数据入栈,栈指针esp减去4(32位系统下),所以栈顶是向着内存低地址方向生长的。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include "stdafx.h" #include <stdio.h> using namespace std; static int stack_dir; static void find_stackorheap_dir(void){ static char *addr=NULL; //char dummy; //栈地址 char *dummy=new char(0); if(addr==NULL){ addr=dummy; find_stack_dir(); } else if(dummy>addr) stack_dir=1; else stack_dir=-1; } int _tmain(int argc, _TCHAR* argv[]){ find_stackorheap_dir(); if(stack_dir==1) puts("stack grew upward"); else puts("stack grew downward"); return 0; } |