C语言程序设计教程第6章.ppt

上传人:max****ui 文档编号:6333712 上传时间:2020-02-23 格式:PPT 页数:89 大小:485KB
返回 下载 相关 举报
C语言程序设计教程第6章.ppt_第1页
第1页 / 共89页
C语言程序设计教程第6章.ppt_第2页
第2页 / 共89页
C语言程序设计教程第6章.ppt_第3页
第3页 / 共89页
点击查看更多>>
资源描述
第六章函数与编译预处理 6 1模块化程序设计与函数6 2函数的定义与调用6 3函数的递归调用6 4变量的作用域与存取方式6 5编译预处理 C语言程序设计教程 2 6 1模块化程序设计与函数 在设计较复杂的程序时 我们一般采用的方法是 把问题分成几个部分 每部分又可分成更细的若干小部分 逐步细化 直至分解成很容易求解的小问题 这样的话 原来问题的解就可以用这些小问题来表示 2020 2 23 3 基本概念 4 模块与函数 C语言程序由基本语句和函数组成 每个函数可完成相对独立的任务 依一定的规则调用这些函数 就组成了解决某个特定问题的程序 5 模块与函数 把大任务分解成若干功能模块 用多个函数来实现这些功能模块 通过函数的调用来实现完成大任务的全部功能 6 模块与函数 任务 模块与函数的关系 一个大任务分成多个功能模块 功能模块则由一个或多函数实现 模块化的程序设计是靠设计函数和调用函数实现的 7 例 分数排序 任务 输入三个分数 从大到小的顺序的输出 如果大于等于85 在该数后面输出 A 小于85且大于等于70 则输出 B 小于70且大于等于60 输出 C 如果小于60 则输出 D 思路 scanf 输入分数另建一个排序函数判断并输出等级函数打印分数及等级的函数 虽然也可以由一个主函数来完成 但这样做可读性及操作性会更好 8 voidmain floata b c scanf f f f 输出a b c三个数 9 voidsortabc a b c floata b c floatt if a b t a a b b t 交换a 和b的值 if b c t b b c c t 交换b 和c的值 if a b t a a b b t 交换a 和b的值 10 chargrade x 根据x的值 得到等级标准 floatx if x 85 return A elseif x 70 return B elseif x 60 return C elsereturn D 11 voidputabc a b c floata b c charg g grade a 判别等级 printf 6 1f c a g g grade b printf 6 1f c b g g grade c printf 6 1f c c g 12 模块设计的原则 模块独立 规模适当 层次分明 功能专一 13 独立性原则表现在模块完成独立的功能 和其它模块间的关系简单 各模块可以单独调试 修改某一模块 不会造成整个程序的混乱 每个模块完成一个相对独立的特定子功能 在对任务逐步分解时 要注意对问题的综合 例如 一些模块的相似的子任务 可以把它们综合起来考虑 找出它们的共性 把它们做成一个完成特定任务的单独模块 每个模块有特定功能 模块独立 14 15 16 模块不能太大 但也不能太小 模块的功能复杂 可读性就不好 而且也违背独立性原则 但如果做得太小 实际上也会复杂各个模块间反复调用 可读性也会降低 这点需要慢慢积累经验 好好把握 模块规模适当 17 分解模块要注意层次 要多层次的分解任务 要注意对问题进行抽象化 开始不要过于注意细节 注意做到逐步细化求精 18 算法简介 算法简介 算法简介 什么是算法 通俗地说 算法是解决一类特定问题的方法和步骤 算法是一个有限操作的序列 算法的每一步都是确定的 算法的每一步计算机都能操作 有一个或多个的输入或输出 19 算法的描述 算法描述的任务是将解题步骤和方法用一定的形式表示出来 要清楚 准确 严谨 还要可读性好 方便实现 算法两大要素 一是操作 用类计算机语句或自然语言描述 二是控制结构 描述算法一般可以用流程图描述 20 例6 2设计算法 找出a b两数中的较大者 并输出 分析 这个问题分三个步骤 输入两个数 找出其中的大数 输出大数 2020 2 23 21 开始 输入a b a b 交换a b 输出a 结束 非0 0 图6 3找出a b两数中的较大者算法流程图 22 6 2函数的定义与调用 在C语言中 函数 Function 是一个处理过程 可以进行数值运算 信息处理 控制决策 即一段程序的工作放在函数中进行 函数结束时可以携带或不带处理结果 库函数 标准函数 系统提供自定义函数 用户自己写 23 C语言程序处理过程全部都是以函数形式出现 最简单的程序至少也有一个main函数 函数必须先定义和声明后才能调用 24 标准库函数 C语言有丰富的库函数 这些函数的说明在不同的头文件 h 中 想要调用标准的库函数 就必须include includemain printf d 1024 768 25 自定义函数 可以把完成一个任务的过程写成函数 intA to a intcapital intsmall if capital A 26 函数 的主要知识点 函数的定义函数的参数和返回值函数的调用嵌套和递归变量的作用域 27 函数举例 includemain inta b m 说明变量 intmax inta intb 函数声明 scanf d d 调用库函数getch 28 函数举例 intmax inta intb 定义函数max inty y a b a b 条件表达式 returny 29 自定义函数的声明 自定义函数在调用前应先声明 使系统知道将要用到某个函数及它的类型 以便处理 函数声明应与该函数定义时给出的函数类型与名字 形参的个数 类型 次序相一致 30 求1 2 3 10 算法i 1 s 0 当i 10s s i 定义求i 的函数 31 求1 2 3 10 程序 voidmain longmm int 自定义求阶乘函数应先声明 inti longs 0 for i 1 i 10 i s mm i 调用求阶乘函数 求i的阶乘 printf n ld s 32 定义求n 的函数 longmm intn longt 1 inti for i 1 i n i t i returnt 33 函数的参数 intmax inta intb inty y a b a b returny 调用时 m max 3 6 m max a b 34 形式参数与实际参数的关系 形式参数在函数中是变量名 在函数调用时 形参被分配相应的内存实际参数是表达式负责向对应的形参标识的内存单元传递数据实参与形参必须个数相同对应的形参和实参的类型必须一致实参给形参传递值得时候 按顺序传递 与形参名无关 所以实参与形参可以不同名 35 实参与形参 例 主调函数中有如下语句 scanf d d 如果输入6 2函数intmax inta intb 形参a得到第一个实际参数a的值6形参b得到第二个实际参数b 3的值5 36 函数返回值 函数返回值通过return语句获得函数返回值的类型就是函数的类型returny 将变量y的值返回给调用者returny 3 将表达式的值返回给调用者 37 return的数据类型与函数的类型矛盾时 自动将数据转换成函数的类型 intfunct1 charch while ch getch z returnch 调用 i funct1 返回的是int类型 38 函数没有返回值 函数定义成空类型 voidputline inti for i 0 i 35 i printf printf n 函数的功能就是输出35个 调用 putline 应该的语句形式i putline 是错的 39 调用函数 a function x y 或者function x y 取返回值 只是操作 解决更复杂问题时可以嵌套调用 longfac intk longf 1 inti for i 1 i k i f f i returnf longcombination intn intm longc inti c fac m fac n fac m n returnc 主函数 main intn m longc scanf d d 理论上可以a b d e x c f 般嵌套无数层 40 6 3函数的递归调用 函数调用它本身 称为递归 直接在函数内调用自己为直接递归 通过别的函数调用自己为间接递归 voida a voida b voidb a 递归在解决某些问题中 是一个十分有用的方法 因为其一 有的问题它本身就是递归定义的 其二 它可以使某些看起来不易解决的问题变得容易解决 写出的程序较简短 41 递归方法求n 由于n n n 1 是递归定义所以求n n 1 n 1 n 2 n 2 n 3 0 的问题 根据公式有0 1 再反过来依次求出1 2 直到最后求出n 42 递归方法求n longfac intn longf if n 0 f 1 elsef n fac n 1 returnf main longy intn scanf d 刚开始的时候 这个n是前面输入的需要阶乘的n 所以在这里带入的值是n 而这个函数里又调用了本身 不过参数已经变成了n 1 所以这里再次调用时参数已经变成了n 1注意 上次调用fac n 还没有完 只是由于遇到了fac n 1 而执行fac n 1 去了 而在调用fac n 1 时同样遇到了要调用fac n 2 的问题 于是一层一层的包裹下去 每次调用的时候都会在内部调用一个结构相同但变量不同的函数 直到 直到调用到fac 0 时 由于内部if判断 已经不需要再继续调用另一个fac n 1 而直接有了f 1 fac 0 已经执行完毕 它的返回值被fac 1 中的f n fac n 1 语句赋给了f值 同时返回了f 而这个返回的f又被fac 2 乘上当前的n值以后继续返回f 直到最后的fac n 都做完了 f的值被返回到了它的调用点 主函数中 这样就是一个递归运算 43 斐波那契 1170年生于意大利的比萨 在北非的布吉亚 即今阿尔及利亚的贝加亚长大并且接受教育 大约在1200年 他才重新回到比萨 斐波那契无疑在启蒙教育时期就受到过阿拉伯数学家的影响或者接受过他们的辅导 他写过大量的数学论文 取得了一些重大的数学发现 这使他的著作在意大利非常流行 并且引发了当时罗马帝国皇帝弗雷德里克二世的注意 他曾邀请斐波那契到他在比萨宫廷觐见 斐波那契于1250年去世 递归举例 斐波那契数列 44 1202年 他在所著的 算珠原理 中 提出了一个著名而有趣的兔子问题 假定一对小兔子经过一个月后能够长成一对大兔子 而一对大兔子经过一个月后能够生了一对小兔子 现在我们从一对小兔子开始 用表示第个月兔子的总对数 显然 第1个月只有一对小兔子 第2个月只有一对大兔子 第3个月一对大兔子生出一对小兔子 总共两对兔子 于是我们得到一个数列 1 1 2 3 5 8 13 仔细观察这个数列 从第3项起每一项都是它前相邻两项的和 这就是著名的斐波那契数列 45 问题 第1个月有1对兔子过2个月 兔子就可每个月生1对兔子问第n个月有多少对兔子 分析 设第n个月有f n 对兔子根据题意有f 0 0 f 1 1f n f n 1 f n 2 f n 1 前一个月的兔子数f n 2 前2个月的兔子数 46 定义函数f n longf intn switch n case0 return0 break case1 return1 break default returnf n 1 f n 2 调用函数f n 47 兔子问题主函数 voidmain longf intn 自定义函数声明 intn printf ninputn scanf d 调用函数f n 48 斐波那契数列有一系列奇妙的性质 现简列以下几条 供大家欣赏 1 从首项开始 我们依次计算每一项与它的后一项的比值 并精确到小数是第四位 如果将这一工作不断地继续下去 这个比值将无限趋近于某一个常数 这个常数位于1 6180与1 6181之间 它能准确地用黄金数表示出黄金分割率 2 在自然界中 斐波那契数列也常见到 比如 向日葵花冠上的螺旋 如果前一道螺旋直径为21的话 下一道螺旋直径则为34 依次形成连续的斐波那契数字 松果的外弧为顺时针或逆时针方向的螺旋 其相同间隔之间螺旋的直径也能构成斐波那契数列 在上等的鹦鹉螺身上 每圈罗纹的直径与相邻罗纹直径之比亦是1 1 618 菠萝是又一种可以检验斐波那契数的植物 菠萝表皮方块形鳞苞形成两组旋向相反的螺线 它们的条数必须是这个级数中紧邻的两个数字 如左旋8行 右旋13行 49 3 有一种两人游戏 名叫 尼姆 游戏方法是由两个人轮流取一堆粒数不限的砂子 先取的一方可以取任意粒 但不能把这堆砂子全部取走 后取的一方 取数也多少不拘 但最多不能超过对方所取砂子数的一倍 然后又轮到先取的一方来取 但也不能超过对方最后一次所取砂子的一倍 这样交替地进行下去 直到全部砂子被取光为止 谁能拿到最后一粒砂子 谁就算胜利者 在这个游戏中 若所有砂子的粒数是个斐波那契数的话 那么后取的一方稳操胜券 但所有的砂子不是一个斐波那契数的话 那么先取的一方稳胜 50 辗转相除法求最大公约数 求m和n的公约数算法if m n 0n是公约数 else求n和m n的公约数 51 用欧几里德算法 辗转相除法 求两个数的最大公约数的步骤如下 若r是a b的余数 则gcd a b gcd b r 52 求最大公约数的递归算法 intgcd intm intn if m n 0returnn elsereturngcd n m n 53 求最大公约数的主函数 voidmain intm n t intgcd intm intn scanf d d 54 汉诺塔 又称河内塔 问题是印度的一个古老的传说 开天辟地的神勃拉玛在一个庙里留下了三根金刚石的棒 第一根上面套着64个圆的金片 最大的一个在底下 其余一个比一个小 依次叠上去 庙里的众僧不倦地把它们一个个地从这根棒搬到另一根棒上 规定可利用中间的一根棒作为帮助 但每次只能搬一个 而且大的不能放在小的上面 面对庞大的数字 移动圆片的次数 18446744073709551615 看来 众僧们耗尽毕生精力也不可能完成金片的移动 汉诺塔 55 后来 这个传说就演变为汉诺塔游戏 1 有三根杆子A B C A杆上有若干碟子 2 每次移动一块碟子 小的只能叠在大的上面 3 把所有碟子从A杆全部移到C杆上 经过研究发现 汉诺塔的破解很简单 就是按照移动规则向一个方向移动金片 如3阶汉诺塔的移动 A C A B C B A C B A B C A C此外 汉诺塔问题也是程序设计中的经典递归问题 算法思路 1 如果只有一个金片 则把该金片从源移动到目标棒 结束 2 如果有n个金片 则把前n 1个金片移动到辅助的棒 然后把第n个金片移动到目标棒 最后再把前n 1个移动到目标棒 56 汉诺塔是一个很繁杂的游戏 但用递归的方法解决起来异常简单 规则 1 一次只能移动一个 2 大的不能放在小的上面 3 只能在三个位置中移动 57 问题可分为三个步骤 对于把n个金片从第一根针a上移到第三根针c的问题可以分解成如下步骤 1 将n 1个金片从a经过c移动到b 2 将第n个金片移动到c 3 再将n 1个盘子从b经过a移动到c 这样我们就将移动n个金片的问题变成了移动n 1个金片的问题 这样做下去的话最后就会变成移动一个金片的问题 58 递归方法解汉诺塔 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 59 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 60 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 主函数调用hanoi n 1 2 3 第一次调用 第一次调用hanoi n a b c 第一层 即要把三个金片移到c n 3 61 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 由于n 1则执行hanoi n 1 a c b 第二次调用 n 3 62 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 由于仍然n 1则执行hanoi n 1 a c b 第三次调用 n 2 63 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 由于n 1则printf d d a c 即把金片从a移动到c n 1 64 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 第三层执行完毕 返回到第二层 即下去执行printf d d a c 把第二个金片摆到第二根针上 n 2 65 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 执行下一条语句 又调用第三层hanoi 1 3 1 2 n 2 66 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d n 1 执行结果为3 2 n 1 67 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 第二层也执行完了 返回第一层 执行接下来的语句 结果为1 3 n 3 68 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 执行接下来的语句 再次调用第二层hanoi 2 2 1 3 n 3 69 递归汉诺塔步骤 voidhanoi intn inta intb intc if n 1 printf d d a c else hanoi n 1 a c b printf d d a c hanoi n 1 b a c main intn printf inputn scanf d 2020 2 23 70 6 4变量的作用域与存储方式 先看一个例子 错在那里 voidf1 intt 2 a t b t main inta b printf Entera b scanf d d 编译程序会提示出错 Undefinedsymbol a 和Undefinedsymbol b 为什么 2020 2 23 71 一 变量的作用域即变量的有效范围1 变量按作用域分为全局变量和局部变量2 比较 全局变量 外部变量 局部变量 内部变量 定义位置 函数体外函数体内作用域 从定义处到本源从定义处到本函数结束文件结束举例 所有函数体外定义的变量 1 所有在函数体内定义 2 形式参数 注意与局部变量同名的处理局部变量屏蔽全局变量 不同函数中同名局部变量互不干扰 2020 2 23 72 includeinta b a b为全局变量 voidf1 intx intt1 t2 a a t1 x 4 t2 b 3 b 10 printf f1 t1 d t2 d a d b d n t1 t2 a b main a 2 b 4 此a b是全局变量 赋值 f1 a 调用函数f1 printf main a d b d a b 程序输出结果为 f1 t1 8 t2 12 a 8 b 10main a 2 b 10 2020 2 23 73 若将程序改为 includeinta 2 b 4 a b为全局变量 voidf1 intt1 t2 t1 a 2 t2 b 3 b 100 printf t1 d t2 d b d n t1 t2 b main intb 4 此b是局部变量 赋值 f1 调用函数f1 printf a d b d a b 结论 全局变量与局部变量同名时 局部变量起作用 全局变量被屏蔽 不影响 应小心使用 程序输出结果为 t1 4 t2 12 b 100a 2 b 4 2020 2 23 74 二 变量的存储特性1 变量按存在时间分静态变量动态变量 静态存储类型的变量的生存期为程序执行的整个过程 在该过程中占有固定的存储空间 通常称它们为永久存储 动态存储类型变量只生存在某一段时间内 例如 函数的形参和函数体或分程序中定义的变量 只是在程序进入该函数或分程序时才分配存储空间 当该函数或分程序执行完后 变量对应的存储空间又被撤销了 2 c语言中每一个变量有两个属性 数据类型 存储特性完整的变量定义 存储特性 数据类型 变量名 2020 2 23 75 3 变量的存储特性自动型auto静态型static寄存器型register外部型extern 1 auto型每次进入程序是自动分配内存 不长期占用内存例如 形式参数 自动型局部变量 2 static型 局部静态变量 全局静态变量长期占用内存 2020 2 23 76 例1 分析执行结果f inta intb 0 staticintc 3 b c printf 5d 5d 5d a b c return a b c main inta 2 k for k 0 k 3 k printf 5d n f a 静态变量只初始化一次 再次调用定义它的函数时变量保存了前一次被调用后留下的值 结果 214 a b c 7 f a 21582169 2020 2 23 77 3 register型使用频率高的变量定义为register型 可以提高运行速度 数据内存运算器运算器结果控制器数据 寄存器 寄存器变量只限于整型 字符型 指针型的局部变量 寄存器变量是动态变量 而且数目有限 一般仅允许说明两个寄存器变量 例如 registerintd registercharc 2020 2 23 78 4 extern型引用 extern类型变量名 如果某个模块文件中要用到另一个模块文件中的全局变量 就要用extern说明例如 程序模块file1 c中定义了全局变量ints 而在另一个程序模块file2 c中的函数fun1 中需要使用这个变量s 为此 可以在file2 c的函数fun1 中加上外部变量说明语句 fun1 externints 表明变量s是在其他文件定义的 定义时分配内存 其他文件引用时不再分配内存 2020 2 23 79 6 5编译预处理 编译预处理 是C语言编译系统的一个组成部分 是在编译前由编译系统中的预处理程序对源程序的预处理命令进行加工 源程序中的预处理命令均以 开头 结束不加分号 以区别源程序中的语句 它们可以写在程序中的任何位置 作用域是自出现点到源程序的末尾 预处理命令包括执行宏定义 宏替换 包含文件和条件编译 2020 2 23 80 一 宏定义简单宏定义1 一般形式为 define宏名串 宏体 如 definePI3 14159 定义后 可以用PI来代替串3 14159 2 宏定义的作用在宏定义之后 该程序中宏名就代表了该字符串 3 说明 可以用 undef命令终止宏定义的作用域 例如 undefPI 宏定义的嵌套使用 defineR3 0 definePI3 1415926 defineL2 PI R 宏体是表达式 defineSPI R R 2020 2 23 81 main printf L f nS f n L S 2 PI R替换L PI R R替换S 程序运行结果如下 L 18 849556S 28 247333 双引号内与宏同名的字母不作宏展开 见上例 带参数的宏定义1 带参数的宏定义的一般形式为 define宏名 参数表 字符串如 defineS a b a b definePR x printf s f n x 2020 2 23 82 2 带实参的宏名被展开宏名被所定义的宏体替换 宏体中的形参按从左到右的顺序被实参替换 例如 defineL x x x 2 x x 宏调用 y L 5 调用时用实参5去代替形参x 精于处理展开后的语句为 y 5 5 2 5 5 宏定义与函数的区别 1 引用宏只占编译时间 不占运行时间 2 引用宏没有返回值 如 definesqu n n nvoidmain void printf f n 27 0 squ 3 0 程序输出结果为 27 000000 注意 展开为27 0 3 0 3 0不是27 0 3 0 3 0 2020 2 23 83 3 宏替换的形参无类型 4 实参为表达式的情况函数调用是先计算出实参的值 再将值传递给形参 宏的引用是用表达式替换形参 例如 defineS a b a b引用 S a c b c 展开后的表达式为 a c b c 2020 2 23 84 二 文件包含 include1 文件包含是指一个源文件可以将另一个源文件的全部内容包含进来 2 include命令有两种格式 1 include 2 include 文件名 3 两种格式区别 P96 2020 2 23 85 三 条件编译1 控制条件为常量表达式的条件编译 有以下几种形式 if常量表达式程序段 endif 功能 常量表达式为非 时 程序段被编译 否则 程序段不被编译 if常量表达式程序段 else程序段 endif 功能 常量表达式为非 程序段 被编译 否则 编译程序段 2020 2 23 86 if常量表达式 程序段 elif常量表达式 程序段 elif常量表达式 程序段n else程序段n 1 endif 控制条件为定义标识符的条件编译 ifdef标识符程序段 endif功能 当标识符在该条件编译结构前已定义过时 程序段被编译 否则 程序段不被编译 2020 2 23 87 ifdef标识符程序段 else程序段 endif功能 当标识符在该条件编译结构前已定义过时 程序段 被编译 否则 编译程序段 ifndef标识符程序段 else程序段 endif功能是 当标识符在该条件编译结构之前没有被 define定义过时 程序段 被编译 否则 编译程序段 88 条件编译举例 defineinttag1Main intch scanf d ch ifinttagprintf d ch elseprintf c ch endif 编译成 main intch scanf d ch printf d ch 89 再见
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 图纸专区 > 课件教案


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!