1
一、程序设计方法二、作用域或范围 scopel
三、可见性与名称隐藏
2
一、程序设计方法程序的结构是指程序组织、函数安排、变量内存分配、
数据的流动与信息共享等问题。
程序首先由源程序构成,大的程序常分解为一个个相对较小的源程序,每一较小的源程序又细分为若干个功能独立的函数 。
编译和连接器把分散的独立的模块有机的组合起来,形成一个正常运行的可执行文件 。
掌握作用域,可见性,生存期和外部连接属性,静态存储属性这些基本概念是开发软件的必备功底 。
3
1,自顶向下的程序设计现代科学最重要的技巧就是折零,把复杂的问题分解成尽可能简单的细小部分。
自顶向下的模块化处理正是这一手法在程序设计行业的一个运用。
自顶向下的程序设计是一个总体设计的过程,是从一般到具体的设计过程 。
4
涉及到的概念:
a,明确软件的目标软件的目标是用户的直接需要也是程序员创造的原动力,软件的目标确定程序的规模、软件的方案与资源配置,
不编写与软件目标无关的代码。
b,制定整体规划设计整体规划设计是周密安排数据结构与算法的恰当分层和协调呼应。复杂的体系简约为尽量线性无关的简单构架,在各种可能的细分层次中寻求简单明了的布局实现。
5
c,化整为零适当细化化整为零适当细化是在整体框架的层次结构确定以后,
为整体设计的每一个独立部分设计具体的算法和数据结构,
以及不同层次之间数据的协商关系。
d,多路逼近精益求精多路逼近是考虑具体问题存在多种解决途径,此路不通换一种思路,通常总是会存在更好的突破方法;
精益求精则是把高频运用的代码最优化,关键的控制变量简化到最少。
6
模块划分是一个从顶向下的设计过程。顶层是一个主模块,用于程序流程的总控,在交互式编程模式该模块由一个内嵌分支选择结构的无穷循环构成。
主模块下直接监控下一层的若干模块,下层的每一模块各自分管不同的模块。
每一相对独立的模块可分别调试。
模块本身的功能是相对独立的,但模块之间的联系是布局阶段应该重点考虑的内容。
模块之间的联系称为耦合。耦合关系总的分为两种,一种是控制耦合,另一种是数据耦合。
7
模块化程序设计是汇编语言,C语言和 FORTRAN语言强调的编程要领,也是面向对象的编程指南,
程序设计应该遵循的一些基本原则:
大的模块尽量分成若干线性无关的独立模块
模块尽量顺序往下执行或跳转(特别在使用 goto语句或汇编接口的 jmp指令时)
模块的入口或出口尽量简单归一
模块的嵌套层次尽量以少为优(嵌套包括函数调用的嵌套和选择与循环之间的嵌套 )
一个模块或函数的源代码控制在 3个 page之内
8
模块 A 模块 B 模块 C
主模块模块 Ai 模块 Aj 模块 Bk 模块 Cm 模块 Cn模块 Cx
模块 Gb模块 Ga 模块 Gc
模块 Gi 模块 Gj 模块 Gk
层次图示例
9
上面用方块图表示的模块之间的关系为层次图。
主模块调用子模块 A,B,C,而其中的子模块 A调用下层的模块 Ai,Aj。
次下层的模块 Ga是被上层的 A模块序列共同调用的模块,因此这样的模块称为隶属于上层 A模块的公共模块。
同样地 Gb,Gc分别是上层 B或 C模块序列的公共模块。
最下层的模块 Gi,Gj和 Gk则是所有上层共享的模块。
10
2,从下往上的程序设计从下往上的程序设计是一种从简单到复杂的过程。这是一种返朴归真的设计过程,同时也是新手迈入软件殿堂的直接方便法门。
从下往上的程序设计主要的步骤为:
a,从简单的问题入手
b,从易到难各个击破
c,由分到合整体组装
d,由简到繁层层派生
11
复杂的问题是由简单的事物组合成的,因此从简单的问题入手就显得尤为重要,首先解决简单问题的算法和数据结构,确定简单问题的函数实现和相应的数据接口。
简单的问题解决之后,便可以面对略为困难的问题,一步一步地从易到难各个击破,相应的算法和数据结构随之由少到多的丰富。
接着是由分到合整体组装的过程,一个个分散的独立的程序编好后,把所有的模块组装起来形成整体可以运行的程序。
12
3.结构化程序设计模块化强调功能的细分,将大的模块分为若干基本模块,这些模块形成上层调用下层的控制结构。
结构化程序设计重点是关于一个模块的逻辑清晰的组织方式 。
一个模块由七种基本控制结构组成,即顺序结构、三种选择结构和三种循环结构,基本控制结构可以视为单一的语句块融入模块中,通过七种控制结构的适当的排列组合嵌套实现。
结构化程序设计限制其它形式的流程控制形式,特别是
goto语句 。 goto语句跳转的目的直接,当 goto语句从较深的内层的控制结构中向外向下跳转时,比其它形式的控制转向具有更快捷明了的特点。
13
4.多文件结构模块化设计的特点自然导致多文件的组织形式。每一个源文件中包含一到多个函数,这些函数构成源程序。
出现在程序中的名称都遵循先声明后索引的原则,函数的定义和变量的定义必须存在且在各自的范围内唯一的存在。
声明型的语句放置在 *.h头文件中,函数和变量定义放置在实现文件 *.cpp中。
把一个庞大的源程序分解为容易处理的许多相对小的文件符合分层管理的原则 ;一个较小的源文件编译后形成 *.obj
目标模块,这些分别编译的 *.obj目标模块连接后构成 *.exe执行文件。多文件结构常通过工程文件,prj或,dsw来组织。
14
#include<stdio.h>
#include"A,h"
void main (void) { printf ("%d\n",k); } //A,cpp
A.h
A.cpp
A,obj
B.h
B.cpp
B,obj
C.h
C.cpp
C.obj*.lib
x,exe
连接预处理编译
15
模块化设计的特点自然导致多文件的组织形式。每一个源文件中包含一到多个函数,这些函数构成源程序。
出现在程序中的名称都遵循先声明后索引的原则,函数的定义和变量的定义必须存在且在各自的范围内唯一的存在。
声明型的语句放置在 *.h头文件中,函数和变量定义放置在实现文件 *.cpp中。
把一个庞大的源程序分解为容易处理的许多相对小的文件符合分层管理的原则。大的程序由此形成多文件结构。
一个较小的源文件编译后形成 *.obj目标模块,这些分别编译的 *.obj目标模块连接后构成 *.exe执行文件。
多文件结构常通过工程文件,prj或,dsw来组织。
16
二、作用域或范围 scopel
标识符与名称作为同义词使用,作用域或作用范围是名称在程序中可以有效索引、适当操作的特定区域,名称的作用域开始于该名称的声明或定义处,结束于相应的程序段。
1.局部作用域在一个程序块内引入的名称仅在此块中以及包含在此块中的块内是可访问的,且仅有效于从定义开始处直到其匹配的右花括号,}”之间,局部作用域中定义的变量称为局部变量,
程序块由括在花括号 { }中的 0个或多个语句构成,一个程序块整体相当于一条语句。单条语句可以出现的地方程序块也可以出现。
17
程序块包括函数体具有语法格式为:
void funct ( int value,long& parm)
{ typedef long type;
struct sa {int k;int n;};
int add(int,int);
type a =add(1,2);
执行语句序列 1;
type &b=a;
执行语句序列 2;
type c,&d=c ; sa s,&u=s;
执行语句序列 3;
} // value,parm,type,sa,a,b,c,d,s,u,add作用域到此结束
18
前面的执行语句序列 1不识别其后引入的名称 b,c,d,
s,u,后面的执行语句序列 3识别前面同层程序段引入的名称 type,sa,value,parm,a,b,c,d,s,u 。
函数的形参 (在函数标题头内定义的参量 )的作用域认为是在函数体的最外层块的作用域内。
函数形参 value和 parm对于整个函数体都是可访问的。
函数形参就是定义在接口堆栈空间的局部变量,函数型参中的局部变量不可以与函数体中同层的局部变量相同。
19
程序块可以嵌套,灵活地引入变量可以对这些变量的范围和可见性施以准确的控制。
函数的实现部分或定义体就是由一对花括号 { }包含的,
程序块常用于选择语句或循环语句的基本执行语句部分中。
在一个程序块内定义的变量或标识名称其可见性局限于花括号 { }之内的后续部分。
如上 typedef引入类的别名和 struct引入的结构名作用范围介于函数体中。
20
关于 for初始化表达式中的作用域 (C89不支持这种格式 )
C++(C99)中 for循环的第一个表达式中可以引进变量,
其间引入的变量为局部变量,相当于这些变量紧临在 for语句前刚定义。
对于 for (int k=0;e2;e3) {s;}语句,变量 k的有效索引区域即是变量 k的作用域,这个变量的作用域随编译器不同而存在细微的差异。
21
在微软 vc6.0中变量 k的有效索引范围扩展到包含 for语句的最近的程序块,即语句块
{...;for ( int k=0; e2; e3) {s; }..,}相当于
{;...; int k ; for ( k=0;e2;e3) {s;}...k此处有效,..;}。
在变量的作用范围内,不要为一个变量进行两次的存储空间分配。
在 Borland C++Builder5.0中,变量 k的作用域仅局限于循环体语句,超出花括号之外或单条循环体语句之外,名称 k就成为没有定义的变量。
即,
{...; for ( int k=0; e2; e3) {s;}...} 语句块相当于
{...;{ int k ; for (k=0; e2; e3) {s;}}...k此处无效,..}。
22
[例 ] 初始化语句中引入的局部变量的作用范围
#include <iostream.h>
void main()
{ for (int k=0;k<100;++k)
cout << k << "\n";
cout << k << "1.\n";
//?在 Builder5.0中此处的变量 k是没有定义的变量
for (int k=100;k>=0;--k)
//? vc6.0 中 [int k=100;]导致变量 k两次定义
cout << k << "\n";
} //为了程序可移植性应回避作用域不确定的方法。
23
这样,
在 Builder5.0中去掉语句 [ cout << k <<,1.\n”;]则得到正确的程序,
在 vc6.0 中去掉 int改为 for (k=100;k>=0;--k) 则 ok。
在 Builder5.0中运行相关程序遇到变量 k无定义的提示时,可将
for ( int k=0; e2; e3) { s; }
语句改为 int k ; for (k=0; e2;e3) { s; } 。
24
2.函数作用域标号是唯一具有函数作用域的名称。
标号常与 goto语句配合使用,在一个语句前放置一个彼此不同名的标号不影响程序的运行,它们可在一个函数内的任意位置使用,但在函数外部则不能访问。
变量名、标号名属于不同的名称空间,因此可以并行不背地使用。
25
[例 ] 标号名在一个特殊的名称空间
# include<iostream.h>
void main (void)
{ char chs; const int n=8;
for (int k=0; k<n;k++)
{ if(k%2) goto k ;
//标号 k与变量 k同名
chs='a'+k; goto n;
//标号 n与常量 n同名
k,chs='A'+k;
n,cout<< chs <<" ";
}
}
//输出,a B c D e F g H
26
3.函数原型作用域在函数原型中声明的名称其作用域位于两个圆括号之间,
在其它位置无法访问到原型中的标识符,实际上原型中的标识符是可有可无的,但给函数原型的形参一个容易理解、记忆的标识符增加程序的可读性。
以下原型声明了三个名称 (s1,s2,m),这些名称在原型结尾处便超出作用域了,
char * f (char *s1,const char *s2,int m);
//不能写成 char * f (char *s,const char *s,int m);
函数原型可写成抽象的形式,特别当形参中的参数很多使得一行容纳不下函数的原型时:
char *f (char *,const char *,int);
27
4.类作用域类作用域是指类的成员名等可有效访问的范围,结构声明和变量定义,
struct CPoint{ float x ; float y ; }; int y;
5.文件作用域任何在函数或类的外部引进的名称具有文件作用域或全局作用范围,其作用域开始于定义处或声明处,直到文件的结束 。
函数名和在函数体外引入的结构名具有全局作用范围 。
在文件作用域定义的变量称为全局变量,全局变量在定义之后源文件剩余部分不同程序块或函数体的任何位置均可访问,除非被内层块同样的名称所覆盖 。
28
全局变量 (包括静态全局变量 )在全局范围,局部变量在局部范围,在同一范围的变量名不能相同。
不同函数体中的名称属于不同的局部范围,彼此毫不相干。
在一个程序段,全局变量可以与局部变量名相同,系统优先采用局部变量名。
函数形参的数据传递经过堆栈空间进行,堆栈空间中的数据是动态变化的。全局数据可以被不同函数共享。
因此程序设计中把一些关键的数据设置成全局变量,但全局变量越少越好。
29
定义全局变量可同时指定初始值时,若没有指定初始值,则全局变量具有 0值。
初始值可以是一个可以求值的表达式,甚至是函数调用。
全局作用范围中可以通过初始化语句实施特定的运算,
但不可以出现独立的执行语句。独立的执行语句只出现函数体中。 例如:
int g_var=1;
在全局范围不可以分开写成,
int g_var; g_var=1;
30
三、可见性与名称隐藏名称的作用域限定着其在该范围的可操作性,可 见性进一步明确作用域中名称的实际有效性或优先索引性。
通常作用域与可见性是一致的,但复合语句的嵌套结构和在任何位置都可引入变量的灵活特点,导致内层语句块中的变量覆盖外层的同名变量,
C/C++名称搜寻次序为:首先索引内层变量或名称的定义,如果内层程序块中变量或名称没有定义,然后再搜寻次外层变量或名称的定义,依此类推。接着搜寻函数形参列表中的变量定义。最后向上采用全局范围的变量或名称定义。
31
对象引领的成员属于函数形参的最外层。
如果某一层定义的变量往内是唯一的,那末该变量的可见性贯穿其作用域。
可在程序块内引入变量,内层定义的变量有效范围局限于该程序块,与外层的同名变量毫不相干。
内层定义的变量覆盖外层的同名变量或优先索引内层变量。
局部名称隐藏文件作用域的相同名称,但文件作用域名称可以使用全局分辨符,:进行访问。
32
[例 ] 单独使用三目运算符表达式语句,其中的操作数为 void
型的函数调用
#include<stdio.h>
void a (int x)
{ printf ("a=%d\t",x); }
char * s = "b=%d\t";
void main ()
{ int a = 1,b=3;
char * s="%d,%d";
{ char * s="输入两个整数 "; printf ("%s",s); }
scanf (s,&a,&b);
a>b?,:a (a),printf (::s,b);
}
33
程序运行输出结果,程序另一次运行输出结果,
输入两个整数 2,7? 输入两个整数 7,2?
b=7 a=7
函数 {void a(int);} 中的函数名 a是全局名称,定义语句
{int a=1;}中的 a是局部变量名。
::a(a)表示调用全局函数 a(int),圆括号中的 a是局部实参。
34