260 likes | 493 Views
课件制作:刘达明 023-66834110. 《C 语言程序设计 》 龙昭华主编. 第 9 章 编程技巧. 第一节 表达式解释计算 第二节 C 与操作系统接口设计 第三节 C 与汇编语言的接口 第四节 程序调试问题. 1 / 26. 数字. 数: num(). 数字. 数. 因子: factor(). (. ). 表达式. 因子. 项: term(). * /. 因子. 项. 表达式: expr(). + -. 项. 课件制作:刘达明 023-66834110. (第九章 编程技巧). 第一节 表达式解释计算.
E N D
课件制作:刘达明 023-66834110 《C语言程序设计》龙昭华主编 第9章编程技巧 第一节 表达式解释计算 第二节 C与操作系统接口设计 第三节C与汇编语言的接口 第四节 程序调试问题 1 / 26
数字 数:num() . 数字 数 因子:factor() ( ) 表达式 因子 项:term() * / 因子 项 表达式:expr() + - 项 课件制作:刘达明 023-66834110 (第九章 编程技巧) 第一节表达式解释计算 一、表达式句法定义 需要定义一个含有+、-、*、/的运算表达式。加、减、乘、除四则运算符及 括号的意义完全与平常的算术运算习惯一致。程序接受一行字符串,并对它进 行表达式的解释计算,这就相当于一个简单的编译系统。 2 / 26
课件制作:刘达明 023-66834110 第一节表达式解释计算 (第九章 编程技巧) 二、各句法对应函数 为了便于识别表达式的各成份,并计算它们的值,需要定义相应的函数: num()函数:将数的字符序列转换成数值。如:9、8.12、-10.30、1230等。 factor()函数:计算因子的值。如:-91.89、(90*10.8+5.8)、60等。 term()函数:计算项的值。如:89、(9/2+10)、90*7/10、(10-2)/2等。 exor()函数:计算表达式的值。如:9+(10/2-9)、200-90、30+0.99等。 由于表达式句法的递归定义,这些函数也将通过相互递归调用求得各自对应 成份的值。 假设主函数控制流程是反复读入一行正文,调用表达式计算函数expr(),求 出表达式的值并输出,直至输入空行结束。 程序中引入下列全局变量:buf[]字符数组,存储一行正文信息。cpt为当前 正待识别的字符指针。result为存储表达式计算结果的变量。主函数的算法为: ①读入一行存buf数组。②如果输入行非空转③,否则转⑦。③置cpt初值。④ 计算表达式值result=expr();⑤输出result的值。⑥转①。⑦退出。 在第4步中所用到的函数需要编写,其余函数用标准库函数完成。 3 / 26
课件制作:刘达明 023-66834110 第一节表达式解释计算 (第九章 编程技巧) 1、expr()函数算法 根据表达式的句法定义,expr()函数有以下形式: double expr() { value1=term(); /*调用函数term()求项值*/ while(当前有效字符为‘+’或‘-’) { 保存运算符; value2=term(); /*求下一个项值*/ if(运算符==‘+’) value1+=value2; else value1-=value2; } return value1; } 说明: 当前有效字符是指 非空白字符。 函数term()的算法与函数expr()的算法类似。函数term()调用factor()完成 因子计算,处理乘除法运算符‘*’和‘/’。对于除法需要考虑除数为0的情况。 4 / 26
课件制作:刘达明 023-66834110 第一节表达式解释计算 (第九章 编程技巧) 2、factor()函数算法 ⑴当前有效字符为数字符,因子是一个数。 转换该数的字符序列为数值,作为因子的值。 ⑵当前有效字符为左括号‘(’,因子是一个带括号的表达式。 递归调用函数expr(),以表达式的值作为因子的值。且表达式计算后,当前 有效字符应为右括号‘)’,否则因括号不正确配对,是个句法错误的表达式。 ⑶当前有效字符为其它字符,也是个句法错误的表达式。 else 报告表达式句法错误; return fvalue; } else { 报告表达式句法错误; return 1.0; } } double factor() { if(当前有效字符为数字符) return num(); if(当前有效字符==‘(’) { 移动字符指针至下一个有效字符; fvalue=expr(); if(当前有效字符==‘)’) 移动字符指针至下一个有效字符; 注意: 这里没 有讨论 有‘)’而 没有‘(’ 与之匹 配的问 题。 5 / 26
课件制作:刘达明 023-66834110 第一节表达式解释计算 (第九章 编程技巧) 三、完整的程序 3、num()函数算法 num()函数实际上就是字符转换成数值。先转换整数部分,整数每位数字乘 以10,使用循环完成。如果还有小数点‘.’,则小数点后面的数字乘以0.1,仍使 用循环完成。 例9.1 表达式解释计算的详细程序见li9_1.c。 运行结果: 9+8 (enter) he result is 17.000000 (10-20)*2+5 (enter) he result is -15.000000 9+9*9+1-(10/2+1.9) (enter) he result is 84.100000 (enter) 注:最后一个回车退出循环。 6 / 26
课件制作:刘达明 023-66834110 (第九章 编程技巧) 第二节C与操作系统接口设计 一、BIOS中断表 在C语言编程中,常常需要利用操作系统如DOS的低级资源,而这些资源不能 使用C编译程序访问,必须使用DOS中断(软中断)才能完成。 中断是一种特殊类型的指令,它停止执行当前程序,把系统当前状态保留在 堆栈中,然后转移到由中断号确定的相应的中断处理子程序上。当中断子程序 执行完时,它执行中断返回,使原先运行的程序恢复执行。中断分为硬中断和 软中断两种基本类型。8086CPU允许程序通过INT指令执行软中断,跟在INT指令 后的数字指定所用的中断号。如:INT 21h执行21h号中断。中断号是用来找出 相应的中断处理程序的。 在PC-DOS中,使用软中断访问操作系统功能,每个中断命令都有其专门的 访问功能类型,而且这些功能函数是由AH寄存器中的值决定的。。如果需要增 加信息,所增加的信息传给AX、BX、CX和DX寄存器。 PC-DOS操作系统分为ROM-BIOS(Basic I/O System)和DOS(Disk Operating System)两部分。ROM-BIOS提供最低层子例程,而DOS用这些低层子例程提供进 一步的高级功能。二者是交叉在一起的,用户访问它们的方法基本相同:都是 通过软中断。 7 / 26
中断号 中断号 功 能 功 能 盒带控制 15h 屏幕打印服务程序 5h 16h 键盘I/O 显示器I/O 10h 17h 设备清单 11h 打印机I/O 内存大小 12h 18h 执行ROM BASIC 19h 磁盘I/O 执行引导装入程序 13h 系统时间和日期设置 1Ah 14h 串行口I/O 课件制作:刘达明 023-66834110 第二节C与操作系统接口设计 (第九章 编程技巧) BIOS中断表(续) 在PC-DOS中,ROM-BIOS有12个中断,如下表所示: 访问上表中断有两种方法:一是使用系统调用函数int86(),二是使用汇编语言接 口来实现。大多数C都提供了int86()函数。int86()函数的一般格式如下: #include <dos.h> int int86(int intnum, union REGS *in, union REGS *out) 8 / 26
课件制作:刘达明 023-66834110 第二节C与操作系统接口设计 (第九章 编程技巧) 二、利用int86()函数访问BIOS系统功能 在int86()中需要用到的结构体与共用体如下: struct BYTEREGS { unsigned char al, ah; unsigned char bl, bh; unsigned char cl, ch; unsigned char dl, dh; }; /*寄存器字节*/ /*字符寄存器*/ struct WORDREGS { unsigned int ax, bx, cx; unsigned int dx ,si, di unsigned int cflag, flags; }; union REGS { struct WORDREGS x; struct BYTEREGS h; }; 例9.2利用10h号中断功能6,可以实现清屏。 #include <dos.h> void cls() { union REGS r; r.h.ah=6; /*屏幕转动程序*/ r.h.al=0; /*清屏程序*/ r.h.ch=0; /*上转起始行*/ r.h.cl=0; /*列起始*/ r.h.dh=24; /*上转结束行*/ r.h.dl=79; /*列结束*/ r.h.bh=7; /*空行是黑色*/ int86(0x10,&r,&r); } 9 / 26
sc[0] sc[1] int c; char sc[2]; 课件制作:刘达明 023-66834110 第二节C与操作系统接口设计 (第九章 编程技巧) 例9.3 调用16h号中断0号功能读取键盘扫描码。 在为IBM-PC及其兼容机编程时,最难读到的是箭头键和功能键,以及INS、 DEL、PGUP、PGDN、END、HOME等键的ASCII码值。键值存放如下: 当用户在IBM-PC机上按下一个键时,产生一个称为扫描码的两个字节(16位) 的值。该扫描码由两部分组成:低位字节内含相应键的ASCII码(若它是标准键), 高位字节内含该键在键盘上的定位码。对于标准键的定位码为0,因此低位的值 就是它的ASCII码值(8位)。而对于特殊键,它的低位值为0,高位的值才是它的 键值。要得到特殊键值,不能使用gets()、scanf()等函数,只能用这里提供的方 法。其程序参见li9_3.c。运行结果显示部分键盘扫描码为: A-65 a-97 0-48 9-57 (-40 +-43 <-60 =-61 左箭头-75右箭头-77 上箭头-72下箭头-80 INS-82 DEL-83 F2-60 F3-61 F10-68 ESC-27 PGUP-73 PGDN-81 END-79 HOME-71 int get_key() { union REGS r; r.h.ah=0; return(int86(0x16,&r,&r)); } 10 / 26
课件制作:刘达明 023-66834110 第二节C与操作系统接口设计 (第九章 编程技巧) 三、利用DOS访问系统功能 PC-DOS中由ROM-BIOS引导装入程序装入和执行的部分叫DOS。其中包含了大 部分在ROM-BIOS例程中找不到的各种各样的高级功能,利用AH寄存器传送所请 求的DOS功能调用号,通过中断21h可以访问DOS的所有功能。如1号功能为从键 盘读字符,2号功能为在屏幕上显示字符,3号功能从异步端口读字符,4号功能 写字符到异步端口,5号功能在打印机上打印字符,B号功能检查键盘状态,2A 号功能读取系统日期,2B号功能设置系统日期,2C号功能读取系统时间等。 虽然可以像ROM-BIOS功能一样,利用int86()函数访问DOS功能,但很多系统 都有一专门函数bdos(),该函数用来执行21h号中断调用,调用操作系统中的某 个高级功能。 bdos()函数原型如下: int bdos(int fnum,unsigned int Reg_DX,unsigned int Reg_AL) 其中:fnum是DOS功能号; Reg_DX的值赋给DX寄存器; Reg_AL的值赋给AL寄存器; 返回值:bdos()回送AX寄存器的值。 11 / 26
课件制作:刘达明 023-66834110 第二节C与操作系统接口设计 (第九章 编程技巧) 例9.4 调用12h号中断Bh号功能检查键盘状态。 除了第一个参数外。其余都用0,因为不 需要其它信息。把返回值强行变成char型 是必要的,因为所返回的是在AL中的状 态,而AL没有定义。 #include <dos.h> int kbhit() { return((char)bdos(0xB,0,0)); } kbhit()函数的返回值为:如果按下键,则返回“真”,否则返回“假”。kbhit()函数 的一个非常普通的用途就是可以让某个子例程被用户命令所中断。 例9.5调用12h号中断的3号功能读串口,4号功能写串口。如果需要编写一个调 制解调器程序,就要用到在异步串行口上进行读写字符。 这里又强行作了char 转换,以保证放在AH 寄存器中的任何值, 都不会让任何调用例 程乱了套。 /*向串口写字符*/ #include <dos.h> int put_async(char ch) { bdos(0x4,ch,0); } /*从串口读字符*/ #include <dos.h> int get_async() { return((char)bdos(0x3,0,0)); } 12 / 26
课件制作:刘达明 023-66834110 (第九章 编程技巧) 第三节与汇编语言的接口 在C语言编程中,需要使用汇编语言编写例程大概有三个方面的原因: ⑴为了提高速度和效率。 ⑵为了实现某些C语言中不具备、但为不同的机器所特有的功能。 ⑶为了利用通用的汇编语言例程。 把汇编程序模块和用户的C程序结合起来,主要有两种方法:第一种,单独 编写汇编例程,然后再将它同自己的程序连接起来。第二种,使用大多数C编译 器所具有的内部汇编程序功能。 有的C编译系统内部汇编使用#asm开头,使用#endasm结束,中间全部是汇编 语句。而TurboC则不同,每行汇编语句都要以asm开头。如:两数相乘的函数。 int mul(int a,int b) { #asm mov ax,word ptr 8[bp] imul ax,word ptr 10[bp] #endasm } int mul(int a,int b) { asm mov ax,word ptr 8[bp] asm imul ax,word ptr 10[bp] } 13 / 26
课件制作:刘达明 023-66834110 (第九章 编程技巧) 第四节程序调试问题 一、程序易出错问题 1、忘记定义变量。 如:main() {x=6;y=8;z=x+y;} 在使用变量之前,必须加:int x,y,z; 定义变量。 2、输入输出数据的类型与所用格式说明符不一致。 如:int a=3; float b=4.5; printf(“%f,%d\n”,a,b);结果可能不是所需。 3、未注意int型数据的取值范围。 如:int num; num=89101; printf(“%d\n”,num); 造成num超界益出。 4、输入变量时忘记使用地址符。 如:scanf(“%d %d”,a,b); 应改为:scanf(“%d %d”,&a,&b); 5、输入时数据的组织与要求不符。 如:scanf(“%d %d”,&a,&b);输入若按:3,4(Enter)则是错的,两数之间应 为空格,而不是逗号。 再如:scanf(“Input a & b: %d,%d”,&a,&b);想在屏幕上提示Input a & b: 再输入则是不正确的。可改用printf(“Input a&b:”); scanf(“%d,%d”,&a,&b); 这时屏幕显示:Intput a&b: 后再输入:3,4(Enter) 14 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 6、误把“=”作为“等于”比较符。 如:if(a=b) 应改为:if(a==b) 应该用“==”作为“等于”比较符。前则为 赋值操作。 7、语句后面漏分号。 如:a=4 b=5 应为:a=4; b=5; 复合语句的最后一句也应该有分号。 如:{t=a; a=b; b=t } 应改为:{t=a; a=b; b=t; } 8、在不该加分号的地方加分号。 如:if(a>b); printf(“a is larger than b.\n”); 再如:for(i=0;i<10;i++);{scanf(“%d”,&x);printf(“%d\n”,x*x);}它不能 输入10个数据,而只能输入一个数据。 9、对应该有花括弧的复合语句,忘记加花括弧。 如:sum=0; i=1; while(i<=100) sum=sum+i; i++; 结果是在循环中i的值 永远不变,循环是死循环。循环应改为:while(i<100){sum=sum+i;i++;} 10、括号不配对。 如:while((c=getchar()!=‘#’) putchar(c); while语句后面少了右括号。 15 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 11、在用标识符时,忘记了大写字母和小写字母的区别。 如:int a,b,c; a=2; b=3; C=A+B; 大写与小写字母在C语言中为两个不同 的标识符。 12、引用数组元素时误用了圆括号。 如:int i,a(10); for(i=0;i<10;i++) scanf(“%d”,&a(i)); 应改为: int i,a[10]; for(i=0;i<10;i++) scanf(“%d”,&a[i]); 13、在定义元素时,将定义的“元素个数”误认为是“可使用的最大下标值”。 如:int a[10]={1,2,3,4,5,6,7,8,9,10},i; for(i=1;i<=10;i++) printf(“%d\t”,a[i]); 数组下标是从0开始,到“元素个数-1”为止的。for应为:for(i=0;i<10;i++) 14、对二维数组或多维数组的定义和引用的方法不对。 如:int a[5,4]; … printf(“%d”,a[1,2]); …。数组的每一维下标均应该 用一对方括号括起来。而a[1,2]相当于a[2]为数组第4行的首地址。 应改为:int a[5][4]; printf(“%d”,a[1][2]); 15、误以为数组名代表数组中全部元素。 如:int a[4]={1,3,5,7}; printf(“%d,%d,%d,%d\n”,a); 16 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 16、混淆字符数组与字符指针的区别。 如:char s1[20],*s2; s1=“Computer and C”; s2=“C and Computer”; printf(“s1=%s,s2=%s\n”,s1,s2); 编译出错。s1是数组名,代表数组首地址,是 常量,不能再赋值。而s2是指向字符数据的指针变量,对s2可以赋值。s1的赋值 应改为:strcpy(s1,”Computer and C); 或 char s1[20]=“Computer and C”; 17、在引用指针变量之前没有对它赋予确定的值。 如:char *p,c[20]; scanf(“%s”,p); 应在这两句间加上:p=c;语句。 18、switch语句的各分之中漏写break语句。 switch(score) { case 5: printf(“Very good!”); case 4: printf(“Good!”); break; case 3: printf(“Pass!”); case 5: printf(“Fail!”); defult: printf(“data error!”); } 如果score为5时,将打印出: Very good! Good! 而我们希望只有 Very good!输出。程序应改为:在 每个case标号后执行完对应的语句 之后要加上break;语句。case后是一 个常量值,可用整型数据常量或字 符数据常量,只起语句标号作用。 17 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 19、混淆字符和字符串的表示形式。 如:char sex,a[10]=‘a’; sex=“M”; 应改为:char sex,a[10]=“a”;sex=‘M’; 20、使用自加(++)和自减(--)运算符时出错。 如:int *p,a[4]={1,3,5,7},x; p=a; x=*p++; 作用是将*p(这时应为a[0]) 赋给x,然后p再自加变成指向a[1]元素。但过段时间后,程序员觉得应该为: 将p所指向元素的值乘以p所指向元素的值再赋给x。写成:x=*p++*(*p);其结果 相当于:a[0]*a[1],这不是我们要的结果a[0]*a[0]。应为:x=*p*(*p++); 21、有人习惯用传统的方式对函数形参进行声明,但却把对函数的形参和 函数中的局部变量混在一起定义。 如:max(x,y) 应改为:max(x,y) int x,y,z; int x,y; { { int z; z=x>y?x:y; z=x>y?x:y; return(z); return(z); } } 18 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 22、所调用的函数在调用语句之后才定义,而又在调用之前未加声明。 如:main() float max(float x,float y) { float x=3.5,y=-7.6,z; {return(x>y?x:y); } z=max(x,y); 必须在main()函数中声明: printf(“%f\n”,z); float max(float,float); } 或在main()函数之前定义max函数。 23、误认为形参值的改变会影响实参的值。 如:main() swap(int x,int y) { int a=3,b=4; { int t; swap(a,b); t=x; x=y; y=t; printf(“%d,%d\n”,a,b); } } 可用指针方式定义swap函数解决传递。 24、函数的实参和形参类型不一致。 如:main() fun(float x,float y) { int a=3,b=4,c; { … } c=fun(a,b); …} 可改为:fun(int x,int y) 或a,b为float。 19 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 25、不同类型的指针混用。 应改为:main() { int i=3,*p1; float a=1.5,*p2; p1=&i; p2=&a; p2=(float *)p1; printf(“%d,%d\n”,*p1,*p2); } 如:main() { int i=3,*p1; float a=1.5,*p2; p1=&i; p2=&a; p2=p1; printf(“%d,%d\n”,*p1,*p2); } 26、没有注意函数参数的求值顺序。 如:i=3; printf(“%d,%d,%d\n”,i,++i,++i); 有些系统输出:3,4,5。而TurboC等有 些系统则输出:5,5,4。即求函数参数表达式的值的顺序有:从左向右的,也有 从右向左的,注意区分顺序。一般改用先赋值,再带入不含++或--的变量。 27、混淆数组名与指针变量的区别。 应改为:int i,a[5],*p; for(p=a,i=0;i<5;i++) scanf(%d”,p++); 如:int i,a[5]; for(i=0;i<5;i++) scanf(%d”,a++); 20 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 28、混淆结构体类型与结构体变量的区别,对一个结构体类型赋值。 应改为:struct worker { int num; char name[20]; }worker1; worker1.num=1001; strcpy(worker1.name,”Li”); 如:struct worker { int num; char name[20]; }; worker.num=1001; strcpy(worker.name,”Li”); 29、使用文件时忘记打开,或打开方式与使用情况不匹配。 如:对文件的读写,用只读方式打开,却企图向该文件输出数据。此外,有 的程序常忘记关闭文件,虽然系统会自动关闭所用文件,但可能会丢失数据。 其它还有指针引用错误造成野指针、函数重名、恶性语法错误、边界错误、 函数说明的疏漏、调用参数错误、scanf()与gets()函数的区别等等。 以上错误在多练习C语言编程后可以克服,并且也容易检查。在深入使用C语 言后,还会出现其它一些更深入、更隐蔽的错误。 21 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 二、程序出错的三种情况 1、语法错误 程序违背C语法规定引起的错误。编译程序一般能发现错误,根据“出错信息” 可以很快发现并及时纠正。 2、逻辑错误 程序没有语法错误,但执行结果与原意不符。如:sum=0; i=1; while(i<=100) sum=sum+i; i++; 最后两句应用一对花括号括起来。这种错误比较难查,要求程 序员有较丰富的经验。 3、运行错误 程序既无语法错误,也无逻辑错误,但在运行时出现错误,甚至停止运行。 如:int a,b,c; scanf(“%d %d”,&a,&b); c=b/a; printf(“c=%d\n”,c); 如果输入a的值 为0的话,就会出错。因此程序应经受各种数据的“考验”,应具有“健壮性”。 写完一个程序只能说完成任务的一半(甚至不到一半)。调试程序往往比写程序 更难,更需要精力、时间和经验。程序员需要通过大量的实践来掌握调试程序 的方法和技术。 22 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 三、程序调试 所谓程序调试是指对程序的查错和排错。调试程序一般经过以下几个步骤。 1、人工检查,即静态检查 程序写好后,首先应进行人工检查。作为一个程序员应当养成严谨的科学作 风,每一步都要严格把关,不要把问题留给后面的工序。为了更有效地进行人 工检查,编程人员应力求做到以下几点: ⑴应当采用结构化程序方法编程,以增加可读性。 ⑵尽量多加注释,以帮助理解每段程序的作用。 ⑶在编写复杂程序时,不要将全部语句都写在main函数中,而要多利用函数, 用一个函数来实现一个单独的功能。各函数之间除用参数传递数据这一渠道外, 数据间尽量少出现偶合关系,便于分别检查和处理。 ⑷编写程序时,要注意编程风格。好的编程风格也便于查错。 2、上机调试 通过上机发现错误称为动态检查。主要根据编译时给出的错误信息来纠错。 应当注意:有时提示的出错行并不是真正的出错行,应往上查找。可能改了前 面的某一个错误后,后面的大片错误就消失。要分析,找出真正的错误行。 23 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 3、检查运行结果的正确性 在改正“错误”(error)和“警告”(warning)后,程序经过连接(link)就得到可执行的 目标程序。运行程序,输入程序所需要的数据,就可得到运行结果。还应当分 析运行结果,检查它是否符合要求。在验证程序时,应精心选择典型、苛刻而 带有刁难性的几组数据进行输入,看它是否能够得出满足要求的结果。 4、检查运行结果错误的方法 如果运行结果不对,大多属于逻辑错误。对这类错误往往需要仔细检查和分 析才能发现。可以采用以下方法: ⑴将程序与算法仔细对照。算法正确,就是程序错误,仔细对照就很快发现 错误。算法不对,就修正算法,再改写程序。 ⑵如果实在找不到错误,就采取“分段检查”的方法。在程序不同位置设计个 printf()函数输出相关信息或变量值,逐段往下查。直到找到在某一段中数据 不对为止。 ⑶也可用“条件编译”命令来处理调试用的printf()函数语句,用完不必删除。 ⑷如果程序没有错误,就要检查算法了。修改算法后再修定程序。 ⑸有的系统还提供了debug(调试)工具,可跟踪程序运行并给出相应信息。 24 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 三、C的存储方式 在使用8086系列处理机上运行C程序时,C编译系统都提供了6种存储方式: 小方式、一般方式、中等方式、压缩方式、大方式、特大方式。8086用分段存 储结构,有四个段:代码段、数据段、堆栈段和附加段。一个段在RAM中占 64K,段的起点正好是16字节的偶数倍。 1、小方式 要求程序、数据和栈都必须在同一64K段内。用它编译的程序,目标码最少, 执行起来最快。可用DOS的EXE2BIN命令转换成.COM文件。 2、一般方式 这种方式是缺省方式,用得最多。程序代码单独占用64K,数据码占用另外64 K。按这种方式编译的程序最大为128K。 3、中等方式 机器码超过了一般方式下的一个段的限制的大程序,就要用中等方式编译。 这种方式适合只使用少量数据的大程序。 4、压缩方式 压缩方式与中等方式相反,适合使用大量数据,但本身程序不大的情况。 25 / 26
课件制作:刘达明 023-66834110 第四节程序调试问题 (第九章 编程技巧) 5、大方式 大方式允许程序码和数据都使用多个段。但最大数据项(如数组)单项最多只能 占用64K当程序和数据都很大时,要用大方式。 6、特大方式 特大方式与大方式有一点不同,数据单独可占64K以上的内存。 方式的选择: 除非有特殊理由,一般情况下都应使用一般方式。 当程序很大,但数据不多时,可选用中等方式。 而程序不大,数据却很多时,应选用压缩方式。 如果程序和数据都不小,则选用大方式。 当某些数据项目单项大于64K时,用特大方式。 在8086上运行C程序,还可以使用far、near、huge三个说明符来解决存储方式 混用的问题,它们只能用于指针和函数。详细信息参见有关资料。 26 / 26