




已阅读5页,还剩3页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
六种编译模式概述 函数后面接 large 等关键字 六种编译模式概述 Turbo C 提供了六种编译模式 编译模式有时也称为寻址模式或 内存模式 因为它处理的就是如何在内存中为程序 数据 堆栈分配 空间并存取它们 这六种模式是 微模式 tiny 小模式 small 紧凑 模式 compact 中模式 medium 大模式 large 巨模式 huge 它们之间 的关系如下表所示 小程序 大程序 小数据 微 小 中 大数据 紧凑 大 巨 所谓小程序就是只有一个程序段 当然不超过 64K 字节 缺省的 码 函数 指针是 near 所谓大程序就是有多个程序段 每个程序段不 超过 64K 字节 但总程序量可超过 64K 字节 缺省的码指针是 far 所 谓小数据就是只有一个数据段 缺省的数据指针是 near 所谓大数据 就是有多个数据段 缺省的数据指针是 far 下面还会逐个谈到它们 之间的差别 并通过同一程序在六种不同模式下的输出结果 来进一 步加深对这六种模式的理解 但先要强调一点 无论使用哪一种编译 模式 单个的 Turbo C 源文件不可能生成大于 64K 字节的代码 也不能 生成大于 64K 字节的静态 包括全局 数据 例如下面这个程序 int a 15000 b 20000 void main 在任何模式下都不能编译 这是因为 两个数组所要求的总存储量达 70K 字节 编译时会报出 Too much global data defined in file 的出错信息 为了处理大于 64K 字节的代码或静态数据 必须分成几 个源文件 以上面这个程序为例 可以分成文件 A1 C 和 A2 C 分别用 巨模式对这两个源文件进行编译 最后连接成一个可执行文件 al c a2 c a prj int a 15000 int b 20000 a1 void main a2 a1 obj 30k a2 obj 40k a exe 71k 六种编译模式的差别是 它们对来自不同源文件的码和数据段的 处理不同 对动态分配的堆空间的处理不一样 对指针使用也不一样 此外 它们的所形成的 obj 文件中传给连接程序的信息也不一样 以便连接程序相应地安排码段和数据段 把相应的说明放在 exe 文 件的头中并借此通知 DOS 当执行这个程序时如何装入码段和数据段 如何设置各个段寄存器 用于演示六种编译模的程序是由两个源文件 X C 和 Y C 组成 如下 所示 X C include void a static int b int c printf In function A printf CS X CS printf DS X DS printf SS X SS printf Static B p pritnf Automatic C p Y C include int d void main int e a printf In function main printf CS X CS pritnf DS X DS pritnf SS X SS pritnf Global D p pritnf Automatic E p printf Heap address p malloc 2 if defined TINY defined SMALL defined COMPACT pritnf Function A Np a pritnf Function main Np main else printf Function A Fp a printf Function main Fp main endif 第一个源文件包含函数 a 和一个静态 局部 变量 b 第二个源文件 包含主函数 main 和一个全局变量 d 两个源文件中各含有一个自动变 量 c 和 e 第二个源文件的主函数 main 调用了第一个源文件中的函数 a 还调用了 Turbo C 的库函数 malloc 去分配一块堆空间 两个源文件是 分别编译的 然后再通过连接程序连接起来 通过以六种不同模式编译这两个源文件 可以看到它们是如何为 码 数据和堆栈段分配空间 可以看到静态变量 自动变量和堆变量 分别存放在什么位置 函数放在什么地方 正如下面将要看到的那样 在某些模式下 数据指针是 near 而函数指针是 far 在另一些模式下 情况又正好相反 对于数据指针 不管是 far 还是 near pritnf 函数中的格式说明 p 都 能把指针正确地打印出来 对于函数 指针 p 就没有这个功能 所以 在 main 函数中必须加条件编译控制行 if else 和 endif 微模式 在微模式下 整修程序只有一段 这个段内包含码 静态和全局 数据 堆栈和堆 因为只有一个段 在执行时 DOS 将把寄存器 CS DS SS 设置为相等 全都指向这个段 在这个段内 码是首先装入的 地 址最低 接着是静态变和全局变量 然后是堆 最后地堆栈 堆和堆 栈都是动态的 堆从低地址往高地址增长 堆栈从高地址往低地址增 长 若两者相碰 则表示内存空间已耗尽 在微模式下 所有指针都 是 near 且都是相对于寄存器 CS DS 和 SS 的 对于用微模式编译并连 接生成的 exe 文件 DOS 的 exe2bin 实用程序转换为 COM 文件 从下表演示程序的输出结果可以看出 函数 a 比函数 main 的地址 低 变量 b 比变量 d 的地址低 这是因为 在连接时是 x obj 在前 y obj 在后 微模式 小模式 紧凑模式 中模式 大模式 巨模式 In function A CS 74C8 74B1 74B1 74F9 74FD 74FE DS 74C8 75CC 7629 75EC 764A 7674 SS 74C8 75CC 767A 75EC 76A0 76BB Static B 1704 048C 7629 04C8 049A 764A 04D6 7674 0002 Automatic C FFD0 FFD0 767A 0FD6 FFCC 76A0 0FD4 76BB 0FD0 In function main CS 74C8 74B1 74B1 74FE 7502 7503 DS 74C8 75CC 7629 75EC 764A 767B SS 74C8 75CC 767A 75EC 76A0 76BB Global D 1706 048E 7629 04CA 049C 764A 04D8 767B 0004 Automatic E FFD6 FFD6 767A 0FDE FFD4 76A0 0FDC 76BB 0FDA Heap Address 1792 051A 777C 000C 0568 77A2 000C 77BD 000C Function A 0283 01A5 0167 74F9 000E 74FD 000D 74FE 0003 Function main 02C1 01E3 01AE 74FE 0004 7502 000C 7503 0009 小模式 小模式是常用的模式 本书中大部分例子都是用小模式编译的 虽然小模式与微模式一样 都是小数据 小程序模式 但它与微模式 有两点重要的差别 第一 码和数据 堆栈 堆段是分开的 所以 CS 不 等于 DS 和 SS 第二 除了和数据 堆栈共用一个段的堆外 还有一个 远堆 以 far 指针进行存取 从数据 堆栈段的末尾直到常规内存的末 尾都是属于远堆 因为码 静态数据和 近 堆仍然在同一个段内 所以小模式下缺 省的数据指针和函数指针都是 near 结果 在小模式下不能直接通过 该模式下的 Turbo C 函数来处理远堆中的变量 然而 只要程序提供 自己的操作函数 就可以存取整个远堆中的任一单元 即可以使用整 个常规内存 中模式 在数据 堆栈 堆的分配方面 中模式与小模式是一样的 差别在 于码段的分配 在中模式下 来自不同源文件的码模式放在不同的码 段内 严格地讲 同一源文件内的各函数也是放在不同码段内 各码 段的总空间数只受微机上可用内存的限制 因为有多个码段 所以 Turbo C 必须用 far 函数指针 在演示程序输出的结果中函数 a 的地址 为 74F9 000E 函数 main 的地址为 74FE 0004 函数 a 的地址较低 是 因为在连接时包含函数 a 的 x obj 在前 在中模式下 堆仍然有近堆和 远堆之分 紧凑模式 紧凑模式在概念上是最简单的 码 静态数据 堆栈 堆各有其 自己的段 堆只有远堆 没有近堆 像小模式和中模式中的远堆一样 堆是用 far 指针来存取的 可以用 Turbo C 的库函数来处理堆变量 所 有数据指针都是 far 函数指针都是 near 从演示程序的输出中可以 看出 CS DS SS 三个寄存器的值彼此不等 值得注意的是 静态数 据的总量仍不可超过 64K 字节 大模式 在静态数据 堆栈 堆的分配方面 大模式等同于紧凑模式 在码 的分配方面 大模式等同于中模式 无论是数据指针还是函数指针 一律都是远指针 与紧凑模式一样 静态数据的总量不可超过 64K 字 节 巨模式 巨模式取消了静态数据总量不可超过 64K 字节的限制 来自不同 源文件的码放在不同的段内 来自不同源文件的静态数据也是放在不 同的段内 只有堆栈是合在一起的 以前举的例子就是利用了这一特 点 从演示程序的输出中也可以看 当从函数 main 内调用函数 a 时 不但 CS 改变了 DS 也改变了 当然 两个函数共用了同一个堆栈 否 则就无法正确返回 应该注意的是 不要把巨模式和巨指针混为一谈 在巨模式下缺省的指针仍是 far 而不是 huge 堆栈的组织 Turbo C 堆栈是用来存储其生命期与函数生命期相同的数据 这 样的数据包括函数参数和函数体内定义的自动变量 为了表明函数堆 栈内部各数据的存放关系 设有这样一个函数定义 long f char a int b int c char d 每当调用函数 f 时 调用进行首先按相反顺序 即按从右到左的 顺序把调用参数压入堆栈 本例就是先压入 b 再压入 a 尽管参数 a 是字符型 但仍压入 16 位 因为 80X86 的机器没有 8 位的压栈指令 在 压入参数之后 根据调用指令是 near 还是 far 再压入 2 个或 4 个字节 的返回地址 进入被调用函数 f 之后 它首先把寄存器 BP 的当前值压入堆栈 并把 SP 寄存器的值拷贝到 BP 寄存器 接着再次执照相反的顺序在堆栈 内建立起函数体内的各个自动变量 本例里就是先 d 后 c 直到此时 堆栈的内容将会如下所示 b a 返回地址 保留的 BP d c 这里之所以要对 BP 和 SP 作如此处理 目的有 3 个 第一 为了利 用 BP 作地址寄存器 通过 BP n 这样的寻址方式到堆栈中存取调用 者传过来的参数和被调用函数自己的自动变量 因为在 80X86 中规定 当用 BP 作地址寄存器时 缺省的段地址是 SS 而不是 DS 第二 腾出 DS 和其它地址寄存器 仍用来存取缺省的数据段内的数据 第三 腾出 SP 以便在函数体内再调用其它函数 当函数 f 完成了它的工作以后 它就把返回值放到相应的位置 如果返回值是 char 型 则在返回前先强制转换为 int 型 凡是返回值 占两个字节的都通过寄存器 AX 返回 凡是返回值占 4 个字节的都是通 过寄存器对 DX AX 返回 超过 4 个字节的 struct 返回值 则被放在一个 静态变量内 返回的是指向这个变量的指针 dboule 返回值是放在数 值协处理器的 top of stack 寄存器或协处理器软件模拟包内与这个寄 存器等效的地方 接着函数 f 把 BP 拷贝到 SP 从堆栈中弹出入口时保 留的 BP 值到 BP 寄存器 最后执行一条 near 或 far 返回指令 返回到调 用者 返回以后 调用函数必须把调用调用时压入堆栈的参数从堆栈 中清除 上面这一套函数调用规则就叫做 C 调用规则 从这个过程中可以 看出 调用函数和被调用函数在参数数目上可以不一致 如果调用函 数压了过多的参数 被调用函数不存取这些多余参数是没有什么影响 的 调用函数在重新获得控制权后 会正确地把这些参数清除掉 如 果调用函数压入了过少的参数 被调用函数就可能把一些并非参数的 内容取来人微言轻参数而产生意想不到的结果 为了克服这个困难 如果参数数目是不定的 那么第一个参数最好是说明随后的参数的个 数 另一套不同的函数调用规则叫做 PASCAL 规则 它与 C 调用规则有 两点重要的差别 第一 压入参数的顺序是从左到右 第二 被调用 函数的工作完成以后 从堆栈中弹出参数是由被调用函数而不是由调 用函数去完成 PASCAL 调用规则要求调用函数和被调用函数参数上数 完全一致 顺便说一句 Turbo PASCAL 语言使用的不是 PASCAL 调用规 则 而是一种更为精心设计的堆栈格式 使得从被嵌套的函数内可以 存取函数的自动变量 堆的组织 前面已经说过 在小模式和中模式下 堆有近堆和远堆之分 处 理办法也不一样 近堆和堆栈共享一个段 它们相向增长 如果相遇 则说明缺省数据段已耗尽 远堆使用了缺省数据段之上直至常规内存 末尾的整个空间 为了管理这两个堆 Turbo C 提供了两组相应的函 数 coreleft farcoreleft realloc farrealloc malloc farmalloc free farfree calloc farcalloc 左边的近堆函数用近指针寻址各个堆变量 所用的参数也都 16 位 的 unsigned 型 右边的远堆函数用远指针寻址各个堆变量 所用的参 数也都是 unsigned long 型 在微模式下没有远堆 在紧凑模式 大模式和巨模式下只有一个 不改堆 其组织形式如同远堆 但在这三种模式下 既可以使用近堆 函数也可以使用远堆函数存取堆中变量 这是因为 不管使用哪一种 堆函数 这三种模式决定了所有数据指针是 far 如果使用近堆函数 则表示所需容量的参数 size 还必须是 unsigned 型的 16 位数 如果必须 处理大于 64K 字节的内存块 还必须使用远堆函数 分配和释放是随机进行的 没有一定的次序 结果就造成了各个 堆变量在堆中是不连续的 Turbo C 用一个链表来处理这些堆变量 在每一个堆变一的前面都有一个头 头中包含两个信息 此变量的长 度和指向下一个堆变量的指针 对于小数据模式 每个头占 4 个字节 对于大数据模式 每个头占 8 个字节 为了说明分配 释放 再分配在堆中是如何进行的 请看下面这 个演示程序 htap dem 的输出结果 include define report printf coreleft u coreleft void main void p q r printf report p malloc 1 printf p malloc 1 p p report q malloc 2 printf q malloc 2 p q report q realloc p 3 printf p realloc P 3 p p report r malloc 1 printf r malloc 1 p r report free q printf free q report free p printf free p report 这个程序产生的输出如下 coreleft 63952 p malloc 1 0500 coreleft 63946 q malloc 2 0506 coreleft 63940 p realloc P 3 050c coreleft 63932 r malloc 1 0500 coreleft 63932 free q coreleft 63932 free p coreleft 63946 这个演示程序是用小模式编译的 首先 coreleft 报告可用的内 存量 其次 malloc 建立单字节堆变量 p 和双字节变量 q 因为总是以 2 字节整数倍进行分配的 所以单字节变量 p 实际上也占用两个字节的 空间 每个堆变量还需要 4 个字节的头 这样 每分配一个堆变量 内存容量就减少 6 个字节 接着 realloc 把变量 p 扩大到 3 个字节 这 就要求重新分配 返回的指针也指向了新地址 050C 重新分配的堆变 量 p 占用了 8 个字节 包括它的头 尽管此时原来占用的 6 具字节已经 释放了 但 coreleft 仍报告减少了 8 个字节 而不是减少了两个字节 这是因为 coreleft 报告的只是堆中最上面最后一个变量之后连续可用 的内存容量 也就是说由于堆的碎片化 coreleft 报告的值是不准确 的 接着 程序又分配了一个单字节变量 r 它占用了第一次分配给 变量 p 后来又被释放的那 6 字节空间 在些之后 程序释放变量 q 在 变量 r 和 p 之间留下一个空洞 应该注意 分配 r 和释放 q 都不影响 coreleft 报告的值 最后 程序释放变量 p 此时 coreleft 报告的 值才是准确的 因为堆中只在其开始部分剩一个变量 r 了 下面这个 farheap dem 程序演示了如何从远堆中分配一个大于 64K 字节的数组 a 数组 a 是由 9000 具 double 型元素组成的 共需 72K 字节 把函数 farcalloc 返回的远指针强制转换为 huge 指针 以后就 可以用这个 huge 指针存取数组中的各个元素 include void main int i n 9000 double huge a double sum a double huge farcalloc n sizeof double for i 0 i n a i i for i 0 sum 0 i n sum a i printf a i for i 0 n 1 n d n printf sum of all a 8 0f sum printf n 1 n 2 1d long n n 1 2 其它内存操作函数 Turbo C 中还提供了许多有关内存拷贝 比较 设置和查找的函 数 这些函数的说明都在头文件 mem h 中 一般来说 它们都不牵涉 到什么结构 而是直接对内存进行操作 这些函数可对简单的字节进 行操作 也可实现 C 语言不直接支持的对数据结构的操作 如用一个 数组对另一个数组赋值 数组或 C 结构之间的比较等 用于内存之间拷贝数据的 Turbo C 函数有如下 5 个 void Cdecl memccpy void dest const void src int c size t n void Cdecl memcpy void dest const void src size t n void Cdecl memmove void dest const void src size t n void Cdecl movmem void src void dest unsigned length void Cdecl movedata unsigned srcseg unsigned srcoff unsigned dstseg unsigned dstoff size t n 函数 memcpy 从源 src 拷贝 kn 个字节到目 dest 如果源和目有重叠 的地方 则结果不一定正确 函数 memccpy 与 memcpy 类似 但若被拷贝的字节中有字符 c 则在 拷贝完这个字符后也停止拷贝 返回的指针指向目 dest 中的下一个字 节位置 若 n 个字节全部拷贝完 则返回的指针为空 函数 memmove 和
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 来宾市2025广西来宾市城市照明维护中心招聘编外聘用人员1人笔试历年参考题库附带答案详解
- 攀枝花市2025四川攀枝花市人力资源和社会保障信息中心招聘就业见习生3人笔试历年参考题库附带答案详解
- 富宁县2025云南文山州富宁县综合应急救援队伍招聘23人笔试历年参考题库附带答案详解
- 宁德市2025福建宁德市畲族歌舞艺术传承中心(宁德市畲族歌舞团)招聘紧缺急需及高笔试历年参考题库附带答案详解
- 离婚协议小说改编:财产分割与子女抚养权的法律困境
- 2025企业餐厅供货协议
- 矿业并购过程中员工安置与劳动法合规合同
- 离婚谈判实战技巧三大策略实战操作合同
- 离婚协议书:房产、股权、债权等复杂财产分割范本
- 离异家庭子女专属抚养权及教育支出协议书模板
- 4.2《遵守规则》教学设计 -2025-2026学年八年级道德与法治上册
- 人工智能+高质量发展文化旅游产业智能化升级研究报告
- 2025年自考专业(计算机网络)考试综合练习附参考答案详解(A卷)
- 企业融资培训课件
- GB/T 3810.14-2016陶瓷砖试验方法第14部分:耐污染性的测定
- 西师版三年级上册四则混合运算形成性测试题
- 企业知识产权管理中的专利挖掘工作概述课件
- 【高等数学练习题】兰州交通大学专升本自考真题汇总(附答案解析)
- 【完整版】锁骨骨折护理查房课件
- 在商会中秋团圆会上的讲话
- 大学信息系统建设与运行维护管理办法
评论
0/150
提交评论