模块化程序设计-函数 重写例1.1—模块化程序设计 函数 程序设计实例 作业: 5.1 5.2 5.3 5.6 5.7 5.8 作业: 5.1 5.2 5.3 5.6 5.7 5.8 练习: 5.5 5.13 5.17
§5.1 重写例1.1—模块化程序设计
回顾第四章最后的例4.16,打印前六行和后五行中的“打印第x行”程序是一样的,在程序中重复写了一次。 for ( x=‘A’;x<=‘F’;x++){ for ( y=x; y<='I';y++) printf (" %c", y); for ( y='A'; y<x; y++) printf (" %c", y); for ( i=1;i<=((5-(x-'A'))*2);i++) printf ( " "); for ( i=1;i<=2*(x-'A')+1;i++) printf (" %c", x); for ( i=1;i<=((5-(x-'A'))*2); i++) printf ( " "); for ( y=x+1; y<='I';y++) printf ( " %c", y); for ( y='A'; y<=x;y++) printf ( " %c", y); printf ( "\n"); } for ( x='E';x>='A';x--){ for ( y=x; y<='I';y++) printf ( " %c", y); for ( y='A'; y<=x-1;y++) printf ( " %c", y); for ( i=1;i<=((5-(x-'A'))*2);i++) printf ( " "); for ( i=1;i<=2*(x-'A')+1;i++) printf (" %c", x); for ( i=1;i<=((5-(x-'A'))*2); i++)printf ( " "); 回顾第四章最后的例4.16,打印前六行和后五行中的“打印第x行”程序是一样的,在程序中重复写了一次。
再回顾第一章例1.1的程序例1.3和例2.4,“计算三角形ABD的面积”和“计算三角形BCD的面积”程序也是一样的,在程序中也重复写了一次;另外在计算三角形面积时,计算一个边长度的程序也是一样的,在程序中重复照写了五次。 ab=sqrt( (xa-xb)*(xa-xb)+(ya-yb)*(ya-yb) ); //边ab长 bd=sqrt( (xb-xd)*(xb-xd)+(yb-yd)*(yb-yd) ); //边bd长 ad=sqrt( (xa-xd)*(xa-xd)+(ya-yd)*(ya-yd) ); //边ad长 s=(ab+bd+ad)/2; s1=sqrt( s*(s-ab)*(s-bd)*(s-ad) );//△ABD面积 bc=sqrt( (xb-xc)* (xb-xc)+(yb-yc)* (yb-yc) ); //边BC长 cd=sqrt( (xc-xd)* (xc-xd)+(yc-yd)* (yc-yd) ); //边CD长 s=(bc+bd+cd)/2; s2=sqrt( s*(s-bc)*(s-bd)*(s-cd) ); // △BCD面积
以模块为指导思想的程序设计过程称模块化程序设计。 例5.1 重写例1.1 运行结果演示 float xa,ya,xb,yb,xc,yc,xd,yd; float s1,s2,ss,m; void main(void) { printf("please input xa,ya,xb,yb, xc,yc,xd,yd:\n"); scanf("%f%f%f%f%f%f %f%f ",&xa,&ya,&xb,&yb,&xc,&yc,&xd,&yd); s1 = areauvw (xa, ya, xb, yb, xd, yd ); s2 = areauvw (xb, yb, xc, yc, xd, yd ); ss=s1+s2; ss=ss/1000; m=ss*950; printf(“m=%10.3f\n”,m); } float liners( float xr,float yr,float xs,float ys ){ return sqrt( (xr-xs)*(xr-xs)+(yr-ys)*(yr-ys) ); float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ float uv , uw , vw ; float s ; uv = liners ( xu, yu, xv, yv ); uw = liners ( xu, yu, xw, yw ); vw = liners ( xv, yv, xw, yw ); s=(uv+uw+vw)/2; return sqrt( s*(s-uv)*(s-uw)*(s-vw); 对照例5.1与例2.4的程序。显然例5.1干净、利索、清晰,即好读又好看,并且与原始问题目有相当高的可对照性。因为这里使用了“子程序”技术 以模块为指导思想的程序设计过程称模块化程序设计。 这就是模块化程序。
在软件发展史上,引进子程序概念是一个重大成就,对程序设计技术发展有着重大影响,它是模块化、分块编译、逐步求精等技术的基础。 子程序技术起源于前述的原因——简化代码、缩小代码长度以及数学中的函数概念。但后来随着子程序技术的发展,尤其是在结构化程序设计中,它已不仅仅是为了简化代码,而被提升到程序设计抽象的高度来认识了。 因此,不但简化代码时使用子程序,而且为了程序概念的抽象和程序的可读性及清晰性,有时在看起来操作极简单,不必使用子程序,甚至于只有一小段代码的地方也引进子程序。 例如例5.1中的函数lines,只计算一个表达式,完全可以不引进函数。但是引进函数后,使得程序与问题本身具有极强的可对照性,十分清晰。从而增加了程序的可读性,也为程序正确性打下良好基础。
子程序技术是“自顶向下、逐步求精”的基础。 “自顶向下、逐步求精”是一种思维方式: 对于某一个要解决的问题,首先从问题的整体出发,将它分解成独立的若干个子问题。 向下再一个个的具体考虑下一层的各个子问题,针对每个子问题,仍采用对待整体问题解的思路,继续对其进行分解(求精),得到该子问题解法的分解步骤。 如此下去,直到最低层的每个子问题都能明显写出解法为止。便找到解决整体问题的解题算法了。
子程序技术将“做什么”与“怎么做”分 离开 在程序设计过程中,当为了要完成某一操作而调用一个子程序时,程序员只关心要做什么即可,而对于怎样去完成这一操作,完全可以不必操心。使程序员可以集中精力考虑高一层次的算法,不必为具体的某一微小细节而缠绕住。 然后,当回过头来(或由别人)设计子程序本身时再具体关心“怎么做”,设计具体算法、具体考虑怎样去实现总体上的要求。 使用子程序技术分离了“做什么”与“怎么做”使得: 程序的逻辑结构清晰,程序易写,易读,易懂。 程序的设计,调试,维护变得容易。
例5.1程序执行的主要步骤 从第18行主函数开始,执行 printf("please input xa,ya,xb,yb,xc,yc,xd,yd:\n"); 输出一行提示信息: please input xa,ya,xb,yb,xc,yc,xd,yd : 执行第19行函数调用 scanf("%f%f%f %f%f%f%f%f ",&xa,&ya, &xb,&yb,&xc,&yc,&xd,&yd); 等待操作员键入数值。设操作员键入 547 411 804 77 39 208 116 332 当操作员键入并回车之后,变量xa、ya、xb、yb、xc、yc、xd、yd分别取得相应值。
第20行以xa、ya、xb、yb、xd、yd为实参调用函数areauvw 将547、411、804、77、116、332分别 送入areauvw的形参xu、yu、xv、yv、xw、yw中 进入函数areauvw ,执行areauvw的操作部分 第11行,以xu,yu , xv , yv为实在参数调用函数liners,计算边uv长 分别计算xu,yu , xv , yv得547、411、804、77 ; 将547、411、804、77顺序送入liners形参xr, yr, xs, ys中 第6行返回语句 return sqrt( (xr-xs)* (xr-xs)+(yr-ys)* (yr-ys) ); 计算出表达式的值为 421.432 带着函数值421.432返回调用处 areauvw中第11行,给uv赋值。uv的值为421.432
第21行,计算三角形BCD的面积,s2=52473.5 第22行,计算总面积,ss=134602 第23行,折合成亩,ss=134.602 第12行,以xu,yu , xw , yw为实参调用函数liners,计算边uw长uw。uw值为:438.18 第13行,以xv,yv , xw , yw为实参调用函数liners,计算边vw长vw。vw值为:733.736 第14行,计算出s=796.674 15行返回语句。计算表达式的值为82128.5,也就是函数areauvw的值。 带着函数值82128.5返回调用处主程序第20行 第21行,计算三角形BCD的面积,s2=52473.5 第22行,计算总面积,ss=134602 第23行,折合成亩,ss=134.602 第24行,计算总产量,m=127871.9 第25行,输出函数printf在屏幕显示: m=127871.90
§5.2 函数 函数定义 /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ §5.2 函数 函数定义 /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ // 8 float uv , uw , vw ; // 三条边长 //9 float s ; //10 uv = liners ( xu, yu, xv, yv ); //边uv长 //11 uw = liners ( xu, yu, xw, yw ); //边uw长 //12 vw = liners ( xv, yv, xw, yw ); //边vw长 //13 s=(uv+uw+vw)/2; //s //14 return sqrt( s*(s-uv)*(s-uw)*(s-vw) );//面积//15 }
函数调用 void main(void) { // 主函数 printf("please input xa,ya,xb,yb, xc,yc,xd,yd:\n"); //18 scanf("%f%f%f%f%f%f %f%f ",&xa,&ya, &xb,&yb,&xc,&yc,&xd,&yd); //19 s1 = areauvw (xa, ya, xb, yb, xd, yd ); //20 s2 = areauvw (xb, yb, xc, yc, xd, yd ); //21 ss=s1+s2; //22 ss=ss/10000; // 折合成亩 //23 m=ss*950; // 计算总产量 //24 printf(“m=%10.3f\n”,m); // 打印输出 //25 } 函数调用
§5.2.1 函数定义 除标准库函数外, 程序中使用函数必须先定义,然后再用“函数调用”调用它。 标准函数是系统已经定义好的函数,不必定义即可直接调用。 §5.2.1 函数定义 /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ // 8 float uv , uw , vw ; // 三条边长 //9 float s ; //10 uv = liners ( xu, yu, xv, yv ); //边uv长 //11 uw = liners ( xu, yu, xw, yw ); //边uw长 //12 vw = liners ( xv, yv, xw, yw ); //边vw长 //13 s=(uv+uw+vw)/2; //s //14 return sqrt( s*(s-uv)*(s-uw)*(s-vw) );//面积//15 }
函数定义的形式: 函数类型 函数名 形式参数 /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ 函数定义说明符 形式参数 /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ // 8 float uv , uw , vw ; // 三条边长 //9 float s ; //10 uv = liners ( xu, yu, xv, yv ); //边uv长 //11 uw = liners ( xu, yu, xw, yw ); //边uw长 //12 vw = liners ( xv, yv, xw, yw ); //边vw长 //13 s=(uv+uw+vw)/2; //s //14 return sqrt( s*(s-uv)*(s-uw)*(s-vw) );//面积//15 } 形式参数表 函数体 复合语句 参数类型 返回语句
int f(int x, int y, float z) 函数类型 函数名 函数定义说明符 形式参数 int f(int x, int y, float z) int f(int x,y ; float z) int f(int x,y , float z) /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ // 8 float uv , uw , vw ; // 三条边长 //9 float s ; //10 uv = liners ( xu, yu, xv, yv ); //边uv长 //11 uw = liners ( xu, yu, xw, yw ); //边uw长 //12 vw = liners ( xv, yv, xw, yw ); //边vw长 //13 s=(uv+uw+vw)/2; //s //14 return sqrt( s*(s-uv)*(s-uw)*(s-vw) );//面积//15 } 形式参数表 函数体 复合语句 参数类型 形式如下 TT F ( 参数列表) 具体指明: 函数的结果类型 ——由“类型说明符”( TT )标明; 函数的名字 ——由类型说明符后的“标识符” ( F )标明; 函数的形式参数个数和每个形式参数的特性 ——由“参数列表”标明。 函数类型 函数的结果类型 缺省 int 类型 结果类型不能是数组类型、函数类型 函数可以是无值的,即“无类型”的 void 参数列表形式 T id ,T id ,... ,T id C允许使用无参函数,无参函数的参数列表为空,或使用“空类型”的类型说明符“void” TT F() TT F (void ) 函数名是一个标识符 每个参数声明形式: 类型说明符 标识符 返回语句
复合语句(compound-statement)由声明和语句列表组成 声明部分具体的说明本函数内使用的其它量; 语句部分规定在本函数中要执行的算法动作。 函数类型 函数名 函数定义说明符 形式参数 /* 计算三角形uvw面积,参数:u点x、y坐标,v点x、y坐标,w点x、y坐标 */ float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ // 8 float uv , uw , vw ; // 三条边长 //9 float s ; //10 uv = liners ( xu, yu, xv, yv ); //边uv长 //11 uw = liners ( xu, yu, xw, yw ); //边uw长 //12 vw = liners ( xv, yv, xw, yw ); //边vw长 //13 s=(uv+uw+vw)/2; //s //14 return sqrt( s*(s-uv)*(s-uw)*(s-vw) );//面积//15 } 返回语句形式: return 表达式; return ; 形式参数表 函数体 复合语句 参数类型 返回语句
函数定义的形式: TT F ( T id ,T id ,... ,T id ){ ... }
实在参数表由逗号分隔开的一个个表达式组成 sqrt(s*(s-uv)*(s-uw)*(s-vw)) void main(void) { // 主函数 printf("please input xa,ya,xb,yb, xc,yc,xd,yd:\n"); //18 scanf("%f%f%f%f%f%f %f%f ",&xa,&ya, &xb,&yb,&xc,&yc,&xd,&yd); //19 s1 = areauvw (xa, ya, xb, yb, xd, yd ) ; //20 s2 = areauvw (xb, yb, xc, yc, xd, yd ) ; //21 ss=s1+s2; //22 ss=ss/10000; // 折合成亩 //23 m=ss*950; // 计算总产量 //24 printf(“m=%10.3f\n”,m); // 打印输出 //25 } §5.2.2 函数调用 实在参数 实在参数表 被调用函数名 调用过程 首先顺序计算实参表中各实参值 然后把这些值顺序传入形参表的各个形参中 最后进入函数执行复合语句。 被调用函数名:标识符 实在参数表由逗号分隔开的一个个表达式组成 一般形式 F (U, U, …,U) F ( ) 每个实在参数是一个表达式 liners (xu,yu, xv, yv) sqrt(s*(s-uv)*(s-uw)*(s-vw))
调用一个函数的大致执行过程是: 主程序 函数 调用函数 返回 结束
一、参数结合 动作 计算实参表达式的值 规则 把实参的值按赋值转换规则,转换成形参的类型。如果不能完成该转换,则称函数参数不一致,产生错误 float xa,ya,xb,yb,xc,yc,xd,yd; float s1,s2,ss,m; void main(void) { … … … … … … … … s1 = areauvw (xa, ya, xb, yb, xd, yd ); s2 = areauvw (xb, yb, xc, yc, xd, yd ); } float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ float uv , uw , vw ; float s ; uv = liners ( xu, yu, xv, yv ); uw = liners ( xu, yu, xw, yw ); vw = liners ( xv, yv, xw, yw ); s=(uv+uw+vw)/2; return sqrt( s*(s-uv)*(s-uw)*(s-vw); 动作 计算实参表达式的值 把实参的值按赋值转换规则,转换成形参的类型。如果不能完成该转换,则称函数参数不一致,产生错误 把转换后的实参值送入形参 各个实参的计算次序是依赖于实现的。 规则 静态上看,实参表中的实参与被调用函数中形参表的形参,按位置从左向右依次一一对应 对应位置上的形实参间要赋值兼容
int f (int x, int y){ return x*y; } void main(){ int a=3, b=2,x=9; int c; c=f(a+b,a+x); printf(“%d\n”, c); printf(“%d\n”, x); y 12 x 5 f 返回值 60 c 60 x 9 b 2 程序输出为: 60 9 main a 3 内存
由左至右 z 2 返回值 4 f …… v 4 u 返回值 g y main x 1 内存 #include "stdio.h" int x,y ; int f( int z ){ x=x+1 ; return (z*z); } int g( int u , int v ){ … … void main(){ x=0 ; g(x,f(2)) ; z 2 返回值 4 f …… v 4 u 返回值 g y main x 1 内存
由右至左 z 2 返回值 4 f …… v 4 u 1 返回值 g y main x 1 内存 #include "stdio.h" int x,y ; int f( int z ){ x=x+1 ; return (z*z); } int g( int u , int v ){ … … void main(){ x=0 ; g(x,f(2)) ; z 2 返回值 4 f …… v 4 u 1 返回值 g y main x 1 内存
并行计算-----不知道结果 #include "stdio.h" int x,y ; int f( int z ){ x=x+1 ; return (z*z); } int g( int u , int v ){ … … void main(){ x=0 ; g(x,f(2)) ;
二、函数返回 返回方式: return ; return 表达式; 函数运行到复合语句末尾。 float xa,ya,xb,yb,xc,yc,xd,yd; float s1,s2,ss,m; void main(void) { … … … … … … … … s1 = areauvw (xa, ya, xb, yb, xd, yd ); s2 = areauvw (xb, yb, xc, yc, xd, yd ); } float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ float uv , uw , vw ; float s ; uv = liners ( xu, yu, xv, yv ); uw = liners ( xu, yu, xw, yw ); vw = liners ( xv, yv, xw, yw ); s=(uv+uw+vw)/2; return sqrt( s*(s-uv)*(s-uw)*(s-vw);
三、函数值 执行返回语句时,表达式计算出的值 float xa,ya,xb,yb,xc,yc,xd,yd; float s1,s2,ss,m; void main(void) { … … … … … … … … s1 = areauvw (xa, ya, xb, yb, xd, yd ); s2 = areauvw (xb, yb, xc, yc, xd, yd ); } float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ float uv , uw , vw ; float s ; uv = liners ( xu, yu, xv, yv ); uw = liners ( xu, yu, xw, yw ); vw = liners ( xv, yv, xw, yw ); s=(uv+uw+vw)/2; return sqrt( s*(s-uv)*(s-uw)*(s-vw);
有返回类型的函数 int f (int x, int y){ if (x>=y) return x; else return y; } 无返回类型的函数 void g( int w){ if (w==1) return; else x=3; }
有返回类型函数的函数值 使用 return e; 向调用函数的主程序传递函数值 return e ; 的执行过程是: 计算表达式e的值 把表达式值按赋值转换规则,转换成函数的结果类型; 返回语句中表达式的类型与函数的结果类型必须赋值兼容。 用类型转换后的值作为函数值,并带着它返回到调用该函数处
无返回类型函数的函数值 在函数调用处,所调函数无值可以带回。 void g( int w){ if (w==1) return; else x=3; } 在函数调用处,所调函数无值可以带回。 对于无类型函数,在函数调用处不需要函数值,这种返回是正常的; 对于有类型函数,在函数调用处极可能需要函数值参加下一步运算,这将带来不可预料的结果
任何标识符都必须声明,而且必须先声明后使用 §5.2.3 先调用后定义—函数原型 为什么使用函数原型 任何标识符都必须声明,而且必须先声明后使用 在声明定义函数时必须保证函数声明位置在使用前出现 从程序行文顺序上控制 ——一般比较难实现 函数原型—— 方便灵活
float xa,ya,xb,yb,xc,yc,xd,yd; float s1,s2,ss,m; float liners( float xr,float yr,float xs,float ys ) ; float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ); void main(void) { printf("please input xa,ya,xb,yb, xc,yc,xd,yd:\n"); scanf("%f%f%f%f%f%f %f%f ",&xa,&ya,&xb,&yb,&xc,&yc,&xd,&yd); s1 = areauvw (xa, ya, xb, yb, xd, yd ); s2 = areauvw (xb, yb, xc, yc, xd, yd ); ss=s1+s2; ss=ss/1000; m=ss*950; printf(“m=%10.3f\n”,m); } float liners( float xr,float yr,float xs,float ys ){ return sqrt( (xr-xs)*(xr-xs)+(yr-ys)*(yr-ys) ); float areauvw(float xu,float yu, float xv,float yv, float xw,float yw ){ float uv , uw , vw ; float s ; uv = liners ( xu, yu, xv, yv ); uw = liners ( xu, yu, xw, yw ); vw = liners ( xv, yv, xw, yw ); s=(uv+uw+vw)/2; return sqrt( s*(s-uv)*(s-uw)*(s-vw)
一般形式 例子 TT F ( T ,T ,... ,T ); TT F ( T id ,T id ,... ,T id ); float f ( int, float, int, char ) ; float f ( int z, float u, int v, char w ) ;
函数原型的功能 满足了C标识符先定义后使用的要求 并向编译系统提供所调用函数的信息 函数返回类型 函数的参数个数 函数参数特性等信息 程序设计风格 最好把所有函数原型集中,放在主函数之前
§5.3 程序设计实例 重写打印字符矩阵 重写打印100以内素数 验证哥德巴赫猜想 验证 Pascal 定理 十六进制数翻译
例5.3 重写打印字符方阵 x 打印一行 打印一行 打印前 6 行 for(x=‘A’;x<=‘F’;x++) 打印后5行 例5.3 重写打印字符方阵 x A B C D E F G H I A B C D E F G H I A B C D E F G H I A B B B C D E F G H I A B C D E F G H I A B C C C C C D E F G H I A B C D E F G H I A B C D D D D D D D E F G H I A B C D E F G H I A B C D E E E E E E E E E F G H I A B C D E F G H I A B C D E F F F F F F F F F F F G H I A B C D E F E F G H I A B C D E E E E E E E E E F G H I A B C D E D E F G H I A B C D D D D D D D E F G H I A B C D C D E F G H I A B C C C C C D E F G H I A B C B C D E F G H I A B B B C D E F G H I A B A B C D E F G H I A B C D E F G H I A for(x=‘A’;x<=‘F’;x++) 打印一行 打印前 6 行 打印一行 for(x=‘E’;x>=‘A’;x--) 打印后5行
void writelinex(char); void main( ) { char x ; for ( x=’A’;x<=’F’;x++) writelinex(x); for ( x=’E’;x>=’A’;x--) writelinex(x); }
void writelinex(char u){ // 打印第u行 char y ; int i ; for ( y=u; y<=’I’;y++) printf ( “□%c”, y); for ( y=’A’; y<=u-1;y++) for( i=1;i<=(5-(u-‘A’))*2; i++) printf ( “□”); for( i=1;i<=2*(u-‘A’)+1;i++) printf ( “□%c”, u); for( i=1;i<=(5-(u-‘A’))*2); i++) for ( y=u+1; y<=’I’;y++) printf ( “□ %c”, y); for ( y=’A’; y<=u;y++) printf ( “\n”); } 运行结果演示
for (j=i/2;j>=2;j--) 例5.4 重写打印100以内素数 for (i=2;i<=100;i++) 开始 结束 打印 i i为素数 i为素数? 结束 return false for (j=i/2;j>=2;j--) return true i%j == 0 拿例子讲解整个过程比如6以内的素数
#include “stdio.h” bool prime( int ); void main( ) { int i ; for ( i = 2 ;i <= 100 ; i++ ) if ( prime(i) ) printf(“%5d\n” , i ) ; } bool prime( int n ) { int j ; for ( j = n / 2 ; j >= 2 ; j-- ) if ( n%j == 0 ) return false ; return true ; 运行结果演示
例5.5 验证 Pascal 定理 圆内接六边形三双对边延线的交点在一条直线上 开始 B1 读入六个极角的值 求六个顶点A, B, C, D, E, F 在直角坐标系中的坐标 求三双对边交点B1, B2, B3 坐标 验证B1, B2, B3 是否在一条直线上 开始 结束 A B C D E F B1 B2 B3
已知极角theta,矢径r,求一点的直角坐标(px, py) 将6个点的极坐标 转换成直角坐标 已知极角theta,矢径r,求一点的直角坐标(px, py) trans_abcdef 转换a → xa , ya 转换b → xb , yb 转换d → xd , yd 转换f → xf , yf 转换e → xe , ye 转换c → xc , yc return coordinate px=r*cos(theta); py=r*sin(theta); return
已知四点r, s, t, u 求两条直线交点B 求交点B1,B2,B3的坐标 three_inter intersection 直线方程l1, l2 求l1、l2交点B return three_inter 求AB 、DE交点B1 求BC 、EF交点B2 求CD 、FA交点B3 return
已知两点坐标px, py, qx, qy, 求直线的斜率a和截距b 已知四点r, s, t, u 求两条直线l1,l2的方程 已知两点坐标px, py, qx, qy, 求直线的斜率a和截距b straightline b=(py*qx-qy*px)/(qx-px) return a=(py-qy)/(px-qx) eqution return 求r,s直线方程l1 求t,u直线方程l2
已知两条直线方程斜率分别为ma, na; 截距mb, nb;求直线交点(wx, wy) 验证B1, B2, B3 是否在一条直线上 inter wx=(nb-mb)/(ma-na); wy=ma*px+mb; return test return 求过B1, B2直线方程L 判断B3是否在直线L上
/*PROGRAM Pascal theorem*/ #include "math.h" #include "stdio.h" #define PI 3.1415927 #define eps 1e-5 float radius; /* 圆的半径 */ float theta1, theta2, theta3, theta4, theta5, theta6; / * 六个极角的度数 */ float xa, ya, xb, yb, xc, yc, xd, yd, xe, ye, xf, yf; /* 六个顶点的直角坐标 */ float b1_x, b1_y, b2_x, b2_y, b3_x, b3_y; /* 三个交点的直角坐标 */ float b12_a, b12_b; /*B1和B2构成直线的斜率和截距*/
/*主程序之前这段为“函数原型”以及各个函数返回结果所用变量*/ void trans_abcdef(); float px, py; /* 用来保存coordinate()转换的直角坐标 */ void coordinate (float, float); void three_inter(); void intersection (float, float, float, float, float, float, float, float); float l1_a, l1_b, l2_a, l2_b; /* 两条直线的斜率和截距 */ void equation (float, float, float, float, float, float, float, float); float a, b; /* 直线方程的斜率和截距 */ void straightline(float, float, float, float); float wx, wy; /* 直线交点的直角坐标 */ void inter (float, float, float, float); int test (float, float, float, float, float, float);
/*主函数*/ void main(){ /*读入圆形的半径*/ printf("please input the radius of the circle:"); scanf("%f", &radius); /*读入六个角*/ printf("please input six angle:"); scanf("%f %f %f %f %f %f", &theta1, &theta2, &theta3, &theta4, &theta5, &theta6); trans_abcdef(); /*计算六个定点坐标*/ three_inter(); /*求三个交点*/ if( test(b1_x, b1_y, b2_x, b2_y, b3_x, b3_y) ) /*验证*/ printf("ok"); else{ printf("There is an error when:\n"); printf("theta1=%d theta2=%d\n", theta1, theta2); printf("theta3=%d theta4=%d\n", theta3, theta4); printf("theta5=%d theta6=%d\n", theta5, theta6); }
/*计算六个顶点坐标*/ void trans_abcdef(){ coordinate(radius, theta1); xa=px; ya=py; coordinate(radius, theta2); xb=px; yb=py; coordinate(radius, theta3); xc=px; yc=py; coordinate(radius, theta4); xd=px; yd=py; coordinate(radius, theta5); xe=px; ye=py; coordinate(radius, theta6); xf=px; yf=py; }
/*计算一个顶点坐标*/ void coordinate(float r, float theta) { /* 先把“角度”转换成“弧度”,再转换成直角坐标 */ px=r*cos(PI*theta/180); py=r*sin(PI*theta/180); }
/*求三个交点*/ void three_inter(){ intersection(xa, ya, xb, yb, xd, yd, xe, ye); b1_x=wx; b1_y=wy; intersection(xb, yb, xc, yc, xe, ye, xf, yf); b2_x=wx; b2_y=wy; intersection(xc, yc, xd, yd, xf, yf, xa, ya); b3_x=wx; b3_y=wy; }
/*已知四点,求两条直线交点*/ void intersection(float rx, float ry, float sx, float sy, float tx, float ty, float ux, float uy){ equation (rx, ry, sx, sy, tx, ty, ux, uy); inter (l1_a, l1_b, l2_a, l2_b); }
/*已知四点,求两条直线方程*/ void equation ( float rx, float ry, float sx, float sy, float tx, float ty, float ux, float uy){ straightline(rx, ry, sx, sy); l1_a=a; l1_b=b; straightline(tx, ty, ux, uy); l2_a=a; l2_b=b; }
/*计算由两点确定直线方程的斜率(a)和截距(b)*/ void straightline(float ex, float ey, float fx, float fy){ a=(fy-ey)/(fx-ex); /* 斜率 */ b=ey-a*ex; /* 截距 */ }
/*已知两个直线方程的斜率和截距,求它们交点*/ void inter ( float ma, float mb, float na, float nb){ wx=(nb-mb)/(ma-na); wy=ma*wx+mb; }
/* 检验 */ bool test ( float b1_x, float b1_y, float b2_x, float b2_y, float b3_x, float b3_y) { straightline(b1_x, b1_y, b2_x, b2_y); if(fabs(b3_y-(a*b3_x+b))<eps) return true; else return false; } 运行结果演示
. 例5.6 十六进制数翻译 编程序,从终端读入一个十六进制数,把它翻译成十进制数。 数字 X 十六进制数字字符有: 例5.6 十六进制数翻译 编程序,从终端读入一个十六进制数,把它翻译成十进制数。 数字 X . 十六进制数字字符有: 0 、1 、2 、3 、4 、5 、6 、7 、8 、9 、A 、B 、C 、D 、E 、F
按语法图,十六进制数具有如下三种形式: 第一种形式是最普通的一般形式,它的含义是:
翻译过程可以写成如下PAD 开始 滤掉前导空白 翻译整数部分=> n 处理小数点“.” 翻译小数部分=>f 合并 n 、f 结束
返回 滤掉前导空白 读(ch) ch==’ ‘
综合上述算法PAD 读(ch) ch==’ ‘ 滤掉前导空白 翻译整数部分=> n 处理小数点“.” 翻译小数部分=>f
把整数部分表达式整理一下,可以改写成: 翻译整数部分 n = 0 返回 n = n*16+ch 读(ch) ch==数字
综合上述算法PAD 读(ch) ch==’ ‘ n = 0 n = n*16+ch ch==数字 读(ch) 翻译整数部分=> n 处理小数点“.” 翻译小数部分=>f 合并 n 、f
小数点处理 翻译小数部分=>f ch== '.' 返回 小数点处理
综合上述算法PAD 读(ch) ch==’ ‘ n = 0 n = n*16+ch ch==数字 读(ch) 翻译小数部分=>f 处理小数点“.” 翻译小数部分=>f 合并 n 、f
考虑翻译小数部分:小数部分表示成: 翻译小数部分 f = 0 ; g = 1.0 返回 读(ch) ch=数字 f=f+g*ch g = g/16
综合上述算法PAD 读(ch) ch==’ ‘ n = 0 n = n*16+ch ch==数字 读(ch) f = 0 ; g = 1.0 f=f+g*ch g = g/16 翻译小数部分=>f ch== '.' f = n+f 输出 f 输出 n 合并 n 、f
#define radix 16 void main(){ char ch; float g; // 小数部分“位值” int n; // 保存整数部分 float f; // 保存小数部分,和最后实数 /*滤掉前导空白字符*/ ch=getchar(); while(ch==' ') /*翻译整数部分*/ n=0; while( isdigit(ch) ){ n=n*radix + calculate_char(ch) ; } … … … …
void main(){ … … … … if(ch=='.'){ /*翻译小数部分*/ f=0.0; g=1.0; ch=getchar(); while( isdigit(ch) ){ g=g/radix; f=f+g* calculate_char(ch) ; } /*整数部分与小数部分合并*/ f=f+n; printf(" 该数为实数:%g\n",f ); } else printf(“ 该数为整数:%d\n”,n);
// 计算合法数字字符的数值 int calculate_char(char ch){ if((ch>='0')&&(ch<='9')) return (int)ch-'0'; else if((ch>='A')&&(ch<='F')) return (int)ch-'A'+10; else return (int)ch-'a'+10; } // 判断ch是否十六进制数字的函数 bool isdigit(char ch){ return (ch>='0')&&(ch<='9') ||(ch>='A')&&(ch<='F') ||(ch>='a')&&(ch<='f'); 运行结果演示
解2. 状态矩阵方法 一个十六进制数由以下几部分组成: 1. 前导字符──这部分不是数的成分; 2. 整数部分; 3. 小数点; 4. 小数部分。 解法1程序也分成这几个阶段。若称每个阶段为一个状态,则十六进制数的处理过程分别在如下四个状态下进行: 1. 处理前导字符状态(s0); 2. 处理整数部分状态(s1); 3. 处理小数点状态(s2); 4. 处理小数部分状态(s3)。 为方便起见再加一个结束状态s4 ,
. 数字 X 四个状态 + 一个状态: 1. 处理前导字符状态(s0) 2. 处理整数部分状态(s1) 3. 处理小数点状态(s2) 遇到数字字符,则进行整数部分的翻译拼数,状态不变; 遇到小数点“.”,则转到状态 s2 ,该数为实数; 遇到空白字符,则翻译结束,转到结束状态s4 ,该数为整数; 遇到其它字符,则错误,设为2号错误,转到结束状态s4 。 在 s0 状态下, 遇到空白字符,则越过,状态不变; 遇到数字字符,则进行整数部分的翻译拼数,并转到状态 s1 ; 遇到小数点“.”,则转到状态 s2 ,该数为实数; 遇到其他字符,则错误,设为1号错误,转到结束状态s4 。 在 s3 状态下, 遇到数字字符,则进行小数部分的翻译拼数,状态不变; 遇到小数点“.”,则错误,设为3号错误,转到结束状态s4 。 遇到空白字符,则翻译结束,转到结束状态s4 ,该数为实数。 遇到其它字符,则错误,设为4号错误,转到结束状态s4 。
十六进制数翻译状态矩阵 数字 sort=0 “.” sort=1 空白 sort=2 其它 sort=3 S0 拼整数/s1 /s2 /s0
以 s 保存当前所处的状态: s==0:处理前导空格状态; s==1:处理整数部分状态; s==2:处理小数点状态; s==3:处理小数部分状态; s==4:结束状态。 以 sort 保存当前处理的字符类。 sort==0:当前处理(遇到)字符为数字字符; sort==1:当前处理(遇到)字符为小数点“.” ; sort==2:当前处理(遇到)字符为空白字符; sort==3:当前处理(遇到)字符为其他字符;
十六进制数翻译状态矩阵 数字 sort=0 “.” sort=1 空白 sort=2 其它 sort=3 S0 拼整数/s1 /s2 /s0
s 开始 读(ch) 形成 sort 拼整数; s = 1 初始化: s = 0; n = 0; s = 2 f = 0 ; g = 1; 结束 形成 sort 读(ch) s 1 2 3 错误(3);s = 4 合并整数、小数;s=4 sort 拼小数; s = 3 错误(4);s = 4 s = 2 ; 拼整数; s = 1 错误(1);s = 4 s=4 错误(2);s = 4
#include “stdio.h” #define radix 16 int n ; /* 整数部分 */ float f ; /* 小数部分 */ char ch ; /* 当前读入字符 */ int num ; /* ch 对应的数值 */ float g ; /* 翻译小数部分权值 */ int s , sort ; /* 状态、字符类 */ bool intflag, errflag; // 整数、错误标志 void com_integer(void); /* 翻译整数部分 */ void com_real(void); /* 翻译小数部分 */ int sort_char(char ); /*字符分类、形成 sort */ int calculate_char(char ch);/*计算合法数字字符的数值,与方法1相同,函数体略 */ bool isdigit(char ch); /* 判断ch是否十六进制数字,与方法1相同,函数体略 */ void com_error(int ); /*错误处理*/
void main(){ /* 状态炬阵方法翻译*/ intflag = true ; /* 首先假设翻译的是整数 */ errflag = true ; /* 首先假设翻译正确 */ s = 0; /* 状态初值 */ g = 1.0 ; /* 小数部分权值 */ n = 0; /* 整数单元 */ f = 0; /* 实数单元 */ while ( s<4 ) { ch=getchar(); //读入字符 sort=sort_char(ch); //为字符分类,并计算数字字符对应的数值 → num switch ( s ){ /* 状态操作 */ case 0: … … break ; case 1: … … break ; case 2: … … break ; case 3: … … break ; } /* switch( s ) */ } /* while( s<4 ) */ if ( errflag ) if ( intflag ) printf(“ 该数为整数:%d\n”,n); else printf(" 该数为实数:%g\n",f ); }/* main */
case 0: switch ( sort ){ case 0: com_integer(); /* 遇到数字,翻译整数 */ s = 1 ; break ; case 1: s = 2 ; /* 遇到小数点 */ case 2: break ; /* 前导字符 */ case 3: com_error(1); } case 1: switch ( sort ){ case 0: com_integer(); /* 翻译整数 */ case 1: s = 2; /* 翻译整数时遇到小数点“.”*/ case 2: s = 4; /* 翻译整数时遇到空白字符 */ case 3: com_error(2); /* 翻译整数时遇到其它字符 */ … … … … … …
case 2: switch ( sort ){ case 0: com_real(); /* 翻译小数 */ s = 3 ; break ; case 2: s = 4; /* 翻译整数时遇到空白字符 */ f=f+n; intflag = false ; case 1: com_error(3); /* 存在两个以上小数点 */ break; case 3: com_error(4);/* 翻译小数时遇到非法字符*/ } case 3: switch ( sort ){ case 0: com_real(); /* 翻译小数 */ intflag = false ; f = n+f ; case 3: com_error(4);/* 翻译小数时遇到非法字符 */
void com_integer(void){ /* 翻译整数部分 */ n = n*radix+num; } void com_real(void){ /* 翻译小数部分 */ intflag=false; g = g/radix; f = f+num*g /*字符分类、形成 sort */ int sort_char(char ch){ if( isdigit(ch) ){ //数字字符 num=calculate_char(ch); //数字字符对应数值 → num return 0; }else{ if(ch=='.') //小数点 return 1; else if(ch==' '||ch=='\t'||ch=='\n') return 2; //空白字符 else return 3; //非法字符
运行结果演示 /*错误处理*/ void com_error(int errnum){ errflag = false ; s=4; switch(errnum){ case 1: printf("#error1:数开始符非法!\n"); break; case 2: printf("#error2:整数部分输入非法字符!\n"); case 3: printf("#error3:输入两个或两个以上小数点\n!"); case 4: printf("#error4:小数部分输入非法字符!\n"); } 运行结果演示
本章小结 函数的定义 函数的调用 模块化程序设计思想