1.2.4 流程控制
C和C++使用循环结构来控制程序的执行。在写程序的时候,某些任务需要重复指定的次数或遇到终止条件才能结束。循环是简化重复任务的编程结构,有3种主要类型:for、while和do…while。
例1-4 for循环
for( Start_Condition ; Test_Condition ; Operation ){
[Statement Block];
}
for循环是最常用的循环结构。在循环开始执行的时候,它会检查关键字for后面的条件。对于给定的Start_Condition,如果Test_Condition的值为真,那么循环执行。循环中的第3个参数Operation操作Start_Condition。循环将一直执行,直到Test_Condition为假时才结束。
for循环特别适用于迭代。如果程序员希望Statement Block执行5次,可以进行如下设置:
for( i = 0 ; i < 5 ; i++ ){
[Statement Block];
}
例1-5 while循环
while( condition ){
[Statement Block];
}
在while循环中,循环一开始就要执行循环条件。如果循环条件的值为真,则执行循环体;如果为假,则退出循环。直到循环条件变为假时,循环才会停止。
例1-6 do…while循环
do{
[Statement Block];
} while( condition );
在do…while循环中,循环条件是最后才被检查的。在执行循环体之后,循环条件才决定是否继续执行循环。如果循环条件的值为真,则继续执行循环;如果为假,则终止循环。do…while循环的循环体在检查循环条件之前至少被执行一次,除了这点以外,其他和while循环很相似。因此,for循环和while循环使用得更频繁。
有一点需要指出,上述3种循环结构在功能上是等效的。哪种循环结构更好,主要取决于其匹配的问题类型。如果循环结构和程序员的处理过程相匹配,可以使错误(特别是off-by-one错误)出现的可能性最小。
例1-7 等效循环——通过循环迭代5次
for循环:
for( i = 0 ; i < 5 ; i++ ){
Statement_Block;
}
while循环:
int i = 0;
while( i < 5 ){
Statement_Block;
i++;
}
do…while循环:
int i = 0;
do{
Statement_Block;
i++;
} while( i < 5 )
在上述示例中,循环体被执行了5次。虽然使用不同的循环方法,但是结果是一样的。换句话说,可以认为所有的循环类型在功能上是等效的。
1.2.5 函数
函数可以看做是小型的程序。在某些示例中,程序员可能想获取某类输入、对该输入执行特定的操作并以特定的格式输出结果。函数的概念是为需要重复执行的操作而提出的。一个程序往往包含了多个函数,可以调用每个函数来执行数据操作。它们还要携带特定数目的参数,并返回一个输出值。
例1-8是携带整型参数并返回阶乘结果的函数实例。
例1-8 阶乘函数
1 int Factorial( int num ){
2 for ( i = (num – 1) ; i > 0 ; i-- ) {
3 num *= i; /* shorthand for: num = num * i */
4 }
5 return num;
6 }
在第1行中,Factorial是函数名。Factorial之前的int指出该函数返回一个整数;int num指出该函数携带一个名称为num的整型参数。return语句的作用是指定函数的输出值。
1.2.6 类
面向对象的程序是以类的形式组织的。类是具有某些特性的离散的程序单元。因为C语言是面向过程的语言,所以它没有类。
类是某类变量和函数的聚合体。类通常要创建一个用于定义类实例(称为对象)的构造函数。类还包含其他函数,用于对对象的实例进行操作。
比如,程序员为飞机厂商开发一个飞行模拟器,用于协助制定设计方案。在这种情形下,使用面向对象技术是很理想的。其设计思想是,创建一个plane类来封装评估飞机飞行的属性和函数。创建多个plane类的实例,每个实例都包含其惟一的数据。
plane类包含以下4个变量。
● weight
● speed
● maneuverability
● position
在模拟环境中,程序员可能要测试飞机在某种场景下的飞行情况。要修改对象的属性,还需要编写下面几个辅助函数。
● SetWeight( int )
● SetSpeed( int )
● SetManeuverability( int )
● SetPosition( [ ] )
● MoveToPosition( [ ] )
下面给出该对象的类的代码,如例1-9所示。
例1-9 plane类
1 public class plane{
2 int weight;
3 int speed;
4 int maneuverability;
5 Location position; /* The Location type defined elsewhere as an (x, y,
z) coordinate */
6
7 plane( int W, int S, int M, Location P ){
8 weight = W;
9 speed = S;
10 maneuverability = M;
11 position = P;
12 }
13
14 void SetWeight( plane current, int W ){
15 current.weight = W;
16 }
17
18 /* Additional Methods for SetSpeed, SetWeight, SetPosition,
SetManeuverability,SetPosition defined here */
19 }
这些代码用于初始化一个plane对象。调用方法必须指定plane对象所需的各个选项:weight、speed、maneuverability和position。例如,SetWeight函数就指出了该对象可以执行的操作。
模拟程序可能要创建plane类的多个实例,并运行一个test flights集,也可能要创建多个实例来测试飞机的特性。例如,plane1可能重5000英磅、飞行时速500、可操作级别为10,而plane2可能重6000英磅、飞行时速600、可操作级别为8。在C++中,类的实例都是以同样的方式作为新的变量来创建的。plane1可以用下述代码来创建。
1 plane plane1;
2 Location p;
3 p = ( 3, 4, 5 );
4 plane1 = plane( 5000, 500, 10, p );
类层次也可以通过继承来协助程序员。类通常是以树状结构来组织的,每个类都有父类和可能的子类。一个继承的类可以访问其父类或超类的函数。比如,如果plane类是vehicle类的子类,那么plane对象就可以访问所有vehicle对象的执行函数。
类带来了其他语言所不具备的优点,它们能有效地将程序组织成可以被继承的模块。还可以创建抽象类(abstract class)用来作为接口。接口只是定义函数,并不实现函数,其实现的细节留给子类来处理。类也可以指定为private类型,以保证除了特定的函数外,类内部的内容都不被外界访问。
1.2.7 案例研究:傅立叶系数估算
在使用有限的带宽发送数据时,要发送和接收完整的二进制数据是不可能的。根据不同的传输电压可以估算通过的将在目标地点进行重构的原始二进制数据。由于传输的电压能标识几个值,因此也可能发送除了1和0以外的其他信息。傅立叶分析可以完成该函数估算。19世纪早期,Jean-Baptiste Fourier提出的方程式就指出,几乎所有的周期性函数都可以用正弦和余弦函数的组合来表示,方程式如下所示:
![]()
对其求积分后(留给读者自己练习),可以计算出a、b、c的值:

下面的程序用a、b、c的值来计算g(t),但是并没有模仿前面的方程式,而是使用了估算曲线下方的图形面积的简易方法。阅读程序,思考一下傅立叶系数是怎样估算出来的。
问题
如何利用矩形来估算曲线下方的面积?
傅立叶系数估算代码
1 #include <stdio.h>
2 #include <math.h>
3
4 void main( void );
5 double geta( double );
6 double getb( double );
7 double getsee( void );
8 double g( double );
9
10 /*globals */
11 double width = 0.0001;
12 double rightorleft=0; /* Initialized to zero so that I sum the
rectangles from the left sides first */
13 /* I put this in in case I want to later prove the accuracy of A and
B */
14 int numterms=10; /* Set the number of coefficients be be calculated
and printed here */
15 double T=1; /* Set period and frequency here */
16 double f=1;
17
18 void main( void ){
19 double a [ numterms + 1 ], b[ numterms + 1 ], c, ctoo , n;
20 int i, j;
21 printf( "\n" );
22 c = getsee( );
23
24 for ( n=1 ; n <= numterms ; n++ ){
25 /* I ignore the zero array value so a[ 1 ] can represent a1 */
26 i = n; /* Need to set i because a[ ] won't take a double */
27 a[ i ] = geta( n );
28 }
29
30 for ( n=1 ; n <= numterms ; n++ ){
31 i = n;
32 b[ i ] = getb( n );
33 }
34 rightorleft=width;
35 /* I'm using this to calculate areas using the right side */
36
37 ctoo = getsee( );
38
39 for ( i=1 ; i<=numterms ; i++ ){ /* Prints table of results */
40 printf( "%s%d%s" , "a", i, " is: " );
41 printf( "%lf", a[ i ] );
42 printf( "%s%d%s" , " b" , i , " is: " );
43 printf( "%lf\n" , b[ i ] );
44 }
45
46 printf( "\n%s%lf\n" , "c is " , c );
47 printf( "%s%lf\n\n" , "ctoo is " , ctoo );
48
49 }
50
51 double geta( double n ){
52 double i, total=0;
53 double end;
54
55 if ( rightorleft==0 ) end = T - width; /* This is needed to make sure
an extra rectangle isn't counted */
56 else end = T;
57
58 for ( i=rightorleft ; i <= end ; i+=width )
59 total += width * ( g( i ) * sin( 6.28 * n * f * i ) );
60 total *= 2/T;
61 return total;
62 }
63
64 double getb( double n ){
65 double i, total=0;
66 double end;
67
68 if ( rightorleft==0 ) end = T - width; /* This is needed to make sure
an extra rectangle isn't counted */
69 else end = T;
70
71 for ( i=rightorleft ; i <= end ; i+=width )
72 total += width * ( g( i ) * cos( 6.28 * n * f * i ) );
73 total *= 2/T;
74 return total;
75 }
76
77 double getsee( void ){
78 double i, total=0;
79 double end;
80
81 if ( rightorleft==0 ) end = T - width; /* This is needed to make sure
an extra rectangle isn't counted */
82 else end = T;
83
84 for ( i=rightorleft ; i <= end ; i+=width )
85 total += width * g( i );
86 total *= 2/T;
87 return total;
88 }
89
90 double g( double t ){
91 return sqrt( 1 / ( 1 + t ) );
92 }
式中的结果不能直接计算出来,本例使用矩形来估算曲线下方面积的方法来估算。使用矩形估算曲线下方面积的时候,可能会低估或高估面积的值。对于g(t)而言,如果使用矩形的左移方法,则由于矩形的边角扩展到曲线的外面会使值高估;同样使用矩形的右移则会低估值。
下面来研究一下该程序的流程。main函数初始化了调用傅立叶系数的变量,并将其结果打印出来。如果有需要,可以添加注释以增加可读性。第1行和第2行引入了标准的输入/输出和数学库。第3行到第7行声明程序中的函数。第8行到14行声明全局变量。其他部分则注重傅立叶转换中的计算条件。变量numterms描述估算的精确性。为了更接近实际的曲线,估算中还使用了大量的条件和矩形。第20行到第28行声明包含a和b的值的数组。第40行到第72行计算矩形的面积。再看看傅立叶系数估算代码的最初的规则,就知道该程序用估算出来的a、b、c来计算g(t)的值。思考一下,估算是如何影响有限带宽环境下的信息传输的。






