VC 60 编译器编译期 存储器分配模型(内存布局)
第10章目标程序运行时的存储组织

p3活动记录 存取链(静态链) 控制链(动态链)
p3活动记录 存取链(静态链) 控制链(动态链)
main活动记录
2、用Display表
Display表---嵌套层次显示表 当前激活过程的层次为K,它的Display表含有K+1个
单元,依次存放着现行层,直接外层…直至最外层的每 一过程的最新活动记录的基地址。 说明:1、由于过程的层数可以静态确定,因此每个过程 的Display表的体积在编译时即可以确定。
Q的 活动记录
P的 活动记录 主程序的 活动记录
DISPLAY表பைடு நூலகம்维护和建立
为便于组织存储区,将display作为活动记录的一 部分,其相对地址在编译时是完全可以确定的。
假设过程P1可调用P2,为了能在P2中建立P2的 display,在P1调用P2时设法把P1的display地址 作为连接数据之一(全局display地址)传送给P2, 因此连接数据包括: 老SP值(动态链) 返回地址 全局display地址
嵌套过程的栈式分配方案
分程序结构的存储分配方案
3、过程活动:一个过程的活动指的是该过程的一次执行。
4、活动记录:一个过程的一次执行所需要的信息使用一个连 续的存储区来管理,这个区(块)叫做一个活动记录。
活动记录一般包含:
(1)连接数据
返回地址—调用过程指令的下一条指令的地址。
动态链(控制链)—指向调用该过程活动记录地址的指针。用 于当调用返回时,将当前栈顶正确切换到调用者的活动记录
2、某过程p是在层次为i的过程q内定义的,并且q是 包围p的直接外层,那么p的过程层数为i+1。
例: program main(i,0);
c语言的内存结构

c语言的内存结构C语言是一种高级编程语言,但实际上在计算机中运行时,C语言程序会被编译成可执行文件,然后在计算机内存中运行。
因此,了解C 语言的内存结构对于理解C程序的运行及性能优化至关重要。
C语言的内存结构主要可以分为以下几个部分:栈(Stack)、堆(Heap)、全局内存(Global Memory)和代码区(Code Segment)。
首先是栈(Stack),栈是一种自动分配和释放内存的数据结构。
它用于存储局部变量、函数参数和函数调用信息等。
栈的特点是后进先出(LIFO),也就是最后进入的数据最先被释放。
栈的大小在程序运行时是固定的,一般由编译器设置。
栈的操作速度较快,但内存空间有限。
其次是堆(Heap),堆是一种动态分配和释放内存的数据结构。
它用于存储动态分配的变量、数据结构和对象等。
堆的大小一般由操作系统管理,并且可以在运行时进行动态扩展。
堆的操作相对较慢,因为需要手动分配和释放内存,并且容易产生内存碎片。
全局内存(Global Memory)是用于存储全局变量和静态变量的区域。
全局变量在程序的生命周期内都存在,并且可以在多个函数之间共享。
静态变量作用于其所在的函数内,但是生命周期与全局变量相同。
全局内存由编译器进行分配和管理。
代码区(Code Segment)存储了程序的指令集合,它是只读的。
在程序运行时,代码区的指令会被一条一条地执行。
代码区的大小由编译器决定,并且在程序执行过程中不能修改。
此外,C语言还具有特殊的内存区域,如常量区和字符串常量区。
常量区用于存储常量数据,如字符串常量和全局常量等。
常量区的数据是只读的,且在程序的整个生命周期内存在。
字符串常量区是常量区的一个子区域,用于存储字符串常量。
在C语言中,内存分配和释放是程序员的责任。
通过使用malloc和free等函数,程序员可以在堆中动态地分配和释放内存,从而灵活地管理程序的内存使用。
不过,应当注意避免内存泄漏和野指针等问题,以免出现内存错误和性能问题。
编译时分配内存

1、所谓在编译期间分配空间指的是静态分配空间(相对于用new动态申请空间),如全局变量或静态变量(包括一些复杂类型的常量),它们所需要的空间大小可以明确计算出来,并且不会再改变,因此它们可以直接存放在可执行文件的特定的节里(而且包含初始化的值),程序运行时也是直接将这个节加载到特定的段中,不必在程序运行期间用额外的代码来产生这些变量。
其实在运行期间再看“变量”这个概念就不再具备编译期间那么多的属性了(诸如名称,类型,作用域,生存期等等),对应的只是一块内存(只有首址和大小),所以在运行期间动态申请的空间,是需要额外的代码维护,以确保不同变量不会混用内存。
比如写new表示有一块内存已经被占用了,其它变量就不能再用它了;写delete表示这块内存自由了,可以被其它变量使用了。
(通常我们都是通过变量来使用内存的,就编码而言变量是给内存块起了个名字,用以区分彼此)内存申请和释放时机很重要,过早会丢失数据,过迟会耗费内存。
特定情况下编译器可以帮我们完成这项复杂的工作(增加额外的代码维护内存空间,实现申请和释放)。
从这个意义上讲,局部自动变量也是由编译器负责分配空间的。
进一步讲,内存管理用到了我们常常挂在嘴边的堆和栈这两种数据结构。
最后对于“编译器分配空间”这种不严谨的说法,你可以理解成编译期间它为你规划好了这些变量的内存使用方案,这个方案写到可执行文件里面了(该文件中包含若干并非出自你大脑衍生的代码),直到程序运行时才真正拿出来执行。
2、编译其实只是一个扫描过程,进行词法语法检查,代码优化而已,编译程序越好,程序运行的时候越高效。
我想你说的“编译时分配内存”是指“编译时赋初值”,它只是形成一个文本,检查无错误,并没有分配内存空间。
当你运行时,系统才把程序导入内存。
一个进程(即运行中的程序)在主要包括以下五个分区:栈、堆、bss、data、code代码(编译后的二进制代码)放在code区,代码中生成的各种变量、常量按不同类型分别存放在其它四个区。
VC++60上机操作说明

VC++概述(续-1)
VC++6.0使用说明
VC++的文件系统 在Visual Studio环境下开发应用程序时, 系统会生成一系列的文件, 这些文件用不同的扩展名来区分, 下面分别来介绍. C源程序文件: 扩展名为 c; C++源程序文件: 扩展名为 cpp; C/C++源程序的头文件: 扩展名为 h 或 hpp 工作空间(Workspace)文件: 扩展名为 dsw. 这种文件在VC++中 的级别是最高的, 它负责对工作空间中的各个工程(Project) 进行 管理和协调, 这些工程可以是独立的, 也可以存在相互依赖关系 (主从关系), 也可以由不同语言构成; 工程(Project)文件: 扩展名为 dsp. 在VC++中, 应用程序是以工 程的形式存在的, 在一个Project文件中, 包含着构成应用程序的 所有文件的信息. VC++类信息文件: 扩展名为 clw, 包含类和各个资源的信息; Workspace辅助文件: 扩展名为opt,包含本地计算机的配置信息; 资源模板文件: 扩展名为rc, 包含各种资源的信息;
VC++6.0 使用说明
VC++6.0 概述
VC++6.0使用说明
Visual C++ 是Microsoft公司推出的, 功能非常强大的可视化应用 程序开发工具, 可用于开发大型Windows应用程序, 是计算机界最 优秀的应用开发工具之一. VC++是Microsoft Visual Studio大型 集成开发系统中的一部分, 它既可以单独使用, 也可以与其它语言 系统, 如Visual Fortran, VB, VJ++等进行混合编程. 该集成系统采 用统一的Developer Studio 开发环境, 因此, 熟练掌握VC++的使 用, 对使用其它语言系统也有不少帮助.
c编译的程序占用的内存

3.4申请效率的比较
栈由系统自动分配,速度较快。但程序员是无法控制的。
}
3.堆与栈的比较
3.1申请方式
stack: 由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间。
heap: 需要程序员自己申请,并指明大小,在C中malloc函数,C++中是new运算符。
如p1 = (char *)malloc(10); p1 = new char[10];
strcpy(p1, "123456"); //123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
===============
C语言程序的内存分配方式 来自1.内存分配方式 内存分配方式有三种:
[1]从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
char *p3 = "123456"; //"123456\0"在常量区,p3在栈上。
static int c =0; //全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
//分配得来得10和20字节的区域就在堆区。
[2]在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
vc60使用教程详解

你首先要打开VC6.0界面,一般用得较多的是Win32控制台应用程序(源程序,扩展名.cpp),步骤是:(先工程—后文件—编译—连接---运行)1,建立一个工程,“文件”——“新建”,出现下面界面:选择“Win32 Console Application”(控制台应用程序,左边倒数第三个),命名工程名称,选择保存位置,点击“确定”,进入下一步,看到如下提示界面:建立一个空工程,对应其他需要的你一可以建立别的工程;点击“完成”,之后:显示你创建的工程的信息。
2,再在有一个的工程的条件下,我们再建立一个源文件;“文件”——“新建”(快捷键Ctri+N),出现:建立源文件,选择“C++ Source ”,一般都是建立这种文件的(适用在当文件中适用)如果要建立头文件的话,选择“C/C++ Header File”,(适用在多文件工程中使用)命名,文件名称,点击“确定”,之后:进入编辑区,在主界面编写代码:如下编写完之后呢:可以按编译按钮调试程序,看看有没有错误,有的话改正,没有的话就可以再按连接按钮检查连接(多文件工程时常用,检查文件间是否正常连接),最后,点运行按钮,就可以运行了。
如果是您有代码如:cpp文件,或 .h 文件,想添加都VC6.0里来测试的话,可以这样做:首先,要理解一下文件扩展名为:cpp和.h文件扩张名是.h,代表的是头文件,一般是书写一些函数原型,以及一些在整个程序中常用到的结构体,频繁使用的函数说明,定义等等;文件扩张名为,cpp的,是C++中的源文件,也是最常用到的文件,每建立一个工程都要至少一个源文件(至少要有一个函数入口——主函数main() ),包含了核心代码;建立与运行说明:(以VC 6.0编译器为例,其他编译器类似)首先,打开VC 6.0编译环境;在菜单栏——文件(的下拉菜单中选择“新建”),在弹出的选择窗口中,选择Win32 Console Application(控制台应用程序),在填写工程名称,选择一个程序保存路径,点击“完成”,查看工程信息。
VC++ 6.0 编译器编译期存储器分配模型(内存布局)

一、内存区域的划分一个由C/C++编译的程序占用的内存分为以下几个部分:1)、栈区(Stack):由编译器(Compiler)自动分配释放,存放函数的参数值,局部变的值等。
其操作方式类似于数据结构中的栈。
2)、堆区(Heap ):一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3)、全局区(静态区)(static):全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
程序结束后由系统释放。
4)、文字常量区:常量字符串就是放在这里的。
程序结束后由系统释放。
5)、程序代码区:存放函数体的二进制代码。
二、测试案例(源码与反汇编对照)2.1 测试案例源码与反汇编对照为了能够形象地说明内存布局模型,先来看一段Win32 Console Application代码(表3.1),其中,加粗文字(行最左端为行标号)为C源代码,未加粗文字(行最左端为地址)为反汇编后的指令代码。
看上去比较零乱,不过一定要耐住性子,后面的文字将基于此。
3.2 内存布局图对于该案例,以下几幅图形象地说明了第2节提到的内存5大区域。
需要注意的是,图中各区域的起始地址不是绝对的,不同的编译环境可能不完全相同,这里给出的只是一个典型示例。
需要注意的是,不同区域地址编址方向也不同。
3、应用通过对第3节案例的理解,我们将对一些现象予以解释。
3.1、变量初始化1)局部变量将被分配在栈中,如果不初始化,则为不可预料值。
编译时,编译器将抛出一个编号为C4700警告错误(local variable '变量名' used without having been initialized)。
表4.1代码测试了局部变量未初始化的情况。
该测试的一个典型的输出结果为:-858993460,同时,编译时编译器抛出了一条警告错误。
内存空间分配

C/C++语言变量声明内存分配By---陆涛1、一个由C编译的程序占用的内存分为以下几个部分1、栈区(stack)—程序运行时由编译器自动分配,存放函数的参数值,局部变量的值等。
其操作方式类似于数据结构中的栈。
程序结束时由编译器自动释放。
2、堆区(heap)—在内存开辟另一块存储区域。
一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—编译器编译时即分配内存。
全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。
- 程序结束后由系统释放4、文字常量区—常量字符串就是放在这里的。
程序结束后由系统释放5、程序代码区—存放函数体的二进制代码。
例子程序这是一个前辈写的,非常详细//main.cpp char aaa[3*2048*2048];int a = 0; 全局初始化区char *p1; 全局未初始化区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"优化成一个地方。
}===============2、内存分配方式有三种:[1]从静态存储区域分配。
内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
main()例程的栈(Stack) 0x0012fec8 0x0012fecc 0x0012fed0 *char2(指针) *char1(指针) init_array_l2[0] …… init_array_l2[9] init_array_l1[0] …… init_array_l1[9] no_init_array_l1[0] …… no_init_array_l2[9] no_init_array_l1[0] …… no_init_array_l1[9] init_l2 init_l1 no_init_l2 no_init_l1 图3.1 低地址
表 4.2 未初始化全局变量测试
#include <iostream.h> int no_init_g1; void main(void) { cout<<no_init_g1<<endl; } 该测试的输出结果为:0。
//未初始化的全局变量 1
3)全局变量初始化为 0 与不初始化效果一样。请留意表 3.1 第 9 行代码,即 int init_array_g1[10]={0}; //初始化的全局数组 1 我们原来的意图是将 init_array_g1[]第 0 个元素初始化为 0(其它元素也将被初始化 为 0) ,然而从该案例的内存布局来看,init_array_g1[]被安排在 “全局区(静态区)未 初始化区域(图 3.2a 所示) 。 ” 因此,对于全局变量初始化为 0 和不初始化是一样的。对于本案例有, int init_array_g1[10]={0}; //初始化的全局数组 1 等效于: int init_array_g1[10]; //初始化的全局数组 1 当然,出于谨慎,我们还是建议在使用全局变量前对其初始化。 4.2 变量初始化对代码空间的影响 本小节仍然讨论变量初始化问题,但出于重视,我们将其独立成小节。 现来看两个测试 案例。 案例 1:建立 Win32 Console Application 工程,工程名:Test1,代码如表 4.3。
dword ptr [ebp-10h],0DEh
20: 21: 22:
004106FC 00410706 0041070B 0041070D 00410713
int no_init_array_l1[10]; int no_init_array_l2[10]; int init_array_l1[10]={0};
3.2 内存布局图 对于该案例,以下几幅图形象地说明了第 3 节提到的内存 5 大区域。需要注意的是,图 中各区域的起始地址不是绝对的, 不同的编译环境可能不完全相同, 这里给出的只是一个典 型示例。需要注意的是,不同区域地址编址方向也不同。
Page 2/7
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
mov mov dword ptr [ebp-0Ch],6Fh
//未初始化的局部变量 1 //未初始化的局部变量 2 //初始化的局部变量 1 //初始化的局部变量 2 //未初始化的局部数组 1 //未初始化的局部数组 2 //初始化的局部数组 1
19:
004106F5
int init_l2=222;
表 4.3 工程 Test1
int init_array_g1[10000000]={0}; //初始化的全局数组 1 void main(void) { } 编译成 Debug 版本, 察看 Debug 目录下的 Test1.exe 可执行文件大小, 典型大小约 184KB
Page 5/7
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
(约 0.18MB) 。 案例 2:建立 Win32 Console Application 工程,工程名:Test2,代码如表 4.3。
//123456 在常量区,char1 在栈上。 //分配得来得 20 字节的区域在堆区
dword ptr [ebp-0B4h],offset string "123456" (00426ee8)
25:
00410738 0041073A 0041073F 00410742
26:
}
00410748 00410749 0041074A 0041074B 00410751 00410753 00410758 0041075A 0041075B
0x0042cቤተ መጻሕፍቲ ባይዱ50
0x0042c028
0x0042c000 0x0042bffc 0x0042bff8
低地址 4个字节
Page 3/7
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
全局区(静态区) 已初始化部分 高地址 0x0042c658 init_static init_array_g2[9] …… init_array_g2[0] init_g2 init_g1 图3.2b
0x0042630 0x0042b62c 0x0042b628
低地址 4个字节
堆(Heap) 如果动态内存申请成功,则char2将指向该区域 图3.4
Page 4/7
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
4. 应用
通过对第 3 节案例的理解,我们将对一些现象予以解释。 4.1 变量初始化 1)局部变量将被分配在栈中,如果不初始化,则为不可预料值。编译时,编译器将抛 出一个编号为 C4700 警告错误(local variable '变量名' used without having been initialized) 。 表 4.1 代码测试了局部变量未初始化的情况。
表 4.1 未初始化局部变量测试
#include <iostream.h> void main(void) { int no_init_l1; //未初始化的局部变量 1 cout<<no_init_l1<<endl; } 该测试的一个典型输出结果为: -858993460, 同时, 编译时编译器抛出了一条警告错误。 2)全局变量如果不初始化,则默认为 0,编译时编译器不提示“变量未初始化” 。 表 4.2 代码测试了全局变量未初始化的情况。
mov mov xor lea rep stos mov mov xor lea rep stos mov push call add mov pop pop pop add cmp call mov pop ret dword ptr [ebp-88h],0 ecx,9 eax,eax edi,[ebp-84h] dword ptr [edi]
23:
00410715 0041071F 00410724 00410726 0041072C
int init_array_l2[10]={1};
dword ptr [ebp-0B0h],1 ecx,9 eax,eax edi,[ebp-0ACh] dword ptr [edi]
//初始化的局部数组 2
12: 13: 14: 15:
static int init_static=3; void main(void) {
push mov sub push push push lea mov mov rep stos ebp ebp,esp esp,0F8h ebx esi edi edi,[ebp-0F8h] ecx,3Eh eax,0CCCCCCCCh dword ptr [edi]
24:
0041072E
char *char1="123456"; char *char2=(char *)malloc(20);
14h malloc (004049b0) esp,4 dword ptr [ebp-0B8h],eax edi esi ebx esp,0F8h ebp,esp __chkesp (00410690) esp,ebp ebp
3. 测试案例(源码与反汇编对照)
3.1 测试案例源码与反汇编对照 为了能够形象地说明内存布局模型,先来看一段 Win32 Console Application 代码(表 3.1) ,其中,加粗文字(行最左端为行标号)为 C 源代码,未加粗文字(行最左端为地址) 为反汇编后的指令代码。看上去比较零乱,不过一定要耐住性子,后面的文字将基于此。
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
作者:合肥工业大学 杨铸 网址: 电邮:fairyfar@ 2008.04.04 清明节 Scripted by FairyFar.
1. 关于本文
Page 1/7
//未初始化的全局变量 1 //未初始化的全局变量 2 //初始化的全局变量 1 //初始化的全局变量 2 //未初始化的全局数组 1 //未初始化的全局数组 2 //初始化的全局数组 1 //初始化的全局数组 2 //未初始化的静态量
VC ++ 6.0 编译器编译期存储器分配模型(内存布局)
//初始化的静态量
004106D0 004106D1 004106D3 004106D9 004106DA 004106DB 004106DC 004106E2 004106E7 004106EC
16: 17: 18:
004106EE
int no_init_l1; int no_init_l2; int init_l1=111;
表 3.1 测试源码与反汇编对照
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
#include <malloc.h> int no_init_g1; int no_init_g2; int init_g1=111; int init_g2=222; int no_init_array_g1[10]; int no_init_array_g2[10]; int init_array_g1[10]={0}; int init_array_g2[10]={1}; static int no_init_static;