程序组织与控制结构
立即解锁
发布时间: 2025-08-21 02:03:36 阅读量: 1 订阅数: 5 


C语言中的数值计算艺术:科学计算必备指南
### 程序组织与控制结构
#### 程序、诗歌与音乐的类比
计算机程序、书面诗歌和书面乐谱存在紧密的类比关系。它们都是视觉媒介,以二维页面或计算机屏幕上的符号呈现。然而,这些静态的二维表示实际上传达的是随时间展开的过程。诗歌供人阅读,音乐供人演奏,程序则按顺序执行计算机指令。
在这三种情况中,信息传达的目标都是人类。其目的是尽可能高效地让接收者提前充分理解过程将如何随时间展开。在诗歌里,目标是读者;在音乐中,是演奏者;在编程中,则是程序用户。
有人可能认为程序的传达目标是计算机,程序用户只是给机器输入数据的无关中间人。但对于想要了解程序工作原理、对其进行修改以适应特定应用,并且希望他人能够理解自己工作的人来说,程序的传达目标显然是人类,而非机器。
#### 编程的层次结构
编程、音乐和诗歌都是人类大脑的符号构造,自然地呈现出多层次的嵌套结构。
在语言中,声音(音素)构成有意义的小单元(词素),词素组成单词,单词组成短语,短语组成句子,句子组成段落,进而形成更高层次的意义。在音乐里,音符组成音乐短语,短语形成主题、对位、和声等,进而构成乐章、协奏曲、交响乐等。
程序的结构同样具有层次性。良好的编程实践会针对不同层次采用不同的技术。
- **低层次**:包括 ASCII 字符集、常量、标识符、操作数和运算符。
- **语句层次**:如 `a[j+1]=b+c/3.0;` 。编写代码时应清晰明了,避免过于复杂。例如,将 `j = (0, 1, 2)` 循环置换为 `k = (1, 2, 0)` 时,以下几种实现方式中,更推荐中间的方式:
- 复杂方式:`k=(2-j)*(1+3*j)/2;`
- 推荐方式:
```c
k=j+1;
if (k == 3) k=0;
```
- 详细方式:
```c
switch (j) {
case 0: k=1; break;
case 1: k=2; break;
case 2: k=0; break;
default: {
fprintf(stderr,"unexpected value for j");
exit(1);
}
}
```
- **语句块层次**:语句常以“组”或“块”的形式出现,只有作为一个整体才有意义。例如:
```c
swap=a[j];
a[j]=b[j];
b[j]=swap;
```
这显然是两个变量的交换操作。而 `ans=sum=0.0; n=1;` 很可能是在迭代过程之前对变量的初始化。在这个层次,添加注释(如“initialize” 或 “exchange variables”)是良好的编程习惯。
- **控制结构层次**:如 `switch` 结构、`for` 循环等,后续会详细讨论。
- **函数和模块层次**:涉及函数、模块以及整个计算任务的“全局”组织。在这个层次,模块化和封装是重要的编程概念。模块化意味着程序单元之间仅通过明确定义且范围狭窄的接口进行交互。良好的模块化实践是大型复杂软件项目成功的关键,即使对于个人科学家或普通编程任务也很有帮助。
一些编程语言(如 Modula - 2 和 C++)通过高级语言构造来促进良好的模块化。在 Modula - 2 中,函数、类型定义和数据结构可以封装到“模块”中,通过声明的公共接口进行通信,其内部工作对程序的其他部分隐藏。在 C++ 中,“类”是关键概念,它是用户可定义的数据类型的泛化,提供数据隐藏、自动数据初始化、内存管理、动态类型和运算符重载等功能。
超越模块化的是面向对象编程的概念。像 C++ 或 Turbo Pascal 5.5 这样的编程语言允许模块的公共接口接受类型或操作的重新定义,这些重新定义会在模块的层次结构中共享(即多态性)。此外,还涉及继承(定义一个继承另一个类型的所有结构并添加自身额外结构的数据类型的能力)和对象可扩展性(在不访问模块源代码的情况下为模块添加功能的能力)等概念。
由于选择的语言是 C,难以实现模块化和面向对象编程,同时考虑到读者可能希望将算法融入自己选择的模块或对象中,目前也没有科学面向对象计算的标准“类”集合,因此没有对代码进行模块化或面向对象处理。但在 C 的限制范围内,尝试使程序“对对象友好”,采用 ANSI C 及其函数原型作为默认 C 方言,并在实现部分特别关注结构化编程实践。
#### 控制结构
执行中的程序并非严格按照语句编写的线性顺序展开。影响语句执行顺序或决定语句是否执行的语句称为控制语句。控制语句只有在其所控制的语句组或块的上下文中才有意义。
结构化编程的目标是使程序的控制在视觉呈现上清晰明了。这与计算机如何看待程序无关,计算机并不关心是否使用结构化编程,但人类读者会在意,因为调试和完善结构良好的程序要容易得多。
实现结构化编程目标有两种互补的方法:
1. **熟悉标准控制结构**:编程中反复出现的基本控制结构数量不多,大多数编程语言都为它们提供了方便的表示。应尽可能仅用这些标准控制结构来思考编程任务,并以一致、常规的方式表示它们。虽然这可能会限制创造力,但就像莫扎特受奏鸣曲形式、莎士比亚受十四行诗格律要求的限制一样,在适当的格式限制下,创造力更有利于信息传达。
2. **避免难以识别的控制语句**:尽量避免使用带有命名标签的语句和 `goto` 语句。`goto` 语句虽然会打断程序阅读,但命名语句标签才是危险所在。当阅读程序时遇到命名语句标签,会产生不确定性,难以理解程序的执行逻辑。
以下是一些标准控制结构的介绍:
- **迭代结构**:
- **for 循环**:在 C 中,简单的迭代可以使用 `for` 循环实现,例如:
```c
for (j=2;j<=1000;j++) {
b[j]=a[j-1];
a[j-1]=j;
}
```
- **while 循环**:大多数语言(除了 FORTRAN)都提供类似以下 C 语言的结构:
```c
while (n < 1000) {
n *= 2;
j += 1;
}
```
`while` 循环的特点是在每次迭代前评估控制条件。如果条件不成立,循环体中的语句将不会执行。
- **do - while 循环**:与 `while` 循环相关的控制结构,在每次迭代结束时测试控制条件。在 C 中,其形式如下:
```c
do {
n *= 2;
j += 1;
} while (n < 1000);
```
`do - while` 循环的循环体至少会执行一次,无论初始条件是否成立。
- **IF 结构**:C 语言中的 `if` 结构与 Pascal、Algol、FORTRAN 等语言类似,通常形式如下:
```c
if (...) {
...
}
else if (...) {
...
}
else {
...
}
```
由于 C 语言中只有当块中有多个语句时才需要复合语句的花括号,所以 `if` 结构可能不如 FORTRAN 或 Pascal 那样明确。在构造嵌套 `if` 子句时需要小心。例如:
```c
if (b > 3)
if (a > 3) b += 1;
else b -= 1;
```
按照缩进,代码的意图是 “如果 b 大于 3 且 a 大于 3,则增加 b;如果 b 不大于 3,则减少 b”,但根据 C 语言规则,实际含义是 “如果 b 大于 3,则评估 a;如果 a 大于 3,则增加 b,如果 a 小于等于 3,则减少 b”。为避免这种混淆,应添加花括号:
```c
if (b > 3) {
if (a > 3) b += 1;
} else {
b -= 1;
}
```
以下是一个主要由 `if` 控制语句组成的工作程序,用于计算指定日期的儒略日数:
```c
#include <math.h>
#define IGREG (15+31L*(10+12L*1582))
long julday(int mm, int id, int iyyy)
{
void nrerror(char error_text[]);
long jul;
int ja,jy=iyyy,jm;
if (jy == 0) nrerror("julday: there is no year zero.");
if (jy < 0) ++jy;
if (mm > 2) {
jm=mm+1;
} else {
--jy;
jm=mm+13;
}
jul = (long) (floor(365.25*jy)+floor(30.6001*jm)+id+1720995);
if (id+31L*(mm+12L*iyyy) >= IGREG) {
ja=(int)(0.01*jy);
jul += 2-ja+(int) (0.25*ja);
}
return jul;
}
```
- **Break 结构**:当循环需要在某个条件满足时退出,C 语言中可以使用 `break` 语句。`break` 语句会终止最内层的 `for`、`while`、`do` 或 `switch` 结构,并继续执行下一条顺序指令。例如:
```c
for(;;) {
[statements before the test]
if (...) break;
[statements after the test]
}
[next sequential instruction]
```
以下是一个使用多种迭代结构的程序,用于查找 1900 年到 2000 年期间星期五 13 号且月亮为满月的日期:
```c
#include <stdio.h>
#include <math.h>
#define ZON -5.0
#define IYBEG 1900
#define IYEND 2000
int main(void) /* Program badluk */
{
void flmoon(int n, int nph, long *jd, float *frac);
long julday(int mm, int id, int iyyy);
int ic,icon,idwk,im,iyyy,n;
float timzon = ZON/24.0,frac;
long jd,jday;
printf("\nFull moons on Friday the 13th from %5d to %5d\n",IYBEG,IYEND);
for (iyyy=IYBEG;iyyy<=IYEND;iyyy++) {
for (im=1;im<=12;im++) {
jday=julday(im,13,iyyy);
idwk=(int) ((jday+1) % 7);
if (idwk == 5) {
n=(int)(12.37*(iyyy-1900+(im-0.5)/12.0));
icon=0;
for (;;) {
flmoon(n,2,&jd,&frac);
frac=24.0*(frac+timzon);
if (frac < 0.0) {
--jd;
frac += 24.0;
}
if (frac > 12.0) {
++jd;
frac -= 12.0;
} else
frac += 12.0;
if (jd == jday) {
printf("\n%2d/13/%4d\n",im,iyyy);
printf("%s %5.1f %s\n","Full moon",frac," hrs after midnight (EST)");
break;
} else {
ic=(jday >= jd ? 1 : -1);
if (ic == (-icon)) break;
icon=ic;
n += ic;
}
}
}
}
}
return 0;
}
```
运行该程序可知,在 1900 年到 2000 年期间(时区 GMT - 5),星期五 13 号且月亮为满月的日期有:3/13/1903、10/13/1905、6/13/1919、1/13/1922、11/13/1970、2/13/1987、10/13/2000、9/13/2019 和 8/13/2049。
#### 其他“标准”结构
建议避免使用编程语言中一些不常用的“特色”结构。这些结构在设计时看似不错,但随着时间推移,会使程序难以翻译成其他语言,也难以阅读。
在 C 语言中,`switch...case...default` 结构存在问题,不同编译器对其控制表达式允许的数据类型存在不确定性。对于 `char` 和 `int` 类型普遍支持,对于其他类型(如 `float` 或 `double`),应使用更易识别和翻译的 `if...else` 结构。ANSI C 允许控制表达式为 `long` 类型,但许多旧编译器不支持。`continue` 结构通常可以用 `if` 结构替代,且不会损失清晰度。
#### 标准控制结构流程图
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B{迭代完成?}:::decision
B -- 否 --> C(执行块):::process
C --> D(增加索引):::process
D --> B
B -- 是 --> E([结束]):::startend
```
#### 标准控制结构总结
| 控制结构 | 特点 | 示例代码 |
| ---- | ---- | ---- |
| for 循环 | 适用于已知迭代次数的情况 | `for (j=2;j<=1000;j++) { b[j]=a[j-1]; a[j-1]=j; }` |
| while 循环 | 先判断条件,再执行循环体 | `while (n < 1000) { n *= 2; j += 1; }` |
| do - while 循环 | 先执行循环体,再判断条件,循环体至少执行一次 | `do { n *= 2; j += 1; } while (n < 1000);` |
| if 结构 | 根据条件执行不同的代码块 | `if (b > 3) { if (a > 3) b += 1; } else { b -= 1; }` |
| break 结构 | 用于跳出循环 | `for(;;) { if (...) break; }` |
### 程序组织与控制结构
#### 高级主题说明
文中以较小字体呈现的内容为“高级主题”,这些内容可能超出了主要论述范围,或者需要更多的数学背景知识,又或者是一些更具推测性的讨论或不太成熟的算法。初次阅读时跳过这些高级主题不会影响对关键内容的理解。
#### 儒略日转换为日历日期的例程
前面提到的 `badluk` 程序通过遍历月份和年份,避免了使用将儒略日数转换为日历日期的算法。下面给出一个实现该功能的例程:
```c
#include <math.h>
#define IGREG 2299161
void caldat(long julian, int *mm, int *id, int *iyyy)
{
long ja,jalpha,jb,jc,jd,je;
// 具体实现代码后续补充
// 该例程将输入的儒略日数转换为对应的月份、日期和年份
}
```
此例程是前面 `julday` 函数的逆操作,输入为儒略日数,输出为该日中午开始的月份、日期和年份。
#### 结构化编程的重要性及实践建议
结构化编程强调程序控制在视觉呈现上的清晰性,这对于人类读者理解和维护程序至关重要。以下是一些结构化编程的实践建议:
1. **遵循标准控制结构**:熟悉并使用常见的控制结构,如 `for` 循环、`while` 循环、`if` 结构等,以一致的方式编写代码,提高代码的可读性和可维护性。
2. **避免使用 `goto` 和命名标签**:`goto` 语句和命名标签会使程序的控制流程变得复杂,增加理解和调试的难度。尽量使用标准控制结构来实现相同的功能。
3. **添加注释**:在关键的代码块和控制结构处添加注释,解释代码的功能和意图,帮助他人(包括未来的自己)更好地理解代码。
4. **模块化设计**:将程序分解为多个模块,每个模块负责特定的功能,模块之间通过明确定义的接口进行交互。这样可以提高代码的可复用性和可维护性。
#### 不同控制结构的使用场景分析
不同的控制结构适用于不同的编程场景,以下是一些常见的使用场景分析:
| 控制结构 | 使用场景 |
| ---- | ---- |
| for 循环 | 当需要执行固定次数的迭代时,使用 `for` 循环非常方便。例如,遍历数组、执行特定次数的计算等。 |
| while 循环 | 当迭代次数不确定,需要根据某个条件来决定是否继续迭代时,使用 `while` 循环。例如,读取文件直到文件结束、等待用户输入等。 |
| do - while 循环 | 当需要至少执行一次循环体,然后再根据条件决定是否继续迭代时,使用 `do - while` 循环。例如,菜单选择系统,用户至少需要进行一次选择。 |
| if 结构 | 当需要根据不同的条件执行不同的代码块时,使用 `if` 结构。例如,根据用户输入的选项执行不同的操作。 |
| break 结构 | 当需要在循环中提前退出时,使用 `break` 结构。例如,在查找某个元素时,一旦找到就立即退出循环。 |
#### 面向对象编程与模块化编程的结合
虽然本文主要基于 C 语言进行讨论,但面向对象编程和模块化编程的概念在现代编程中具有重要意义。在更高级的编程语言(如 C++、Java 等)中,可以将两者结合起来,实现更高效、可维护的代码。
面向对象编程通过类和对象的概念,将数据和操作封装在一起,提高了代码的安全性和可复用性。模块化编程则将程序分解为多个独立的模块,每个模块负责特定的功能,降低了代码的耦合度。
例如,在一个图形处理程序中,可以将不同的图形对象(如矩形、圆形等)定义为类,每个类包含自己的数据和操作方法。同时,将图形的绘制、变换等功能封装在不同的模块中,通过模块之间的接口进行交互。
#### 代码优化与性能考虑
在编写程序时,除了考虑代码的可读性和可维护性,还需要考虑代码的性能。以下是一些代码优化的建议:
1. **减少不必要的计算**:避免在循环中进行重复的计算,尽量将计算结果缓存起来。
2. **使用合适的数据结构**:选择合适的数据结构可以提高代码的性能。例如,使用数组可以快速访问元素,使用链表可以方便地进行插入和删除操作。
3. **优化算法**:选择高效的算法可以显著提高程序的性能。例如,使用快速排序算法代替冒泡排序算法。
4. **避免内存泄漏**:在使用动态内存分配时,要确保及时释放不再使用的内存,避免内存泄漏。
#### 总结
本文介绍了程序组织与控制结构的相关知识,包括程序与诗歌、音乐的类比,编程的层次结构,控制结构的类型和使用方法,以及结构化编程、模块化编程、面向对象编程等概念。通过遵循良好的编程实践,使用标准的控制结构,添加注释,进行模块化设计等,可以提高代码的可读性、可维护性和性能。
在实际编程中,要根据具体的需求选择合适的编程方法和技术,不断学习和实践,提高自己的编程能力。
#### 流程图:程序执行流程
```mermaid
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(初始化变量):::process
B --> C{条件判断}:::decision
C -- 是 --> D(执行操作 1):::process
D --> E{另一个条件判断}:::decision
E -- 是 --> F(执行操作 2):::process
F --> G([结束]):::startend
E -- 否 --> H(执行操作 3):::process
H --> C
C -- 否 --> I(执行操作 4):::process
I --> G
```
这个流程图展示了一个典型的程序执行流程,包括初始化、条件判断、操作执行等步骤。通过流程图可以更直观地理解程序的控制结构和执行逻辑。
0
0
复制全文
相关推荐










