第一篇:编程中内存分配总结
栈, 临时变量,大小有限制,函数返回后就被回收
堆,可以动态分配,大小可近似认为无限制,可以一直存在
堆:malloc(), free()需要自己申请与自己负责释放!适用于事先不知道需要分配多大空间的情况。
栈:void fun(int x){int y;...}系统帮你分配与释放。
/////////////////////////////////////////////////////////////////// 一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap)— 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回
收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。已初始化全局/静态变量,在整个软件执行过程中有效;.bss函数调用栈,其中的内容在函数执行期间有效,并由编译器负责分配和收回;
.heap程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、堆和栈的理论知识 2.1申请方式
stack: 由系统自动分配。例如,声明在函数中一个局部变量 int b;系统自动在栈中为b开辟空间 heap: 需要程序员自己申请,并指明大小,在c中malloc函数 如p1 =(char *)malloc(10);在C++中用new运算符 如p2 = new char[10];但是注意p1、p2本身是在栈中的。
2.2 申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢 出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表
中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的
首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部
分重新放入空闲链表中。
2.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意
思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有 的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将
提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储 的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小
受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
2.4申请效率的比较:
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是
直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。
2.5堆和栈中的存储内容
栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可
执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈 的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地
址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。
2.6存取效率的比较
char s1[] = “aaaaaaaaaaaaaaa”;char *s2 = “bbbbbbbbbbbbbbbbb”;aaaaaaaaaaa是在运行时刻赋值的;
而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小结:
堆和栈的区别可以用如下的比喻来看出:
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。(经典!)
第二篇:C语言内存分配
C语言变量声明及内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。程序结束时由编译器自动释放。
2、堆区(heap)— 在内存开辟另一块存储区域。一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—编译器编译时即分配内存。全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(BSS)。-程序结束后由系统释放
4、文字常量区 —常量字符串就是放在这里的,程序结束后由系统释放。
5、程序代码区 —存放函数体的二进制代码。例子程序
这是一个前辈写的,非常详细 #include
#include
int a = 0;
//全局初始化区 char *p1;
//全局未初始化区 void main(){
int b=1;// 栈
char s[] = “abc”;//栈
char *p2;//栈
char *p3 = “123456”;//“123456 ”在常量区,p3在栈上。
static int c =0;//全局(静态)初始化区
p1 =(char *)malloc(10);
p2 =(char *)malloc(20);//分配得来得10和20字节的区域就在堆区。
strcpy(p1, “123456”);//123456 放在常量区,编译器可能会将它与p3所指向的“123456”优化成一个地方
system(“pause”);
}
===============
C语言程序的内存分配方式 1.内存分配方式
内存分配方式有三种:
[1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
[2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
[3]从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
2.程序的内存空间
一个程序将操作系统分配给其运行的内存块分为4个区域,如下图所示。
一个由C/C++编译的程序占用的内存分为以下几个部分,1、栈区(stack)—
由编译器自动分配释放,存放为运行函数而分配的局部变量、函数参数、返回数据、返回地址等。其操作方式类似于数据结构中的栈。
2、堆区(heap)—
一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。分配方式类似于链表。
3、全局区(静态区)(static)—存放全局变量、静态数据、常量。程序结束后由系统释放。
4、文字常量区 —常量字符串就是放在这里的。程序结束后由系统释放。
5、程序代码区—存放函数体(类成员函数和全局函数)的二进制代码。
下面给出例子程序,int a = 0;//全局初始化区
char *p1;//全局未初始化区
int main(){
int b;//栈
char s[] = “abc”;//栈
char *p2;//栈
char *p3 = “123456”;//123456在常量区,p3在栈上。
static int c =0;//全局(静态)初始化区
p1 = new char[10];
p2 = new char[20];
//分配得来得和字节的区域就在堆区。
strcpy(p1, “123456”);//123456放在常量区,编译器可能会将它与p3所指向的“123456”优化成一个地方。
}
3.堆与栈的比较
3.1申请方式
stack: 由系统自动分配。例如,声明在函数中一个局部变量 int b;系统自动在栈中为b开辟空间。
heap: 需要程序员自己申请,并指明大小,在C中malloc函数,C++中是new运算符。
如p1 =(char *)malloc(10);p1 = new char[10];
如p2 =(char *)malloc(10);p2 = new char[20];
但是注意p1、p2本身是在栈中的。
3.2申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。
对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。
由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
3.3申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
3.4申请效率的比较
栈由系统自动分配,速度较快。但程序员是无法控制的。
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是栈,而是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度快,也最灵活。
3.5堆和栈中的存储内容
栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。
3.6存取效率的比较
char s1[] = “a”;
char *s2 = “b”;
a是在运行时刻赋值的;而b是在编译时就确定的;但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。比如:
int main(){
char a = 1;
char c[] = “1234567890”;
char *p =“1234567890”;
a = c[1];
a = p[1];
return 0;
}
对应的汇编代码
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读取字符,显然慢了。3.7小结
堆和栈的主要区别由以下几点:
1、管理方式不同;
2、空间大小不同;
3、能否产生碎片不同;
4、生长方向不同;
5、分配方式不同;
6、分配效率不同;
管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。
空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M。当然,这个值可以修改。
碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以参考数据结构。
生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由mallo函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。
虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。
无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生以想不到的结果。
4.new/delete与malloc/free比较
从C++角度上说,使用new分配堆空间可以调用类的构造函数,而malloc()函数仅仅是一个函数调用,它不会调用构造函数,它所接受的参数是一个unsigned long类型。同样,delete在释放堆空间之前会调用析构函数,而free函数则不会。
class Time{
public:
Time(int,int,int,string);
~Time(){
cout<<“call Time’s destructor by:”< } private: int hour; int min; int sec; string name; }; Time::Time(int h,int m,int s,string n){ hour=h; min=m; sec=s; name=n; cout<<“call Time’s constructor by:”< } int main(){ Time *t1; t1=(Time*)malloc(sizeof(Time)); free(t1); Time *t2; t2=new Time(0,0,0,“t2”); delete t2; system(“PAUSE”); return EXIT_SUCCESS; } 结果: call Time’s constructor by:t2 call Time’s destructor by:t2 从结果可以看出,使用new/delete可以调用对象的构造函数与析构函数,并且示例中调用的是一个非默认构造函数。但在堆上分配对象数组时,只能调用默认构造函数,不能调用其他任何构造函数。 页式存储管理方案中的内存分配 用户提出内存空间申请,按一定策略检查内存空间,找出满足请求的空闲页面,分给申请者 1首先接收输入文件: (1)内存空闲物理页面文件(文本文件),包括若干行,每行2项(起始 物理页面号,连续的物理页面数) (2)进程占用物理内存数据文件(文本文件),包括若干行,若干项(进 程号,物理页面号1,物理页面号2,……)建立空闲页面表,并在屏幕显示输出该表内容 20行 该表中记录了内存中可供分配的空闲页面的起始页号和连续空闲的页面数 3为每个进程建立一个页表,并在屏幕显示输出每个页表的内容 页表记录了每个进程逻辑页面与物理页面的对应关系在用户界面根据用户提示接收一个内存申请,格式为:进程名,申请空间大小(单位为K字节)为该进程建立一个页表,显示输出该表内容,检查空闲页面表,为该进程分配相应的物理页面,并修改有关的数据结构(空闲页面表,页表)假设页面大小为4K重复4,5直到输入特殊字符(0)在屏幕显示输出最新的空闲页面表内容 #include #include #include #include const int MJ=10;//假定系统允许的最大作业数量为10 typedef struct node{ int address; int length; char tag[10]; }job; job frees[MJ]; int free_quantity; job occupys[MJ]; int occupy_quantity; int read() { FILE *fp; char fn[10]; cout<<“请输入初始空闲表文件名:”; cin>>fn; if((fp=fopen(fn,“r”))==NULL){ 其意义是在当前目录下打开文件file a,只允许进行“读”操作,并使fp指向该文件 cout<<“错误,文件打不开,请检查文件名”< } else{ while(!feof(fp)){ fscanf(fp,“%d,%d”,&frees[free_quantity].address,&frees[free_quantity].length);free_quantity++;fscanf(文件指针,格式字符串,输入表列); } return 1; } return 0; } void sort() { int i,j,p; for(i=0;i p=i; for(j=i+1;j if(frees[j].address p=j; } } if(p!=i){ frees[free_quantity]=frees[i]; frees[i]=frees[p]; frees[p]=frees[free_quantity]; } } } void view() { int i; cout< cout<<“输出空闲区表:n起始地址 分区长度状态n”< for(i=0;i cout.setf(2); cout.width(12); cout< cout.width(10); cout< cout.width(8); cout< } cout< cout<<“输出已分分区表:n起始地址 分区长度 占用作业名n”< for(i=0;i cout.setf(2); cout.width(12); cout< cout.width(10); cout< cout.width(8); cout< } } void ear() { char job_name[10]; int job_length; int i,j,flag,t; cout<<“请输入分配内存的作业名和空间大小:”; cin>>job_name; cin>>job_length; flag=0; for(i=0;i if(frees[i].length>=job_length){ flag=1; } } if(flag==0){//未找到空闲区,返回 cout< } else{ t=0; i=0; while(t==0){ if(frees[i].length>=job_length){//找到可用空闲区,开始分配 t=1; } i++; } i--; occupys[occupy_quantity].address=frees[i].address;//修改已分配区表 strcpy(occupys[occupy_quantity].tag,job_name); occupys[occupy_quantity].length=job_length; occupy_quantity++; if(frees[i].length>job_length){ frees[i].address+=job_length; frees[i].length-=job_length; } else{ for(j=i;j frees[j]=frees[j+1]; } free_quantity--; cout<<“内存空间成功:)”< } } } void reclaim()//回收作业所占的内存空间 { char job_name[20]; int i,j,flag,p=0; int address; int length;//寻找已分分区表中对应的登记项 cout<<“输入要回收分区的作业名:”; cin>>job_name; flag=-1; for(i=0;i if(!strcmp(occupys[i].tag,job_name)){ flag=i; address=occupys[i].address; length=occupys[i].length; } } if(flag==-1){ //在已分分区表中找不到作业 cout<<“没有这个作业名”< } else{//修改空闲区表,加入空闲表 for(i=0;i if((frees[i].address+frees[i].length)==address){ if(((i+1) for(j=i+1;j frees[j]=frees[j+1]; } free_quantity--; p=1; } else{ frees[i].length+=length; p=1; } } if(frees[i].address==(address+length)){ frees[i].address=address; frees[i].length+=length; p=1; } } if(p==0){ frees[free_quantity].address=address; frees[free_quantity].length=length; free_quantity++; }//删除分配表中的该作业 for(i=flag;i occupys[i]=occupys[i+1]; } occupy_quantity--; } } void main() { int flag=0; int t=1; int chioce=0; int i; for(i=0;i frees[i].address=-1;//空闲区表初始化 frees[i].length=0; strcpy(frees[i].tag,“free”); occupys[i].address=-1;//已分分区表初始化 occupys[i].length=0; strcpy(occupys[i].tag,“"); } free_quantity=0; occupy_quantity=0; flag=read(); while(flag==1){ sort(); cout<<”选择功能项:(0-退出,1-分配内存,2-回收内存,3-显示内存)n“< cin>>chioce; switch(chioce){ case 0: flag=0; break; case 1: ear(); break; case 2: reclaim(); break; case 3: view(); break; default: cout<<”没有该选项n"< } } } ① 白钢刀(即高速钢刀具)因其通体银白色而得名,主要用于直壁加工。白钢刀价格便宜,但切削寿命短、吃刀量小、进给速度低、加工效率低,在数控加工中较少使用。 ② 飞刀(即镶嵌式刀具)主要为机夹式可转位刀具,这种刀具刚性好、切削速度高,在数控加工中应用非常广泛,用于模胚的开粗、平面和曲面粗精加工效果均很好。 ③ 合金刀(通常指的是整体式硬质合金刀具)精度高、切削速度高,但价格昂贵,一般用于精加工。 数控刀具与普通机床上所用的刀具相比,有以下不同的要求。 (1)刚性好(尤其是粗加工刀具)、精度高、抗振及热变形小。 (2)互换性好,便于快速换刀。 (3)寿命高,切削性能稳定、可靠。 (4)刀具的尺寸便于调整,以减少换刀调整时间。 (5)刀具应能可靠地断屑或卷屑,以利于切屑的排除。(6)系列化、标准化,以利于编程和刀具管理。 ① 刀具直径越大,转速越慢;同一类型的刀具,刀杆越长,吃刀量就要减小,否则容易弹刀而产生过切。 ② 白钢刀转速不可过快,进给速度不可过大。 ③ 白钢刀容易磨损,开粗时少用白钢刀。 ① 以上的飞刀参数只能作为参考,因为不同的飞刀材料其参数值也不相同,不同的刀具厂生产的飞刀其长度也略有不同。另外,刀具的参数值也因数控铣床或加工中心的性能和加工材料的不同而不同,所以刀具的参数一定要根据工厂的实际情况来设定。 ② 飞刀的刚性好,吃刀量大,最适合模胚的开粗。另外,飞刀精加工陡峭面的质量也非常好。③ 飞刀主要是镶刀粒的,没有侧刃,① 合金刀刚性好,不易产生弹刀,用于精加工模具的效果最好。 ② 合金刀和白钢刀一样有侧刃,精铣铜公直壁时往往使用其侧刃。 ① 刀具的名称一般根据刀具的直径和圆角半径来定义,例如,直径为30,圆角半径为5的飞刀,其名称定义为D30R5;直径为12的平底刀,其名称定义为D12;半径为5的球刀,其名称定义为R5。 ② 输入刀具名称时,只需要输入小写字母即可,系统会自动将字母转为大写状态。③ 设置刀具参数时,只需要设置刀具的直径和底圆角半径即可,其他参数按默认即可。加工时,编程人员还需要编写加工工艺说明卡,注明刀具的类型和实际长度。 机床坐标一般在工件顶面的中心位置,所以创建机床坐标时,最好先设置好当前坐标,然后在〖CSYS〗对话框中设置“参考”为WCS。 加工模具时,其开粗余量多设为0.5,但如果是加工铜公余量就不一样了,因为铜公(铜公是火花机放电加工用的电极)最后的结果是要留负余量的。 模具加工要求越高时,其对应的公差值就应该越小。 进行实体模拟验证前,必须设置加工工件和毛坯,否则无法进行实体模拟。第三篇:页式存储管理方案中的内存分配
第四篇:可变分区存储管理方式的内存分配和回收
第五篇:刀具和编程总结