程序结构PPT课件_第1页
程序结构PPT课件_第2页
程序结构PPT课件_第3页
程序结构PPT课件_第4页
程序结构PPT课件_第5页
已阅读5页,还剩90页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

练习 include stdafx h usingnamespacestd boolExchange inta int intmain inta 3 b 6 c 9 d 12 e 15 f 18 cout a a b b c c d d e e f f endl Exchange a b c d 第七章程序结构Chapter7ProgramStructure 程序结构 使程序得以运行的框架组织便是程序结构 对程序结构的研究 是为了更好地表达算法思想 使其符合编译逻辑 又具有更好的可读性和可维护性学习目标 1 从简单的函数层层调用 初步理解 程序结构2 学习合理组织程序的规则与经验 掌握扩展程序规模的基本方法 第七章内容 函数组织 FunctionOrganization 头文件 HeaderFiles 全局数据 GlobalData 静态数据 StaticData 作用域与生命期 Scopes Lifetime 名空间 namespace 预编译 Pre Compilation 7 1 1程序构成 函数 对输入参数负责 埋头做自己的事 最终返回结果函数组织 通过在函数中进行函数调用来扩展运行的规模 层层叠叠的函数构成树结构做法 将若干个函数组织成文件 又将若干个文件构成程序的办法来进行编程分工 对如下的函数调用关系 进行文件划分 文件 文件 文件 图7 2程序结构的描述 7 1 2程序文件拆分 在把握总体模块结构的基础上 一般是一个函数一个函数地编写 如果有很多函数 就要分好类 分成几个程序文件来由几个程序员负责 如图7 2的程序结构 八个函数可以从一个文件拆分成三个程序文件实现 如图7 3 函数总是声明在前 调用在后 为了实现多文件结构的编程 必须在程序工程中将这些编辑好的文件加入 函数声明voidf1 voidf2 voidf3 函数定义intmain f1 f2 f3 voidf1 voidp voidf1 voidf1 intmain voidf1 voidg1 voidg1 图7 3一个文件拆分成三个程序文件 7 2头文件 原始头文件 作为共同开发的项目 为了共享彼此的过程资源 函数 将全体函数声明放在一个共用的头文件中界面头文件 界定模块可用资源 函数 数据 类型等 可由一个或几个头文件组合 其实现由他人提供 或提供他人使用的模块资源 它是由软件工程师分发的 以规范项目开发为目的的资源文件做法 练习划分函数组 模仿学习构造头文件 并注意头文件的应含内容 7 2 1原始头文件 函数声明只能有惟一的一次 为了方便 可以将一个程序中所有要用到的函数专门做成一个程序文件 加在每个程序文件的开始处 也就是将那些函数声明做成一个文件后缀为 h的文件 称之为头文件 用指令 include的方式加在程序文件的开始处 如在前面的多文件结构中可以先做一个abc h的头文件 其内容为 abc hvoidf1 voidf2 voidf3 voidg1 voidg2 voidp voidh 原始头文件 包含图中的一切函数声明 头文件的使用 使函数调用免于声明 a1 cpp include abc h voidf1 if p g1 else g2 h 头文件的使用 使函数调用免于声明 a2 cpp include abc h intmain f1 f2 f3 voidf3 f1 voidp f3 头文件的使用 使函数调用免于声明 a3 cpp include abc h voidh voidf2 g1 g2 voidg1 voidg2 7 2 2界面头文件 上述的规定和使用头文件的方法是很原始的 头文件更重要的作用是在设计阶段规定界面 也就是通过头文件可以明白地看出 某个程序文件提供了什么服务 这种头文件称为用户界面 界面头文件 a1 ha1 cpp提供的资源voidf1 a2 ha2 cpp提供的资源voidp a3 ha3 cpp提供的资源voidg1 voidg2 voidf2 voidh a1 cpp include a2 h include a3 h voidf1 if p g1 else g2 h 使用界面头文件 a2 cpp include a1 h include a3 h staticvoidf3 intmain f1 f2 f3 voidf3 f1 voidp f3 使用界面头文件 a3 cpp include a3 h voidh voidf2 g1 g2 voidg1 voidg2 使用界面头文件 7 2 3头文件的内容 头文件的语句也要符合C 语法头文件的作用是给源程序提供可以使用的外部资源一览表 它不仅仅包括函数声明 它基本上还包括 全局数据声明 如externintn externinta 函数声明 如voidfn 类型声明 如classA 全局常量定义 如constfloatpi 3 14 内联函数定义 如inlinevoidfn 模板声明和定义 如templateclassA 名空间定义 如namespaceN 类型定义 如enumCOLOR classA 预编译指令 如 include注释 如 2003年5月7日创建 由于头文件可能出现在一个程序的若干个源程序文件中 所以将一些实体定义放在头文件中是不明智的 因为一种定义体在一个程序中只能出现一次 如 includeintmain std cout helloworld n 然后 在源文件a cpp中 写上 include abc h 编译运行 虽然能得到结果 但是这从根本上背离了头文件在C 程序结构体系中的作用 头文件一定不能包括 全局数据定义 如inta 函数定义 如voidfn 7 3全局数据 全局数据 使若干个模块在程序范围内共享 读与写 数据 是若干程序文件沟通数据的一种形式意义 模块的独立性由数据的封闭性来支持 全局数据破坏了数据的封闭性 因而对小程序简单而对规范化程序则不登大雅之堂做法 练习函数之间用参数传递数据的常规形式 尽量避免使用全局数据 7 3 1全局数据访问 全局数据就是在任何函数的外部声明或定义的 在程序范围内可以访问的数据 对于大多数函数都要访问某个数据的情形时 可以将它设置为全局的 就可以免于参数传递 例如 对于矩阵的输入 处理和输出 vector a globalDatavoidinput voidtranspose voidprint intmain input usingatranspose usingaprint usinga 7 3 2消除全局数据 全局数据破坏了模块结构的独立性 也破坏了抽象数据结构的封闭性 程序是各个独立模块的聚集 全局数据牵扯了各个模块 使其无法独立 可以通过参数传递的方法消去全局数据 如 消去全局数据 前一个过程的输出作为后一个过程的输入 typedefvector Mat Matinput Mattranspose constMat 7 3 3一次定义原则 全局数据有全局变量 全局常量 全局对象等 它们都有指针 引用 数组和其他形式 全局数据在程序存储结构中置身于全局数据区的位置 全局数据区的整个区域在程序启动时 初始化为0 在多文件结构的程序中 像单文件那样的全局数据定义形式存在着问题 因为在不同源程序文件中定义同名全局数据 就是多次构建全局数据实体 它意味着各个程序文件实际上使用的是各不相同的实体 如 多文件结构中全局数据的冲突 include intn 8 voidf intmain std cout n n f 8 include intn voidf std cout n n 0 程序中的两个函数显示一个全局整型变量 却出现了不一致 原因是全局数据也应像函数那样 多次声明 而只能有一次定义 全局数据的声明形式是在全局数据定义形式前加关键字extern 如 在多个程序文件组成的程序中共享数据 要遵守一次定义规则 item1 cpp includeusingnamespacestd intn 8 definevoidf intmain cout n n f item2 cpp includeusingnamespacestd externintn declarevoidf cout n n 还可以将各程序文件中的全局数据声明放在头文件中实现 如 f0705 h externintn voidf f0705 cpp include f0705 h include intn 8 intmain std cout n n f f07051 cpp include f0705 h include voidf std cout n n 7 3 4全局常量 全局常量也是放在全局数据区 只供读取 不许修改 不会扰乱模块关系 不允许在同一个程序中反复定义全局常量 但可以在不同的程序文件中重复定义 全局常量可能被滥用 如求矩阵的和 其数据放在ab txt中 每组数据前两个整数表示行和列 后面则是矩阵的元素 计算和然后输出 在程序中 为了求若干个矩阵的和 先定义两个最大行列数的二维数组 然后反复重用 由于数组的下标只允许为常量 因此 设置全局变量 带来的后果是 占用了不必要的空间 若行列放宽后 则必须要改程序 带来了函数划分时所引入的参数传递困难 程序如下 include include includeusingnamespacestd constintrow 100 constintcol 100 intmain ifstreamin ab txt intma row col intmb row col intmc row col for intr c in r c for inti 0 i ma i j for inti 0 i mb i j for inti 0 i r i for intj 0 j c j mc i j ma i j mb i j for inti 0 i r i for intj 0 j c j cout setw 4 mc i j cout endl 该程序可以完全不用全局常量 如下列程序可以表示了一种便于函数划分 增加函数独立性的常规方法 结构更加合理 不滥用空间 不滥用全局数据 运行结果与前面程序一致 include include include includeusingnamespacestd typedefvector Mat voidinput istream voidinput istream 7 4静态数据 静态全局数据 在一个程序文件中共享的数据 注意 全局数据则在多个程序文件中共享数据静态局部数据 在屡次调用的同一个函数中共享的数据 7 4 1静态全局数据 在过程化程序设计中 为了使程序文件发挥模块的作用 有必要定义一种模块的局部量 它区别于其他程序文件 称之为静态全局数据 也称全局静态数据 函数的模块性在于只与其输入 输出联系 定义的变量在程序来说是局部变量 但在该函数内部来说 却是全局的 程序文件的模块性在于只与其定义的全局函数联系 只与包含文件联系 其定义的静态全局数据对于该程序文件内部来说是全局的 但对于整个程序来说却是局部的 在程序工程中添加一个程序文件 等于添加了一个程序文件模块 意味着其他地方要使用该程序文件所定义的某些函数 在该程序文件中包含了某些头文件 意味着该程序文件要使用其他地方的函数 如根据图7 3的程序文件划分 其中a2 cpp可以得到图7 4 a1 ha3 h 程序文件模块a2 cpp a2 h 图7 4程序文件模块 f1 f2 p 程序如下 include abc h intmain f1 f2 f3 voidf3 f1 voidp f3 程序中 有的函数是为文件中的其他函数服务的 并不对外提供服务 这些函数应声明为静态 表示局部于程序文件 同样有的变量只是为本文件服务 也不是全局数据 应标以static 这些函数和变量称为静态全局数据和静态全局变量 它只在本文件范围内可见 在其他程序文件中不可见 没有静态全局常量 前述的全局常量可以看做是文件变量 每个程序文件都可以通过 constinta 3 的形式来定义惟一的文件域全局常量 即静态全局常量 这就是为什么可以将全局常量写入头文件的原因 全局常量有内部链接性 文件范围内有效 7 4 2静态局部数据 函数中的变量为局部变量 在定义局部变量前面加上static 就成了静态局部变量 静态局部变量驻留在全局数据区 默认初始化值为0 而且不会受函数的调用和返回的影响 函数第一次被调用时 静态局部变量被建立 此后该变量一直存在 直到程序运行结束 静态局部变量在函数内部定义 但却驻留在全局数据区 所以 从可见性来说 它与局部变量一致 从生命期来说 它与全局变量一致 下面的程序演示了全局变量 静态局部变量和局部变量的区别 includeusingnamespacestd voidfunc intn 1 intmain inta 0 b 10 cout a a b b n n endl func cout a a b b n n endl func voidfunc staticinta 2 intb 5 a 2 b 5 n 12 cout a a b b n n endl a 0 b 10 n 1a 4 b 10 n 13a 0 b 10 n 13a 6 b 10 n 25 由于静态局部变量是局部的 所以若把该变量的指针作为函数返回值到处传播是不妥的 而且也丧失了模块的独立性 因为任何依附于某个块的操作都必须依赖于某个块而不能独立 7 5作用域与生命期 作用域 有很多种 变化最多的是局部作用域 作用域遵守就近原则 它总是取用最贴近的名字 除非名字加前缀 则指特定区域的名字生命期 实体一旦产生 定义 后 存活时间的度量作用域与生命期 作用域是编程规范 用于编译时的语法检查 生命期是程序运行中的实体存活度量 体现运行程序的内在规律 名字访问遵守作用域规则 而作用域以实体存活为前提 7 5 1作用域 C 的作用域有全局作用域 文件作用域 函数作用域 函数原型作用域 类作用域和局部作用域 作用域规则主要是针对程序文件而言 所以全局作用域就是文件作用域 函数作用域即不管名称在函数的什么地方声明 总是可以在函数的任何位置先使用该名称 只有标号是函数作用域的 设置标号即对标号名称进行声明 常以 goto标号 的形式出现 函数原型作用域 是表明函数声明时的形参与上下文无关 如 doublewidth 其中的width并未重复定义voidarea doublewidth doublelength length 0 错 length无定义正因为如此 函数声明中形参可以省略 因为它与上下文没有任何关系 类作用域与名空间机制类似 局部作用域指在函数内部的动作序列描述中 依据各语句块甚至整个函数体范围内所定义的数据应遵循的数据访问规则 在局部作用域中 语句块往往是嵌套的 所以变量在其作用域并非一定可见 如果遇到更贴近的变量定义 则另一个外层同名定义将被暂时屏蔽 如 嵌套的局部作用域 includeusingnamespacestd voidfn inty intj 8 j为全局作用域 intmain intx 1 fn x x作用域结束voidfn inty y作用域开始if inti 1 if语句块 i作用域开始i 2 i elsei 100 if语句块结束 则i作用域结束 intx 1 x作用域开始if x y cout x endl elsecout y endl x作用域结束switch inti 2 switch语句块 i作用域开始case1 cout i endl switch语句块结束 则i作用域结束i 3 errorintsum 0 sum作用域开始for inti 0 i 10 i i作用域开始sum i i作用域结束intj 3 fn函数块中 intj作用域开始charch fn函数块中 charch作用域开始 doublej 本块中 doublej作用域开始j 5 虽赋整数于j 但仍然指doublej 非intj j 6 全局变量通过 操作可见 但局部intj不可见ch A 只要本块中没有定义ch变量 则外块ch可见 doublej作用域结束j 6 0 intj可见 j ch y sum作用域结束 7 5 2生命期 生命期指一个实体产生后 存活时间的度量 静态局部数据的生命期并不与局部作用域一致 它的生命期一直延续到程序运行结束 另有一种动态生命期 是由new申请到内存空间后 该空间实体开始有效 一直到delete语句释放该内存空间 在动态生命期中 其有效的堆空间实体可能被跨函数地访问 因此 其作用域是整个程序范围的 如 动态生命期 include int fn int ap newint ap所指向的内存空间开始有效 可以访问了returnap intmain int bp fn bp 15 std cout bp n delete bp bp所指即ap所指 此处使该内存空间无效 7 6名空间 名空间 解决名字冲突的方法 所有名字都有空间归属 在一定的空间中 名字是不允许冲突的 引用一个名字时 加上空间归属的前缀 就可以唯一确定该名字所对应的实体无前缀名字 很多时候 名字都是无前缀的 这是因为事先已经指定了默认名字空间 如果默认名空间在两个以上 则必须注意名字冲突的可能性 C 的名称理解 肯定不能仅仅用名称的作用域规则来规范 名空间机制才是真正全面发挥作用的名称认定和作用域规则 它规定 一个名称必须在使用的域中明确声称其使用的 空间名 才能在域中默认的使用该名称 就像使用usingnamespacestd 一直在默认使用std标准库名一样 7 6 1名空间的概念 C 的名空间机制 就是为了支持大规模程序的逻辑设计 排解名字冲突而产生的 程序设计语言的描述可以对应整个世界 世界上有许多重名 在不同的场合表达不同的意思 在程序里面 就是通过名空间来区分的 因为程序是跨文件的 所以对应的名空间也是跨文件的 7 6 2名空间的组织 名空间的定义通过下列形式 namespacename 名称声明或定义 因为名空间总是凌驾于其他程序文件之上的 它是其他程序文件中的所有外部名称使用的规范描述 所以一般总是将名空间的定义放在头文件中 7 6 3组织模块 一个模块的输入 是指其他模块提供的服务 模块的输出是提供给其他模块使用的服务 可以将包括许多程序文件在内的更大模块的名称集合 组织在一个头文件中 原先放在头文件中的各个声明 都可以放在名空间中而成为整个程序的模块结构层次中的一个 如有一个程序 由其函数调用关系可以将其划分为三个模块 模块1由文件1组成 模块2由文件2 3 4组成 模块3由文件5 6组成 如图7 5所示 图7 5函数调用关系 模块2 模块3 由图7 5能得到下图的程序文件组模块 共有三个模块 模块1 模块2 模块3 在软件设计人物完成后 便可以根据图例所示的三个模块为软件资源 再加上C 的系统资源 编写代码 7 6 4数据名冲突 名空间解决模块名冲突问题远远不够 因为在模块中还有各种数据的往来 难免存在着交叉 数据的名空间规定更复杂 因为它们不但比模块数量更多 而且访问数据的权限并不是对每个程序员都一样的 常用的方法是使用 数据封装 来解决数据的名空间问题 也就是对象化编程的基本思想 在后面的类机制会详细讲述 7 6 5名空间的用法 当自己要定义一个函数与标准库的名称发生冲突的时候 就不能简单的将标准库的所有名称一并默认 如定义一个 intabs inta returna 0 a a 这个时候 就可以选择一个版本使用 如 解决名字冲突 include usingnamespacestd intabs inta returna 0 a a intmain inta abs 5 intb std abs 5 std cout a std endl b std endl 如果标准库中的某个名称用的很频繁 又可能会遇到标准库的名称与自己定义的名字冲突 就可以局部默认名空间的名称使用 如上述程序可以改写为 局部名空间默认 includeusingstd cout usingstd endl intabs inta returna 0 a a intmain inta abs 5 intb std abs 5 cout a endl b endl 7 7预编译 预编译 在正式编译前的准备工作 它包括文件的加盟 代码的取舍 字符的替代等操作文件加盟由include指令引导 代码取舍由ifdef或ifndef引导 字符替代功能在逐渐退化 因为 语言本身完全可以胜任这项工作 常用的预编译指令 包含指令 include条件指令 if elif else endif ifdef ifndef定义指令 define undef 7 7 1 include指令 include指令指示预编译将包含的头文件的内容附加到程序文件中 以参加编译 如果头文件是C 系统提供的 则用尖括号把头文件括起来 如果是自定义的头文件 则用双引号把头文件括起来 用户自定义的头文件一般放在源程序文件路径中 7 7 2条件编译指令 条件编译的作用是直接取舍程序语句和协调多个头文件 如在C 系统头文件中常有这样的编译指令 ifdef USE OLD RW STL include else include endif 是为了兼容旧版C 头文件 如果C 的编译设置开关并不强调标准C 则在预编译开始时预编译名称 USE OLD RW STL就会已经声明 于是预编译到了此处 ifdef 的编译条件为真 便执行 include编译指令 否则就执行 include编译指令 条

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论