工程编程基础:使用C语言和Fortran
1. 引言
工程编程不仅仅局限于编写代码,它还涉及如何快速原型化程序以解决特定的工程应用问题。在现代工程领域,C语言和Fortran是两种主流的编程语言。C语言以其高效和可移植性著称,而Fortran则以其处理数学公式的能力而闻名。本文将深入探讨这两种语言的基础知识和实际应用,帮助工程师们更好地理解和使用它们。
计算机历史与冯·诺依曼架构
计算机的发展历程可以追溯到查尔斯·巴贝奇(Charles Babbage)的差分机和分析机。分析机是历史上第一台可以编程的机器,它使用打孔卡进行编程。尽管巴贝奇从未完成分析机,但他的工作奠定了现代计算机的基础。冯·诺依曼(John von Neumann)提出的存储程序计算机概念,即程序和数据可以同时存储在内存中,极大地推动了计算机科学的进步。现代计算机依然基于冯·诺依曼架构,其基本组成部分包括:控制单元、算术逻辑单元(ALU)、内存、输入单元和输出单元。
数据表示与二进制数
计算机使用二进制数(base-2)来存储和处理数据。二进制数由0和1组成,与我们熟悉的十进制数(base-10)不同。二进制数的表示方式如下:
| 十进制 | 二进制 |
|---|---|
| 0 | 0000 |
| 1 | 0001 |
| 2 | 0010 |
| 3 | 0011 |
| 4 | 0100 |
| 5 | 0101 |
例如,十进制数403可以表示为二进制数110010011。二进制数的大小取决于计算机架构和使用的语言。一个字节(8位)可以表示从0到255的十进制数。为了表示更大的数,计算机使用多字节表示法。
问题解决与程序开发
编程不仅仅是编写代码,更重要的是解决问题。以下是解决问题的基本步骤:
- 明确陈述问题 :清晰地定义需要解决的问题。
- 描述所需资源 :包括输入、输出、预期结果和所需的变量。
- 手动处理样本数据集 :通过手动计算验证算法的正确性。
- 开发算法 :使用流程图或伪代码表示算法。
- 编码算法 :将算法转化为程序代码。
- 测试代码 :使用编辑-编译-运行循环在各种数据集上测试代码。
例如,计算二次方程的根是一个常见的工程问题。假设方程为 ( ax^2 + bx + c = 0 ),我们需要系数( a )、( b )和( c )的值作为输入。然后我们可以应用熟知的代数公式来求解根。需要注意的是,如果用户输入的( a )为零,会导致除以零的错误。因此,我们需要使用
if
语句来检查输入是否合法。
#include <math.h>
int main() {
float a, b, c, x1, x2, D;
printf("请输入系数 a:");
scanf("%f", &a);
if (a == 0) {
printf("非法的二次方程!\n");
exit(0);
}
printf("请输入系数 b:");
scanf("%f", &b);
printf("请输入系数 c:");
scanf("%f", &c);
D = b * b - 4 * a * c;
if (D < 0) {
printf("复数根!\n");
exit(0);
}
x1 = (-b - sqrt(D)) / (2 * a);
x2 = (-b + sqrt(D)) / (2 * a);
printf("x1 = %f\n", x1);
printf("x2 = %f\n", x2);
exit(0);
}
2. 控制流
控制流结构是编程中的关键部分,它允许计算机根据条件执行不同的代码段。C语言提供了
if
、
for
、
do-while
和
while
等控制结构。这些结构使得程序可以根据不同的条件和循环需求灵活执行。
if
语句
if
语句用于根据条件执行特定代码段。例如,检查温度是否超过200度,然后激活冷却阀:
if (temperature > 200) {
activateCoolingValve();
}
if-else
语句允许选择两种不同的执行路径:
if (temperature > 200) {
activateCoolingValve();
} else {
deactivateCoolingValve();
}
for
循环
for
循环用于在特定范围内重复执行代码。例如,生成华氏温度到摄氏温度的转换表:
#include <stdio.h>
int main() {
float F, C;
for (F = 32.0; F <= 212.0; F += 10.0) {
C = (5.0 / 9.0) * (F - 32.0);
printf("%3.0f\t%3.0f\n", F, C);
}
return 0;
}
do-while
循环
do-while
循环先执行代码段,再进行条件检查。例如,计算一组数据的平均值:
#include <stdio.h>
int main() {
int count = 10;
float mean = 0.0, var = 0.0, dp;
int i = 1;
do {
scanf("%f", &dp);
mean += dp;
i++;
} while (i <= count);
mean /= count;
i = 1;
do {
scanf("%f", &dp);
var += (mean - dp) * (mean - dp);
i++;
} while (i <= count);
var /= count;
float stddev = sqrt(var);
printf("%f %f %f\n", mean, var, stddev);
return 0;
}
while
循环
while
循环在执行代码段之前进行条件检查。例如,生成华氏温度到摄氏温度的转换表:
#include <stdio.h>
int main() {
float F = 32.0, C;
while (F <= 212.0) {
C = (5.0 / 9.0) * (F - 32.0);
printf("%3.0f\t%3.0f\n", F, C);
F += 10.0;
}
return 0;
}
3. 类型、运算符和表达式
编程语言中的类型和运算符是构建有效程序的基础。C语言和Fortran支持多种数据类型和运算符,了解它们的特性可以帮助我们编写更高效、更可靠的代码。
数据类型
C语言支持四种基本数据类型:
int
(整型)、
float
(浮点型)、
double
(双精度浮点型)和
char
(字符型)。Fortran支持类似的数据类型,但还包括复数型和逻辑型。
int i = 20;
float x = 3.14;
double y = 1.23456789;
char c = 'A';
算术运算符
C语言和Fortran的算术运算符基本相同,但有一些细微差别。例如,C语言没有指数运算符(
**
),而Fortran没有模运算符(
%
)。C语言使用
pow(x, y)
函数来计算指数,而Fortran使用
**
运算符。
| C语言 | Fortran |
|---|---|
| + | + |
| - | - |
| * | * |
| / | / |
| % | ** |
逻辑和关系运算符
逻辑和关系运算符用于形成条件表达式。C语言中的逻辑运算符包括
&&
(与)、
||
(或)和
!
(非)。关系运算符包括
==
(等于)、
!=
(不等于)、
<
(小于)、
>
(大于)、
<=
(小于等于)和
>=
(大于等于)。
if (a > b && b < c) {
// 执行代码
}
Fortran中的逻辑运算符和关系运算符如下:
| C语言 | Fortran |
|---|---|
| && | .AND. |
| ! | .NOT. |
| == | .EQ. |
| != | .NE. |
| < | .LT. |
| > | .GT. |
| <= | .LE. |
| >= | .GE. |
类型转换
类型转换在混合使用不同类型的变量时非常重要。C语言中使用强制类型转换操作符
()
来显式转换类型,而Fortran则通过隐式类型提升规则来进行转换。
float result = (float)(5 / 2); // 结果为 2.0
Fortran中没有显式的类型转换操作符,但可以使用
REAL
和
INT
等函数来转换类型。
4. 函数
函数是编程中的重要组成部分,它们可以简化代码结构并提高代码的可重用性。C语言中的函数定义如下:
float FtoC(float temp) {
return (5.0 / 9.0) * (temp - 32.0);
}
Fortran中的函数定义如下:
REAL FUNCTION FTOC(TEMP)
FTOC = (5.0 / 9.0) * (TEMP - 32.0)
RETURN
END
库函数
库函数是预编译的函数,可以直接在程序中调用。C语言中常用的库函数包括
printf
、
scanf
、
pow
和
sqrt
。这些函数需要包含相应的头文件,例如
<stdio.h>
和
<math.h>
。
#include <math.h>
float result = pow(2.0, 3.0); // 计算 2 的 3 次方
Fortran中也有很多内置函数,例如
ABS
、
SIN
、
COS
和
SQRT
。
递归函数
递归函数是指函数调用自身的函数。递归在生成序列的算法中非常有用。例如,计算阶乘:
int factorial(int n) {
if (n == 0) return 1;
return n * factorial(n - 1);
}
递归函数需要一个终止条件,以防止无限递归。在上述例子中,当
n == 0
时,函数返回1,终止递归。
5. 指针和数组
指针和数组是C语言中的重要概念,它们允许直接访问和操作内存。
指针
指针是一个变量,其值为另一个变量的地址。指针可以用于修改函数中的变量值。
#include <stdio.h>
void modifyValue(float *ptr) {
*ptr = 10.0;
}
int main() {
float value = 5.0;
modifyValue(&value);
printf("Modified value: %f\n", value);
return 0;
}
数组
数组是一组同类型的变量,可以通过索引访问。C语言中的数组声明如下:
float x[5][5];
数组的索引从0开始,因此
x[0][0]
表示数组的第一个元素。数组可以用于存储和处理大量数据。
#include <stdio.h>
int main() {
float x[] = {1.0, 2.0, 3.0, 4.0, 5.0};
for (int i = 0; i < 5; i++) {
printf("x[%d] = %f\n", i, x[i]);
}
return 0;
}
Fortran中的数组声明如下:
DIMENSION X(5, 5)
数组的索引从1开始,因此
X(1, 1)
表示数组的第一个元素。Fortran数组的声明和使用方式与C语言类似,但索引从1开始。
6. 文件操作
文件操作允许程序访问硬盘、光盘驱动器或磁带驱动器中的数据。文件操作有两种主要模式:低级操作和高级操作(流)。
低级文件操作
低级文件操作直接处理数据字节。C语言中的低级文件操作函数包括
open
、
read
、
write
和
close
。
#include <stdio.h>
int main() {
int fd = open("data.txt", 0);
if (fd < 0) {
printf("文件打开错误...退出!\n");
exit(0);
}
char buffer[256];
int bytesRead = read(fd, buffer, 256);
if (bytesRead < 0) {
printf("文件读取错误...退出!\n");
exit(0);
}
printf("读取的数据: %s\n", buffer);
close(fd);
return 0;
}
高级文件操作(流)
高级文件操作使用流来处理数据。流是数据传输的路径,可以通过文件指针访问。
#include <stdio.h>
int main() {
FILE *filep = fopen("output.txt", "w");
if (filep == NULL) {
printf("文件打开错误...退出。\n");
exit(0);
}
for (float x = 0.0; x <= 1.0; x += 0.1) {
fprintf(filep, "%f\t%f\n", x, sqrt(x));
}
fclose(filep);
return 0;
}
Fortran中的文件操作使用
OPEN
、
READ
、
WRITE
和
CLOSE
语句。例如:
OPEN(UNIT=3, FILE='data.txt', STATUS='OLD')
READ(3, *) X, Y, Z
CLOSE(3)
Fortran的
OPEN
语句允许指定文件的状态(
OLD
表示打开现有文件,
NEW
表示创建新文件)。
7. 案例研究:潮汐建模程序
潮汐建模程序用于计算给定时间的潮汐高度或给定高度的潮汐时间。这个程序由多个函数组成,每个函数执行不同的任务。
潮汐高度计算
TideHeight
函数用于计算给定时间的潮汐高度。用户需要输入所需的时间(小时和分钟),然后程序调用
getParams
函数获取历书数据,最后计算并返回潮汐高度。
float TideHeight() {
int hour, minute;
float R, high1, high2, low1, low2, timeX;
printf("\n请输入您想知道潮汐的时间:");
scanf("%d", &hour);
printf("请输入您想知道潮汐的分钟:");
scanf("%d", &minute);
getParams(&high1, &high2, &low1, &low2);
R = high1 - high2;
return (((high1 - high2) / 2.0) - ((R / 2.0) * cos(180.0 * ((timeFloat(hour, minute) - low2) / (low1 - low2))));
}
潮汐时间计算
TideTime
函数用于计算给定高度的潮汐时间。用户需要输入所需的潮汐高度,然后程序调用
getParams
函数获取历书数据,最后计算并返回潮汐时间。
float TideTime() {
float hi, h2, tl, t2, hx;
printf("\nEnter the 潮汐高度 you want the 时间 for:");
scanf("%f", &hx);
getParams(&hi, &h2, &tl, &t2);
return (t2 + (tl - t2) * (acos(1.0 - 2.0 * ((hx - h2) / (hi - h2))) / 180.0);
}
获取历书数据
getParams
函数用于从用户获取必要的年鉴数据。用户需要输入当天的高低潮时间和高度。
void getParams(float *H1, float *H2, float *T1, float *T2) {
int hour, minute;
printf("输入高潮的高度:");
scanf("%f", H1);
printf("输入高潮发生的时间(小时):");
scanf("%d", &hour);
printf("输入高潮发生的分钟:");
scanf("%d", &minute);
*T1 = timeFloat(hour, minute);
printf("输入低潮的高度:");
scanf("%f", H2);
printf("输入低潮发生的时间(小时):");
scanf("%d", &hour);
printf("输入低潮发生的分钟:");
scanf("%d", &minute);
*T2 = timeFloat(hour, minute);
}
潮汐时间转换
timeFloat
函数将小时和分钟转换为浮点数格式的时间。
float timeFloat(int h, int m) {
return ((float)h + m / 60.0);
}
用户交互
主函数通过
getch
函数获取用户输入,并根据输入调用相应的函数。
#include <math.h>
#include <stdio.h>
void main() {
while (1) {
printf("\n潮汐时间,高度或退出 (t/h/q)?");
switch (getch()) {
case 't':
case 'T':
TimeOut(TideTime());
if (!more()) exit(0);
break;
case 'h':
case 'H':
printf("\n潮高将会是: %f 英尺。\n", TideHeight());
if (!more()) exit(0);
break;
case 'q':
case 'Q':
exit(0);
default:
printf("\n请输入 't', 'h' 或 'q'。");
}
}
}
流程图
以下是潮汐程序的流程图,展示了各个函数之间的调用关系。
graph TD;
A[主函数] --> B[获取用户输入];
B --> C{用户选择};
C -->|t/T| D[调用 TideTime];
C -->|h/H| E[调用 TideHeight];
C -->|q/Q| F[退出程序];
D --> G[计算潮汐时间];
E --> H[计算潮高];
G --> I[显示结果];
H --> J[显示结果];
I --> K[询问是否继续];
J --> K;
K -->|是| B;
K -->|否| F;
通过这些函数和流程图,我们可以看到潮汐建模程序如何根据用户输入计算潮汐高度或时间。这个程序展示了如何使用多个函数协同工作,以实现复杂的功能。
以上内容涵盖了从计算机历史、基本编程结构到具体案例研究的多个方面,帮助工程师们更好地理解和应用C语言和Fortran进行编程。接下来,我们将探讨如何使用控制台绘图来增强数据展示。
8. 控制台绘图
控制台绘图是一种将数据以图形方式展示的技术,它可以在没有图形界面的情况下,通过字符和符号在控制台上绘制图表。这种技术在工程实践中非常有用,尤其是在需要快速展示数据趋势或模式时。通过控制台绘图,工程师可以更直观地分析数据,而无需依赖复杂的图形库。
正弦波绘制
为了说明控制台绘图的实现,我们以绘制正弦波为例。正弦波是工程中常见的波形,它在很多应用场景中都有重要作用。我们可以使用简单的C语言代码在控制台上绘制正弦波。
正弦波绘制原理
正弦波的值在-1到1之间变化。为了在控制台上绘制正弦波,我们需要将这些值映射到控制台的宽度。假设我们使用40个空格来表示一个周期,当正弦值为-1时,不输出任何空格;当值为0时,输出20个空格;当值为1时,输出40个空格。
#include <stdio.h>
#include <math.h>
#define PI 3.1416
int main() {
float w, theta, Tstart, Tend, dt, T;
int P;
printf("请输入角频率、相位、时间起始值、时间结束值和递增值(以空格分隔):\n");
scanf("%f %f %f %f %f", &w, &theta, &Tstart, &Tend, &dt);
for (T = Tstart; T <= Tend; T += dt) {
printf("%5.2f ", T);
P = (int)(20 * sin(w * T + theta)) + 20;
for (int i = 0; i < P; i++) {
printf(" ");
}
printf("*\n");
}
return 0;
}
流程图
以下是正弦波绘制程序的流程图,展示了如何根据用户输入和正弦函数值在控制台上绘制波形。
graph TD;
A[主函数] --> B[获取用户输入];
B --> C[初始化变量];
C --> D[循环遍历时间区间];
D --> E{计算正弦值};
E -->|P = (int)(20 * sin(w * T + theta)) + 20| F[输出时间值];
F --> G[根据P值输出空格];
G --> H[输出波形符号 *];
H --> I[换行];
I --> J{是否到达时间结束值};
J -->|否| D;
J -->|是| K[结束程序];
添加坐标轴
为了使图表更易于阅读,我们可以在图表中添加坐标轴。具体来说,我们可以在每行的开头输出时间值,并在图表的中心位置添加一个”+”符号作为x轴标记。
#include <stdio.h>
#include <math.h>
#define PI 3.1416
int main() {
float w, theta, Tstart, Tend, dt, T;
int P, spc;
printf("请输入角频率、相位、时间起始值、时间结束值和递增值(以空格分隔):\n");
scanf("%f %f %f %f %f", &w, &theta, &Tstart, &Tend, &dt);
printf("t\n");
for (T = Tstart; T <= Tend; T += dt) {
printf("%5.2f ", T);
P = (int)(20 * sin(w * T + theta)) + 20;
for (spc = 0; spc < 40; spc++) {
if (spc == P) {
printf("*");
continue;
}
if (spc == 20) {
printf("+");
continue;
}
printf(" ");
}
printf("\n");
}
return 0;
}
测试与优化
通过上述代码,我们可以在控制台上绘制正弦波,并添加坐标轴。为了测试程序的正确性,我们可以使用一些已知的正弦波数据点进行验证。例如,使用角频率为1,相位为0,时间范围从0到6.28,递增值为0.25,我们可以得到以下输出:
t
0.00 * +
0.25 * *
0.50 * *
0.75 * *
1.00 * *
1.25 * *
1.50 * *
1.75 * *
2.00 * +
2.25 * *
2.50 * *
2.75 * *
3.00 * *
3.25 * *
3.50 * *
3.75 * *
4.00 * +
4.25 * *
4.50 * *
4.75 * *
5.00 * *
5.25 * *
5.50 * *
5.75 * *
6.00 * +
6.25 * *
进一步优化
为了使程序更友好,我们可以改进用户界面,允许用户选择其他函数进行绘图。例如,用户可以选择绘制余弦波或其他自定义函数。我们还可以通过添加颜色或符号来增强图表的可视化效果。
案例研究:可视化程序
可视化程序涉及将数据以图形方式展示,以便于分析或设计目的。在工程实践中,可视化是非常重要的,因为工程师每天都要处理大量的数据。通过控制台绘图,我们可以快速生成简单的图表,帮助工程师更好地理解数据。
正弦和余弦函数的可视化
我们可以将正弦和余弦函数的值绘制在同一张图表上,以展示它们之间的关系。以下是绘制正弦和余弦函数的代码:
#include <stdio.h>
#include <math.h>
#define PI 3.1416
int main() {
float w, theta, Tstart, Tend, dt, T;
int P_sin, P_cos, spc;
printf("请输入角频率、相位、时间起始值、时间结束值和递增值(以空格分隔):\n");
scanf("%f %f %f %f %f", &w, &theta, &Tstart, &Tend, &dt);
printf("t\n");
for (T = Tstart; T <= Tend; T += dt) {
printf("%5.2f ", T);
P_sin = (int)(20 * sin(w * T + theta)) + 20;
P_cos = (int)(20 * cos(w * T + theta)) + 20;
for (spc = 0; spc < 40; spc++) {
if (spc == P_sin) {
printf("S");
continue;
}
if (spc == P_cos) {
printf("C");
continue;
}
if (spc == 20) {
printf("+");
continue;
}
printf(" ");
}
printf("\n");
}
return 0;
}
流程图
以下是正弦和余弦函数可视化程序的流程图,展示了如何根据用户输入和函数值在控制台上绘制波形。
graph TD;
A[主函数] --> B[获取用户输入];
B --> C[初始化变量];
C --> D[循环遍历时间区间];
D --> E{计算正弦和余弦值};
E -->|P_sin = (int)(20 * sin(w * T + theta)) + 20| F[计算P_cos];
F --> G[输出时间值];
G --> H[根据P_sin和P_cos值输出符号];
H --> I[换行];
I --> J{是否到达时间结束值};
J -->|否| D;
J -->|是| K[结束程序];
测试与应用
通过上述代码,我们可以在控制台上绘制正弦和余弦波形,并添加坐标轴。为了测试程序的正确性,我们可以使用一些已知的正弦和余弦波数据点进行验证。例如,使用角频率为1,相位为0,时间范围从0到6.28,递增值为0.25,我们可以得到以下输出:
t
0.00 S +C
0.25 S C
0.50 S C
0.75 S C
1.00 S C
1.25 S C
1.50 S C
1.75 S C
2.00 S +C
2.25 S C
2.50 S C
2.75 S C
3.00 S C
3.25 S C
3.50 S C
3.75 S C
4.00 S +C
4.25 S C
4.50 S C
4.75 S C
5.00 S C
5.25 S C
5.50 S C
5.75 S C
6.00 S +C
6.25 S C
应用场景
控制台绘图在很多工程应用场景中都非常有用。例如,在信号处理中,工程师可以通过控制台绘图快速查看信号的波形,而无需依赖复杂的图形界面。在教学环境中,控制台绘图也可以帮助学生更好地理解函数的变化趋势。
具体应用步骤
- 确定绘图范围 :根据需要绘制的函数,确定时间范围和递增值。
- 计算函数值 :根据用户输入的时间范围和递增值,计算每个时间点的函数值。
- 映射到控制台宽度 :将函数值映射到控制台的宽度,确定每个时间点对应的字符位置。
-
输出波形符号
:根据映射结果,输出波形符号(如
*、S、C等)。 -
添加坐标轴
:在图表的中心位置添加坐标轴标记(如
+),以增强图表的可读性。
进一步扩展
为了使控制台绘图程序更具实用性,我们可以添加更多功能。例如,允许用户选择不同的函数进行绘图,或支持多条波形的叠加显示。我们还可以通过读取文件中的数据来生成动态图表,帮助工程师实时监控数据变化。
案例研究:可视化程序的应用
可视化程序不仅可以用于绘制正弦和余弦波形,还可以用于其他工程应用。例如,在机械工程中,我们可以绘制振动信号的波形;在电气工程中,我们可以绘制电压和电流的变化趋势。通过可视化,工程师可以更直观地分析数据,发现潜在的问题。
示例:振动信号可视化
假设我们有一个振动信号的数据文件
vibration_data.txt
,文件中每行包含一个时间点和对应的振动幅度。我们可以编写一个程序来读取这些数据,并在控制台上绘制振动信号的波形。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main() {
FILE *filep = fopen("vibration_data.txt", "r");
if (filep == NULL) {
printf("文件打开错误...退出。\n");
exit(0);
}
float time, amplitude;
int P;
printf("t\n");
while (fscanf(filep, "%f %f", &time, &litude) != EOF) {
printf("%5.2f ", time);
P = (int)(20 * amplitude) + 20;
for (int i = 0; i < 40; i++) {
if (i == P) {
printf("*");
continue;
}
if (i == 20) {
printf("+");
continue;
}
printf(" ");
}
printf("\n");
}
fclose(filep);
return 0;
}
数据文件格式
假设
vibration_data.txt
文件的格式如下:
0.0 0.0
0.1 0.1
0.2 0.2
0.3 0.3
0.4 0.4
0.5 0.5
0.6 0.4
0.7 0.3
0.8 0.2
0.9 0.1
1.0 0.0
1.1 -0.1
1.2 -0.2
1.3 -0.3
1.4 -0.4
1.5 -0.5
1.6 -0.4
1.7 -0.3
1.8 -0.2
1.9 -0.1
2.0 0.0
通过读取这些数据,我们可以生成振动信号的波形图表,帮助工程师分析振动信号的特性。
总结
控制台绘图是一种简单而有效的数据可视化方法,它可以帮助工程师快速理解和分析数据。通过合理的算法设计和代码实现,我们可以生成各种类型的图表,满足不同的工程需求。控制台绘图不仅适用于教学和实验环境,还可以用于实时数据监控和故障诊断。
通过上述内容,我们详细探讨了从计算机历史到控制台绘图的多个方面,帮助工程师们更好地理解和应用C语言和Fortran进行编程。控制台绘图作为一种实用的可视化工具,可以显著提高数据的可读性和分析效率。希望这些内容能够为工程师们提供有价值的参考和启发。
超级会员免费看
2106

被折叠的 条评论
为什么被折叠?



