模块化程序设计-函数 重写例1.1—模块化程序设计 函数 程序设计实例 作业:

Slides:



Advertisements
Similar presentations
北大附中深圳南山分校 倪 杰 2016年8月25日星期四 2016年8月25日星期四 2016年8月25日星期四 Ox y 1 1 y=a x (a>1)
Advertisements

While 迴圈 - 不知重複執行次數
CSIM, PU C Language Introduction to the C Programming Language 重覆敘述 (for,while,break,continue) 適合重複性的計算或判斷.
第六章 其他税收法律制度.
软件工程 周志钊
“八皇后”问题 崔萌萌 吕金华.
销售部工作总结 二O一六年五月.
第五章 定积分及其应用.
第6章 PLC控制系统设计与应用 教学目的与要求:熟悉相关指令的综合应用,掌握PLC控制系统设计方法,掌握PLC程序编制方法,巩固所学内容。
第4章 选择结构程序设计 在现实生活中,需要进行判断和选择的情况是很多的 如果你在家,我去拜访你 如果考试不及格,要补考
第七章 财务报告 主讲老师:王琼 上周知识回顾.
C语言程序设计 第十二章 位运算.
C语言程序设计 第五章 选择结构程序设计.
高级语言程序设计 主讲人:陈玉华.
第2章 分支结构 P if语句 2.2 switch语句 2.3 用条件运算符转换大小写字母 2.4 程序举例
循环结构又称为重复结构:用来处理需要重复处理的问题,它是程序中一种很重要的结构。
第3章 顺序结构程序设计 本章要点: 格式化输出函数──printf() 格式输入函数——scanf() 字符输出函数——putchar()
C++程序设计 第二讲 清华大学软件学院.
適用於多選一 可減少if 與 else配對混淆的錯誤.
Chap 10 函数与程序结构 10.1 函数的组织 10.2 递归函数 10.3 宏定义 10.4 编译预处理.
C程序设计.
If … else 選擇結構 P27.
第五章 选择结构程序设计 一、关系运算符和表达式 1、关系运算符 在程序中经常需要比较两个量的大小关系, 以决定程序下一步
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
程式撰寫流程.
3.1.3几种常见函数的导数 高二数学 选修1-1.
Introduction to the C Programming Language
第二章 C++对C 在非面向对象方面的改进 更简洁,更安全.
高级语言程序设计 张长海 软件自动化研究室 Tel:
上节回顾 1 图像的辐射纠正 2 数字图像的增强技术 3 软件实习 4 作业布置.
C语言 程序设计基础与试验 刘新国、2012年秋.
期中考试成绩分布 《程序设计》-2017年秋.
第1章 静力学基础 几个重要名词 静力学:研究力的基本性质和力系的合成以及物体在力系作用下平衡规律及其应用。
計數式重複敘述 for 迴圈 P
第六章 空 间 力 系.
第3章 顺序结构程序设计 为了让计算机处理各种数据,首先就应该把源数据输入到计算机中;计算机处理结束后,再将目标数据信息以人能够识别的方式输出。C语言中的输入输出操作,是由C语言编译系统提供的库函数来实现。 3.1 格式化输出——printf()函数 3.2 格式化输入——scanf()函数.
2.1 C语言的数据类型 2.2 常量与变量 2.3 变量赋初值 2.4 各类数值型数据间的混合运算 2.5 C语言的运算符和表达式
第5讲 结构化程序设计(Part II) 周水庚 2018年10月11日.
第七章 函数及变量存贮类型 7.1 函数基础与C程序结构 7.2 函数的定义和声明 7.3 函数的调用 7.4 函数的嵌套与递归
第4章 顺序程序设计.
第四章 组合逻辑电路 4.1 组合逻辑电路的分析与设计 4.2 常用组合逻辑电路 4.3 组合逻辑电路的竞争与冒险.
汽车机械基础-- 第一篇 汽车常用构件力学分析.
第5章 函数式程序设计语言 过程式程序设计语言由于数据的名值分离, 变量的时空特性导致程序难于查错、难于修改
C语言概述 第一章.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
《数字电子技术基础》(第五版)教学课件 清华大学 阎石 王红
程式結構&語法.
第 二 章 数据类型、运算符与表达式.
C语言程序设计 教案 崔武子制作
浙江长征职业技术学院—计算机与信息技术系—相方莉制作
Chap 5 函数 5.1 计算圆柱体积 5.2 使用函数编写程序 5.3 变量与函数.
程式的時間與空間 Time and Space in Programming
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
C程序设计.
C语言程序设计 李祥 QQ:
C++语言程序设计教程 第2章 数据类型与表达式 第2章 数据类型与表达式 制作人:杨进才 沈显君.
資料結構與C++程式設計進階 遞迴(Recursion) 講師:林業峻 CSIE, NTU 6/ 17, 2010.
第三章 程序的控制结构 第一节 概述 第二节 if选择结构 第三节 switch语句.
第2章 数据类型、运算符与表达式 本章要点: 基本数据类型 常量和变量 算术运算符和算术表达式 关系运算符和关系表达式
第7章 程序的结构 四、生存期与存储属性 五、extern关键字与外部连接属性 六、static关键字与内部连接属性.
Introduction to the C Programming Language
第4章 顺序结构程序设计 为了让计算机处理各种数据,首先就应该把源数据输入到计算机中;计算机处理结束后,再将目标数据信息以人能够识别的方式输出。C语言中的输入输出操作,是由C语言编译系统提供的库函数来实现。 4.1 格式化输出——printf()函数 4.2 格式化输入——scanf()函数.
第五章 逻辑运算和判断选取控制 §5.1 关系运算符和关系表达式
第1章 数据结构基础概论 本章主要介绍以下内容 数据结构研究的主要内容 数据结构中涉及的基本概念 算法的概念、描述方法以及评价标准.
第二章 数据类型、运算符和表达式 §2.1 数据与数据类型 §2.2 常量、变量和标准函数 §2.3 基本运算符及其表达式 目 录 上一章
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
Chap 10 函数与程序结构 10.1 圆形体积计算器 10.2 汉诺塔问题 10.3 长度单位转换 10.4 大程序构成.
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
第二章 Java基础语法 北京传智播客教育
Presentation transcript:

模块化程序设计-函数 重写例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"); } 运行结果演示

本章小结 函数的定义 函数的调用 模块化程序设计思想