Chap 10 函数与程序结构 10.1 圆形体积计算器 10.2 汉诺塔问题 10.3 长度单位转换 10.4 大程序构成.

Slides:



Advertisements
Similar presentations
“八皇后”问题 崔萌萌 吕金华.
Advertisements

第一章 程序设计入门.
第5章 函数与模块化设计 学习目的与要求: 掌握函数的定义及调用方法 理解并掌握参数的传递方法 理解函数的嵌套与递归调用
C语言程序设计 第五章 选择结构程序设计.
C语言程序设计 课程 第5章 数组 主讲:李祥 博士、副教授 单位:软件学院软件工程系.
高级语言程序设计 主讲人:陈玉华.
循环结构又称为重复结构:用来处理需要重复处理的问题,它是程序中一种很重要的结构。
C的發展史 C程式初體驗 C程式設計基本注意事項 上機實習課程
第3章 顺序结构程序设计 本章要点: 格式化输出函数──printf() 格式输入函数——scanf() 字符输出函数——putchar()
第4章 选择结构程序设计 4.1 选择结构和条件判断 4.2 用if语句实现选择结构 4.3关系运算符和关系表达式
Chap 10 函数与程序结构 10.1 函数的组织 10.2 递归函数 10.3 宏定义 10.4 编译预处理.
第4章 选择结构程序设计 4.1 选择结构和条件判断 4.2 用if语句实现选择结构 4.3关系运算符和关系表达式
C程序设计.
If … else 選擇結構 P27.
第五章 选择结构程序设计 一、关系运算符和表达式 1、关系运算符 在程序中经常需要比较两个量的大小关系, 以决定程序下一步
Chap 2 用C语言编写程序 2.1 在屏幕上显示 Hello World! 2.2 求华氏温度 100°F 对应的摄氏温度
Chap 9 结构 9.1 构建手机通讯录 9.2 结构变量 9.3 结构数组 9.4 结构指针.
Introduction to the C Programming Language
STRUCTURE 授課:ANT 日期:2010/5/12.
第七章 函数 目录 有参的加法函数的开发 函数定义的一般形式 函数参数和函数的值 函数的调用
QQ: 李祥 QQ: 欢迎多种方式的学习交流,祝大家学有所成.
计算概论 第十八讲 C语言高级编程 结构与习题课 北京大学信息学院.
Function.
程序设计专题一 结构化程序设计与递归函数 主讲教师: 刘新国.
第八章 函数.
第5章 堆疊(Stacks) 5-1 堆疊的基礎 5-2 堆疊的表示法 5-3 堆疊的應用 - 運算式的計算與轉換
第7章 编译预处理 本章要求: 本章重点: 本章难点: 掌握用#define定义无参数宏和带有参数宏定义和调用方法;
Introduction to the C Programming Language
作弊是否很有诱惑性? 上堂课已经讲了 作业不一定在两个小时里都能完成 答疑没有一个人? 作弊是有记录的 心理系很多同学集体作弊,让人震惊
Chap 3 分支结构 3.1 简单的猜数游戏 3.2 四则运算 3.3 查询自动售货机中商品的价格.
1. 說明一個一維整數陣列passwd,下標範圍0至49 2. 在屏幕顯示 "Enter password"
C语言 程序设计基础与试验 刘新国、2012年秋.
第5讲 结构化程序设计(Part II) 周水庚 2018年10月11日.
第七章 函数及变量存贮类型 7.1 函数基础与C程序结构 7.2 函数的定义和声明 7.3 函数的调用 7.4 函数的嵌套与递归
第4章 顺序程序设计.
授课老师:龚涛 信息科学与技术学院 2016年3月 教材:《Visual C++程序员成长攻略》 《C++ Builder程序员成长攻略》
第九章 预处理命令.
第1章 概述 本章要点: C语言程序结构和特点 C语言程序的基本符号与关键字 C语言程序的编辑及运行 学习方法建议:
C语言概述 第一章.
第1讲 C语言基础 要求: (1) C程序的组成 (2) C语言的标识符是如何定义的。 (3) C语言有哪些基本数据类型?各种基本数
第 二 章 数据类型、运算符与表达式.
1.2 C语言程序的结构与书写规则 一、 C语言程序的总体结构
Introduction to the C Programming Language
C语言程序设计 教案 崔武子制作
Chap 10 函数与程序结构 10.1 函数的组织 10.2 递归函数.
浙江长征职业技术学院—计算机与信息技术系—相方莉制作
Chap 5 函数 5.1 计算圆柱体积 5.2 使用函数编写程序 5.3 变量与函数.
Chap 5 函数 5.1 计算圆柱体积 5.2 数字金字塔 5.3 复数运算.
7.1 C程序的结构 7.2 作用域和作用域规则 7.3 存储属性和生存期 7.4 变量的初始化
第十四章 若干深入问题和C独有的特性 作业: 函数指针 函数作参数 函数副作用 运算 语句 位段 存储类别 编译预处理
C程序设计.
第5章 函 数.
C语言程序设计 李祥 QQ:
資料結構與C++程式設計進階 遞迴(Recursion) 講師:林業峻 CSIE, NTU 6/ 17, 2010.
第3章 数据类型、运算符与表达式.
第2章 数据类型、运算符与表达式 本章要点: 基本数据类型 常量和变量 算术运算符和算术表达式 关系运算符和关系表达式
第2章 基本数据及其运算 本章学习的目标: 1、掌握基本数据的各种表示,基本数据常数的书写方法;
Introduction to the C Programming Language
第三章 基本的輸出與輸入函數 (Basic Output & Input Function)
第3章 最简单的C程序设计 3.1 顺序程序设计举例 3.2 数据的表现形式及其运算 3.3 C语句 3.4 数据的输入输出.
本节内容 指针类型.
第五章 逻辑运算和判断选取控制 §5.1 关系运算符和关系表达式
Introduction to the C Programming Language
第二章 数据类型、运算符和表达式 §2.1 数据与数据类型 §2.2 常量、变量和标准函数 §2.3 基本运算符及其表达式 目 录 上一章
C/C++基礎程式設計班 C語言入門、變數、基本處理與輸入輸出 講師:林業峻 CSIE, NTU 3/7, 2015.
基本資料型態 變數與常數 運算子 基本的資料處理 授課:ANT 日期:2014/03/03.
C/C++基礎程式設計班 陣列 講師:林業峻 CSIE, NTU 3/14, 2015.
第三章 流程控制 程序的运行流程 选择结构语句 循环结构语句 主讲:李祥 时间:2015年10月.
本节内容 指针类型 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
函式庫補充資料 1.
Presentation transcript:

Chap 10 函数与程序结构 10.1 圆形体积计算器 10.2 汉诺塔问题 10.3 长度单位转换 10.4 大程序构成

本章要点 怎样把多个函数组织起来? 怎样用结构化程序设计的思想解决问题? 怎样用函数嵌套求解复杂的问题? 怎样用函数递归解决问题? 如何使用宏?(自学) 如何使用多文件模块构建较大规模程序(自学)

10.1 圆形体积计算器 使用结构化程序设计方法解决复杂的问题 把大问题分解成若干小问题,小问题再进一步分解成若干更小的问题 写程序时,用main()解决整个问题,它调用解决小问题的函数 这些函数又进一步调用解决更小问题的函数,从而形成函数的嵌套调用

程序结构 main( ) 函数1 函数2 …… 函数m 函数1_1 函数1_2 函数m_1 函数m_n

10.1.1 程序解析-计算常用圆形体体积 例10-1 设计一个常用圆形体体积计算器,采用命令方式输入1、2、3,分别选择计算球体、圆柱体、圆锥体的体积,并输入计算所需相应参数。 分析: 输入1、2、3选择计算3种体积,其他输入结束计算 设计一个控制函数cal(),经它辨别圆形体的类型再调用计算球体、圆柱体、圆锥体体积的函数 设计单独的函数计算不同圆形体的体积

程序结构 3层结构,5个函数 降低程序的构思、编写、调试的复杂度 可读性好 main( ) cal ( ) vol_ball ( ) vol_cylind ( ) vol_cone ( ) 3层结构,5个函数 降低程序的构思、编写、调试的复杂度 可读性好

例10-1源程序 #define PI 3.141592654 void cal ( int sel ); int main(void) while( 1 ){ printf(" 1-计算球体体积\n"); printf(" 2-计算圆柱体积\n"); printf(" 3-计算圆锥体积\n"); printf(" 其他-退出程序运行\n"); printf(“ 请输入计算命令:" ); scanf("%d",&sel); if (sel < 1 || sel > 3) break; /* 输入非1~3,循环结束 */ else cal (sel ); /* 输入1~3,调用cal() */ } return 0; 例10-1源程序

{ double vol_ball(void ); double vol_cylind(void ); /* 常用圆形体体积计算器的主控函数 */ void cal ( int sel ) { double vol_ball(void ); double vol_cylind(void ); double vol_cone(void ); switch (sel) { case 1: printf("球体积为:%.2f\n", vol_ball( )); break; case 2: printf("圆柱体积为:%.2f\n", vol_cylind( ) ); case 3: printf("圆锥体积为:%.2f\n", vol_cone( ) ); } /* 计算圆锥体积 V=h/3*PI*r*r */ double vol_cone( ) { double r , h ; printf("请输入圆锥的底圆半径和高:"); scanf("%lf%lf",&r,&h); return(PI*r*r*h/3.0); } /* 计算球体体积 V=4/3*PI*r*r*r */ double vol_ball( ) { double r ; printf("请输入球的半径:"); scanf("%lf",&r); return(4.0/3.0*PI*r*r*r); } /* 计算圆柱体积 V=PI*r*r*h */ double vol_cylind( ) { double r , h ; printf("请输入圆柱的底圆半径和高:"); scanf("%lf%lf",&r,&h); return(PI*r*r*h); }

10.1.2 函数的嵌套调用 顺序调用 int main(void) { …… y = fact(3); …… { …… y = fact(3); …… z = mypow(3.5, 2); …… } double fact(int n) { double mypow(double x, in n) main fact mypow main fact mypow

10.1.2 函数的嵌套调用 嵌套调用 int main(void) { …… cal (sel); …… } { …… cal (sel); …… } void cal (int sel) { …… vol_ball() double vol_ball( ) { main cal vol_ball main cal vol_ball

例10-1 分析 int main(void) { …… cal (sel); } void cal (int sel) { …… { …… cal (sel); } void cal (int sel) { …… vol_ball(); vol_cylind(); vol_cone(); double vol_ball( ) { …… double vol_cylind( ) double vol_cone( ) 例10-1 分析 main( ) cal ( ) vol_ball ( ) vol_cylind ( ) vol_cone ( )

10.1.2 函数的嵌套调用 在一个函数中再调用其它函数的情况称为函数的嵌套调用。 如果函数A调用函数B,函数B再调用函数C,一个调用一个地嵌套下去,构成了函数的嵌套调用。 具有嵌套调用函数的程序,需要分别定义多个不同的函数体,每个函数体完成不同的功能,它们合起来解决复杂的问题。

结构化程序设计方法 自顶向下,逐步求精,函数实现 自顶向下:程序设计时,应先考虑总体步骤,后考虑步骤的细节;先考虑全局目标,后考虑局部目标。先从最上层总目标开始设计,逐步使问题具体化。不要一开始就追求众多的细节。 逐步求精:对于复杂的问题,其中大的操作步骤应该再将其分解为一些子步骤的序列,逐步明晰实现过程。 函数实现:通过逐步求精,把程序要解决的全局目标分解为局部目标,再进一步分解为具体的小目标,把最终的小目标用函数来实现。问题的逐步分解关系,构成了函数间的调用关系。

函数设计时应注意的问题 限制函数的长度。一个函数语句数不宜过多,既便于阅读、理解,也方便程序调试。若函数太长,可以考虑把函数进一步分解实现。 避免函数功能间的重复。对于在多处使用的同一个计算或操作过程,应当将其封装成一个独立的函数,以达到一处定义、多处使用的目的,以避免功能模块间的重复。 减少全局变量的使用。应采用定义局部变量作为函数的临时工作单元,使用参数和返回值作为函数与外部进行数据交换的方式。只有当确实需要多个函数共享的数据时,才定义其为全局变量。

10.2 汉诺塔问题 10.2.1 程序解析 10.2.2 递归函数基本概念 10.2.3 递归程序设计

10.2.1 汉诺(Hanoi)塔问题解析 将64 个盘从座A搬到座B A B C (1) 一次只能搬一个盘子 (3) 大盘不能压在小盘上

分析 A B C

分析 n A B C n-1 n-1 A B C

分析 n A B C A B C

10.2.1 汉诺(Hanoi)塔问题解析 递归方法的两个要点 把汉诺塔的递归解法归纳成三个步骤: (1)递归出口:一个盘子的解决方法; (2)递归式子:如何把搬动64个盘子的问题简化成搬动63个盘子的问题。 把汉诺塔的递归解法归纳成三个步骤: n-1个盘子从座A搬到座C 第n号盘子从座A搬到座B n-1个盘子从座C搬到座B

算法: n-1 A B C hanio(n个盘,A→B, C为过渡) { if (n == 1) 直接把盘子A→B else{ hanio(n-1个盘,A→C, B为过渡) 把第n号盘 A→B hanio(n-1个盘,C→B, A为过渡) }

10.2.2递归函数基本概念 例10-2 用递归函数实现求n! 递推法 递归法 在学习循环时,计算n!采用的就是递推法: 用循环语句实现: result = 1; for(i = 1; i <= n; i++) result = result * i; 递归法 n!= n ×(n-1)! 当n>1 递归式子 = 1 当n=1或n=0 递归出口 即求n!可以在(n-1)!的基础上再乘上n。如果把求n!写成函数fact(n),则fact (n)的实现依赖于fact(n-1)。

10.2.2递归函数基本概念 例10-2 用递归函数求n!。 #include <stdio.h> double fact(int n); int main(void) { int n; scanf ("%d", &n); printf ("%f", fact (n) ); return 0; } double fact(int n) /* 函数定义 */ { double result; if (n==1 || n == 0) /* 递归出口 */ result = 1; else result = n * fact(n-1); return result; 10.2.2递归函数基本概念

10.2.2 递归函数基本概念

例10-2分析 递归出口 递归式 #include <stdio.h> double fact(int n); int main(void) { int n; scanf ("%d", &n); printf ("%f", fact (n) ); return 0; } double fact(int n) { double result; if (n==1 || n == 0) result = 1; else result = n * fact(n-1); return result; 求n! 递归定义 n! = n * (n-1)! (n > 1) n! = 1 (n = 0,1) 递归出口 fact(n)=n*fact(n-1); 递归式

递归函数 fact( n )的实现过程 fact(3)= 3*fact(2)= 3*2=6 2*fact(1)= fact(1)=1 同时有4个函数在运行,且都未完成 fact(3)= 3*fact(2)= 2*fact(1)= fact(1)=1 3*2=6 2*1=2 main() fact(3) fact(2) fact(1) { .... { .... { .... { .... printf(fact(3)) f=3*fact(2) f=2*fact(1) f=1 } return(f) return(f) return(f) } } }

10.2.3 递归程序设计 两个条件缺一不可 解决递归问题的两个着眼点 用递归实现的问题,满足两个条件: 10.2.3 递归程序设计 用递归实现的问题,满足两个条件: 问题可以逐步简化成自身较简单的形式(递归式) n! = n * (n-1)! n n-1 Σi = n +Σ i i=1 i=1 递归最终能结束(递归出口) 两个条件缺一不可 解决递归问题的两个着眼点

10.2.3 递归程序设计 例10-3 编写递归函数reverse(int n)实现将整数n逆序输出。 分析: 10.2.3 递归程序设计 例10-3 编写递归函数reverse(int n)实现将整数n逆序输出。 分析: 将整数n逆序输出可以用循环实现,且循环次数与n的位数有关。递归实现整数逆序输出也需要用位数作为控制点。归纳递归实现的两个关键点如下: 递归出口:直接输出n,如果n<=9,即n为1位数 递归式子:输出个位数n%10,再递归调用reverse(n/10) 输出前n-1位,如果n为多位数

10.2.3 递归程序设计 由于结果是在屏幕上输出,因此函数返回类型为void void reverse(int num) { 10.2.3 递归程序设计 由于结果是在屏幕上输出,因此函数返回类型为void void reverse(int num) { if(num<=9) printf("%d",num); /* 递归出口 */ else{ printf("%d",num%10); reverse(num/10); /* 递归调用 */ }

例10-4 汉诺(Hanoi)塔问题 A B C hanio(n个盘,A→B,C为过渡) { if (n == 1) 直接把盘子A→B else{ hanio(n-1个盘,A→C,B为过渡) 把n号盘 A→B hanio(n-1个盘,C→B,A为过渡) }

源程序 /* 搬动n个盘,从a到b,c为中间过渡 */ void hanio(int n, char a, char b, char c) { if (n == 1) printf("%c-->%c\n", a, b); else{ hanio(n-1, a, c, b); hanio(n-1, c, b, a); } int main(void) { int n; printf("input the number of disk: " ); scanf("%d", &n); printf("the steps for %d disk are:\n",n); hanio(n, 'a', ‘b', ‘c') ; return 0; input the number of disk: 3 the steps for 3 disk are: a-->b a-->c b-->c c-->a c-->b

input the number of disk: 3 the steps for 3 disk are: a-->b a-->c b-->c c-->a c-->b A B C

课堂练习:利用递归函数计算x的n次幂 int mi(int x, int n) { if (n==1) return x; else return x*mi(x,n-1); }

10.3 长度单位转换 10.3.1 程序解析 10.3.2 宏基本定义 10.3.3 带参数的宏定义 10.3.4 文件包含 10.3.1 程序解析 10.3.2 宏基本定义 10.3.3 带参数的宏定义 10.3.4 文件包含 10.3.5 编译预处理

Input mile,foot and inch:1.2 3 5.1 1.200000 miles=1930.800077 meters 3.000000 feet=91.440000 centimeters 5.100000 inches=12.954000 centimeters 10.3.1 程序解析 例10-5 欧美国家长度使用英制单位,1英里=1609米,1英尺=30.48厘米,1英寸=2.54厘米。请编写程序转换。 #include<stdio.h> #define Mile_to_meter 1609 /* 1英里=1609米 */ #define Foot_to_centimeter 30.48 /* 1英尺=30.48厘米 */ #define Inch_to_centimeter 2.54 /* 1英寸=2.54厘米 */ int main(void) { float foot, inch, mile; /* 定义英里,英尺,英寸变量 */ printf("Input mile,foot and inch:"); scanf("%f%f%f", &mile, &foot, &inch); printf("%f miles=%f meters\n", mile, mile * Mile_to_meter); /* 计算英里的米数 */ printf("%f feet=%f centimeters\n", foot, foot * Foot_to_centimeter); /* 计算英尺的厘米数 */ printf("%f inches=%f centimeters\n", inch, inch * Inch_to_centimeter); /* 计算英寸的厘米数 */ return 0; }

10.3.2 宏基本定义 #define 宏名标识符 宏定义字符串 说明: 多用于符号常量 编译时,把程序中所有与宏名相同的字符串,用宏定义字符串替代 #define PI 3.14 #define arr_size 4 说明: 宏名一般用大写字母,以与变量名区别 宏定义不是C语句,后面不得跟分号 宏定义可以嵌套使用 #define S 2*PI*PI 多用于符号常量

10.3.2 宏基本定义 宏定义可以写在程序中任何位置,它的作用范围从定义书写处到文件尾。 可以通过“#undef”强制指定宏的结束范围。

宏的作用范围 #define A “This is the first macro” void f1() { printf( “A\n” ); } #define B “This is the second macro” A 的有效范围 void f2( ) printf( B ) ; B 的有效范围 #undef B int main(void) f1( ); f2( ); return 0;

10.3.3 带参数的宏定义 例10-6 简单的带参数的宏定义。 #include <stdio.h> 10.3.3 带参数的宏定义 例10-6 简单的带参数的宏定义。 #include <stdio.h> #define MAX(a, b) a > b ? a: b #define SQR(x) x * x int main(void) { int x , y; scanf (“%d%d” , &x, &y) ; x = MAX (x, y); /* 引用宏定义 */ y = SQR(x); /* 引用宏定义 */ printf(“%d %d\n” , x, y) ; return 0; }

10.3.3 带参数的宏定义 各位数字的立方和等于它本身的数。例如153的各位数字的立方和是13+53+33=153 10.3.3 带参数的宏定义 #define f(a) (a)*(a)*(a) 例: #define f(a) a*a*a int main(void) /* 水仙花数 */ { int i,x,y,z; for (i=1; i<1000; i++) { x=i%10; y=i/10%10; z=i/100 ; if (x*x*x+y*y*y+z*z*z==i) printf(“%d\n” ,i); } return 0; (f(x)+f(y)+f(z)==i) f(x+y) = (x+y)3 ? = x+y*x+y*x+y

示例 用宏实现两个变量值的交换 #define f(a,b,t) t=a; a=b; b=t; int main( ) { int x,y,t ; scanf(“%d%d” ,&x, &y); f(x,y,t) printf(“%d %d\n”, x, y) ; return 0; } 编译时被替换 t=x ; x=y ; y=t ; 与函数的区别在哪里? 带参数的宏定义不是函数,宏与函数是两种不同的概念 宏可以实现简单的函数功能

宏定义应用示例 定义宏LOWCASE,判断字符c是否为小写字母。 定义宏CTOD将数字字符(‘0’~‘9’)转换为相应的 #define LOWCASE(c) (((c) >= 'a') && ((c) <= 'z') ) 定义宏CTOD将数字字符(‘0’~‘9’)转换为相应的 十进制整数,-1表示出错。 #define CTOD(c) (((c) >= '0') && ((c) <= '9') ? c - '0' : -1)

练习——带宏定义的程序输出 #define F(x) x - 2 #define D(x) x*F(x) int main() { printf("%d,%d", D(3), D(D(3))) ; return 0; }

结果分析 阅读带宏定义的程序,先全部替换好,最后再统一计算 不可一边替换一边计算,更不可以人为添加括号 D(3) = x*F(x) 先用x替换展开 = x*x-2 进一步对F(x)展开,这里不能加括号 = 3*3-2 = 7 最后把x=3代进去计算 D(D(3)) = D(x*x-2) 先对D(3)用x替换展开, = x*x-2* F(x*x-2) 拿展开后的参数对D进一步进行宏替换 = x*x-2* x*x-2-2 拿展开后的参数对F进一步进行宏替换 = 3*3-2*3*3-2-2 = -13 最后把x=3代进去计算 运行结果:7 -13

10.3.4 文件包含 系统文件以stdio.h、math.h等形式供编程者调用 实用系统往往有自己诸多的宏定义,也以.h的形式组织、调用 文件包含 include

文件包含 格式 作用 注意 # include <需包含的文件名> # include “需包含的文件名” 编译预处理命令,以#开头。 在程序编译时起作用,不是真正的C语句,行尾没有分号。 系统文件夹 当前文件夹+系统文件夹

例10-7 将例10-5中长度转换的宏,定义成头文件length.h,并写出主函数文件。 #define Mile_to_meter 1609 /* 1英里=1609米 */ #define Foot_to_centimeter 30.48 /* 1英尺=30.48厘米 */ #define Inch_to_centimeter 2.54 /* 1英寸=2.54厘米 */ 主函数文件prog.c源程序 #include<stdio.h> #include “length.h” /* 包含自定义头文件 */ int main(void) { float foot, inch, mile; /* 定义英里,英尺,英寸变量 */ printf("Input mile,foot and inch:"); scanf("%f%f%f", &mile, &foot, &inch); printf("%f miles=%f meters\n", mile, mile * Mile_to_meter); printf("%f feet=%f centimeters\n", foot, foot * Foot_to_centimeter); printf("%f inches=%f centimeters\n", inch, inch * Inch_to_centimeter); return 0; }

将例10-1的5个函数分别存储在2个.C文件上,要求通过文件包含把它们联结起来。 头文件length.h #define Mile_to_meter 1609 #define Foot_to_centimeter 30.48 #define Inch_to_centimeter 2.54 编译连接后生成的程序   … stdio.h的内容 #define Mile_to_meter 1609 #define Foot_to_centimeter 30.48 #define Inch_to_ centimeter 2.54 int main(void) { float mile,foot,inch; …… return 0; } 主函数文件prog.c #include<stdio.h> #include “length.h” int main(void) { float mile,foot,inch; …… return 0; }

常用标准头文件 ctype.h 字符处理 math.h 与数学处理函数有关的说明与定义 stdio.h 输入输出函数中使用的有关说明和定义 string.h 字符串函数的有关说明和定义 stddef.h 定义某些常用内容 stdlib.h 杂项说明 time.h 支持系统时间函数

10.3.5 编译预处理 编译预处理是C语言编译程序的组成部分,它用于解释处理C语言源程序中的各种预处理指令。 10.3.5 编译预处理 编译预处理是C语言编译程序的组成部分,它用于解释处理C语言源程序中的各种预处理指令。 文件包含(#include)和宏定义(#define)都是编译预处理指令 在形式上都以“#”开头,不属于C语言中真正的语句 增强了C语言的编程功能,改进C语言程序设计环境,提高编程效率

编译预处理 C程序的编译处理,目的是把每一条C语句用若干条机器指令来实现,生成目标程序。 由于#define等编译预处理指令不是C语句,不能被编译程序翻译,需要在真正编译之前作一个预处理,解释完成编译预处理指令,从而把预处理指令转换成相应的C程序段,最终成为由纯粹C语句构成的程序,经编译最后得到目标代码。

编译预处理功能 编译预处理的主要功能: 文件包含(#include) 宏定义(#define) 条件编译

编译预处理功能 条件编译 #define FLAG 1 #if FLAG 程序段1 #else 程序段2 #endif

10.4 大程序构成 ——多文件模块的学生信息库系统 10.4 大程序构成 ——多文件模块的学生信息库系统 10.4.1 分模块设计学生信息库系统 10.4.2 C程序文件模块 10.4.3 文件模块间的通信

10.4.1 分模块设计学生信息库系统 例10-8 请综合例9-1、例9-2、例9-3和例9-4,分模块设计一个学生信息库系统。该系统包含学生基本信息的建立和输出、计算学生平均成绩、按照学生的平均成绩排序以及查询、修改学生的成绩等功能。 函数建立为:

10.4.1 分模块设计学生信息库系统 由于整个程序规模较大,按照功能图,分成三个程序文件模块,并把结构体定义等写成一个头文件。 10.4.1 分模块设计学生信息库系统 由于整个程序规模较大,按照功能图,分成三个程序文件模块,并把结构体定义等写成一个头文件。 头文件student.h 输入输出程序文件input_output.c void new_student (struct student students[ ]) void output_student(struct student students[ ]) 计算平均成绩与平均成绩排序程序文件aver_sort.c void average(struct student students[ ]) void sort(struct student students[ ]) 查询修改程序文件modify.c void modify(struct student students[ ]) void search_student(struct student students[ ], int num)

10.4.1 分模块设计学生信息库系统 一共定义了三个.c程序文件和一个.h头文件,它们各自独立,再通过主函数main()调用。主函数放在student_system.c文件中,各文件存放在同一个文件夹下,相互间的连接采用文件包含的形式。 主函数程序文件student_system.c #include “student.h” #include “input _output.c” #include “aver_sort.c” #include “modify.c” int Count = 0; /* 全局变量,记录当前学生总数 */ int main(void) { ......... } /* 主函数调用各函数 */

10.4.2 C程序文件模块 我们把保存有一部分程序的文件称为程序文件模块 结构化程序设计是编写出具有良好结构程序的有效方法 一个大程序最好由一组小函数构成 如果程序规模很大,需要几个人合作完成的话,每个人所编写的程序会保存在自己的.c文件中 为了避免一个文件过长,也会把程序分别保存为几个文件。 一个大程序会由几个文件组成,每一个文件又可能包含若干个函数。

10.4.2 C程序文件模块 一个大程序可由几个程序文件模块组成,每一个程序文件模块又可能包含若干个函数。程序文件模块只是函数书写的载体。 当大程序分成若干文件模块后,可以对各文件模块分别编译,然后通过连接,把编译好的文件模块再合起来,连接生成可执行程序。 问题:如何把若干程序文件模块连接成一个完整的可执行程序? 文件包含 工程文件(由具体语言系统提供)

10.4.2 C程序文件模块 程序-文件-函数关系 小程序:主函数+若干函数  一个文件 大程序:若干程序文件模块(多个文件)  每个程序文件模块可包含若干个函数  各程序文件模块分别编译,再连接 整个程序只允许有一个main()函数

10.4.3 文件模块间的通信 文件模块与变量 外部变量 静态全局变量 文件模块与函数 外部函数 静态的函数

10.4.3 文件模块间的通信 外部变量 全局变量只能在某个模块中定义一次,如果其他模块要使用该全局变量,需要通过外部变量的声明 10.4.3 文件模块间的通信 外部变量 全局变量只能在某个模块中定义一次,如果其他模块要使用该全局变量,需要通过外部变量的声明 外部变量声明格式为: extern 变量名表; 如果在每一个文件模块中都定义一次全局变量,模块单独编译时不会发生错误,一旦把各模块连接在一起时,就会产生对同一个全局变量名多次定义的错误 反之,不经声明而直接使用全局变量,程序编译时会出现“变量未定义”的错误。

10.4.3 文件模块间的通信 静态全局变量 当一个大的程序由多人合作完成时,每个程序员可能都会定义一些自己使用的全局变量 10.4.3 文件模块间的通信 静态全局变量 当一个大的程序由多人合作完成时,每个程序员可能都会定义一些自己使用的全局变量 为避免自己定义的全局变量影响其他人编写的模块,即所谓的全局变量副作用,静态全局变量可以把变量的作用范围仅局限于当前的文件模块中 即使其他文件模块使用外部变量声明,也不能使用该变量。

10.4.3 文件模块间的通信 文件模块与函数 外部函数 静态的函数 10.4.3 文件模块间的通信 文件模块与函数 外部函数 如果要实现在一个模块中调用另一模块中的函数时,就需要对函数进行外部声明。声明格式为: extern 函数类型 函数名(参数表说明); 静态的函数 把函数的使用范围限制在文件模块内,不使某程序员编写的自用函数影响其他程序员的程序,即使其他文件模块有同名的函数定义,相互间也没有任何关联, 增加模块的独立性。

本章小结 多函数程序的组织结构 递归函数 编译预处理 构成要素:递归式子(重点)与递归出口 运用递归函数解决特殊问题(如汉诺塔) 文件包含 函数调用的层次结构 多文件模块实现:文件包含 合理运用变量在多文件模块、多函数间的关联 程序文件模块:变量与文件模块、 函数与文件模块的关系 递归函数 构成要素:递归式子(重点)与递归出口 运用递归函数解决特殊问题(如汉诺塔) 编译预处理 文件包含 宏实质:编译预处理的替代 带参的宏——不是函数