c语言


1.引言

1.1 程序设计语言的功能

  • 1、数据表达(Data Representation)
  • 2、流程控制(Flow Control)
    • 1)顺序控制结构(Sequential Control Structure):一个程序模块执行完后,按自然顺序执行下一个模块
    • 2)分支控制结构(Branch Control Structure):又称选择结构。计算机在执行程序时,一般按语句的顺序执行,但在许多情况下需要根据不同的条件来选所要执行的模块,即判断某种条件,如果条件满足就执行某个模块,否则就执行另一个模块。
    • 3)循环控制结构(Loop Control Structure):有时,需要反复地执行某个模块。重复执行这些模块一般是有条件的,即检测某些条件,如果条件满足就重复执行相应的模块。

1.1.1 C 语言的主要“单词”

  • 1)标识符
    • C 语言的标识符由字母、数字和下划线组成,其中第一个字符必须是字母或下划线。
    • (C 语言中,最主要的标识符是保留字和用户自定义的标识符)
      • 1))保留字,又称关键字,是 C 语言规定的、赋予特定含义和有专门有途的标识符,它们主要与数据类型和语句有关。如 int(整数类型)、float(实数类型)、char(字符类型)、typedef(自定义类型),以及与语句相关的 if、else、while、for、break 等。
      • 2))用户自定义标识符。用户自定义标识符包括程序中定义的变量名、数据类型名、函数名和符号常量名。一般来说,为了便于程序阅读,经常取有意义的英文单词作为用户自定义标识符(如前面程序中顶i一的 n、fact 等)。
  • 2)常量
    • 常量是有数据类型的,例如,整数常量 123、实型常量 12.34、字符常量 ‘a’、字符串常量 “hello world!”等。
  • 3)运算符
    • 运算符表示对各种数据类型数据对象的运算。如,+、-、*、/、%、>、>=、==、=(赋值)等。运算一般多为双目运算(涉及两个运算对象),也有单目(涉及一个运算对象)和三目(涉及三个运算对象)运算。
  • 4)分隔符
    • 如;、[、]、(、)、和 # 等都是分隔符

1.1.2 C语言的主要语法单位

  • 1)表达式
    • 运算符与运算对象(可以是常量、函数、变量等)的有意义组合就形成了表达式,如 2 + 3 * 4 和 i + 2 < j等。表达式中可以包含多种数据类型的运算符,运算符有运算优先级。例如,i + 2 < j 中,+ 比 < 先算。
  • 2)变量定义
    • 变量也有数据类型,所以在定义变量时要说明相应变量的类型。变量的类型不同,它在内存中所占的存储空间的大小也会有所不同。变量定义的最基本形式就是:类型名 变量名;
  • 3)语句
    • 语句是程序最基本的执行单位,程序的功能就是通过一系列语句来实现的。C 语言中的语句有多种形式。
      • 1))最简单的语句(表达式语句)
        • 最简单的语句就是表达式加分号 “;”。在 C 语言中赋值也被认为是一种运算,如 i = j + 2(把 j 加 2 的结果赋给变量 i)就是一个包含 + 和 = 两种运算表达式, + 的优先级较高。在上述表达式后加 “;”,就这样成了一个执行赋值过程的语句。
      • 2))分支语句
        • 分支语句实现分支控制过程,根据不同的条件执行不同的语句(或语句模块)。具体有两种形式,即双路分支的 if-else 语句与多路分支的 switch 语句。
      • 3))循环语句
        • C 语言实现循环控制的过程具体有 3 种形式,即 while 语句、for 语句和 do-while 语句。
      • 4))复合语句(Compound statement)
        • 用一对大括号 {} 将若干语句顺序组合在一起就形成了一个复合语句。
        • 例如:{ sum = sum + i; i = i + 1;}。
  • 4)函数定义与调用
    • 函数是完成特定任务的独立模块,是 C 语言唯一的种子程序形式。函数的目的通常是接收 0 个或多个数据(称为函数的参数),并返回 0 个或 1 个结果(称为函数的返回值)。函数的使用主要涉及函数的定义与调用。
    • 函数定义的主要内容是用过编写一系列语句来规定其所完成的功能。完整的函数定义涉及函数头和函数体。其中,函数头包括函数的返回值类型、函数名、参数类型;而函数体是一个程序模块,规定了函数所具有的功能。函数调用则通过传递函数的参数并执行函数定义所规定的程序过程,以实现相应功能。以下是函数定义的一个简单的例子。
    • 函数 max 求两个整数(作为参数)的较大值(作为返回值)。
1
2
3
4
5
6
7
int max (int a, int b) /* 函数头:函数类型 函数名 (函数参数列表) */
{ /* 函数体开始 */
int x; /* 函数种要用到的临时变量 */
if (a>b) x = a; /* 判断 a、b 的大小,将 x 赋值为值大的一个 */
else x = b;
return x; /* 结束函数调用并返回 */
} /* 函数体结束 */
  • 5)输入与输出
    • C 语言没有输入输出语句,它通过调用系统库函数种的有关函数(如 printf() 和 scanf() 函数)实现数据的输入与输出。这种处理方式为 C 语言在不同硬件平台上的可移植性提供了良好的基础。
    • 例如:printf(“This integer value is %d”, 123);
    • 将输出:This integer value is 123
    • printf() 函数的第一个参数是输出格式说明,%d 表示将后面的数据 123 按十进制整数形式输出,其他字符串按原样输出。又如,以下输入语句:
    • scanf(“%d”, &i);
    • 将从键盘输入中读进一个整数,并把它存到变量 i 中。其中,scanf() 函数的第一个参数是输入格式说明。

1.2 C 语言的发展历史与特点

C 语言的特点

  • 1)C 语言是一种结构化语言
    • C 语言的主要成分是函数。函数是 C 语言程序的基本结构模块,程序的许多操作可由不同功能的函数有机组装而成,从而容易达到结构化程序设计中模块的要求。另外,C 语言还提供了一套完整的控制语句(如循环、分支等)和构造数据类型机制(如结构、数组等),使程序流程与数据描述也具有良好的结构性。
  • 2)C 语言语句简洁紧凑,使用方便灵活
    • C 语言一共只有 32 个保留字和 9 种控制语句,程序书写形式自由,压缩了一切不必要的成分。
    • 例如,用大括号 { 和 } 代替复合语句的开始与结束,用运算符 ++ 和 – 表示加 1 和减 1,用三目运算符 ?: 来表示一个简单的 if-else 语句,一行中可书写多个语句,一个语句可书写在不同行上,可采用宏定义和文件包含等预处理语句,等等。这些都使 C 语言显得非常简洁紧凑。
  • 3)C 语言程序易于移植
    • C语言将与硬件有关的因素从语言主体中分离出来,通过库函数或其他实用程序实现它们。这特别体现在输入输出操作上,因为 C 语言不把输入输出作为语言的一部分,而是作为库函数由具体实用程序实现,这大大提高了程序的可移植性(Portability)。
  • 4)C 语言有强大的处理能力
    • 由于 C 语言引入了结构、指针(Pointer)、地址、位运算、寄存器存储等功能,在许多方面具有汇编语言的特点,从而大大提高了语言的处理能力。
  • 5)生成的目标代码质量高,运行效率高
    • 用 C 语言编写的程序,经编译后生成的可执行代码比用汇编语言直接编写的代码运行效率仅低 15% ~ 20%。这是其他高级语言无法比拟的。
    • 当然,C 语言也有一些不足之处,这主要表现在数据类型检查不严格,表达式出现二义性,不能自动检查数据越界,初学者较难掌握运算符的优先级与结核性的概念等。

1.3 实现问题求解的过程

1.3.1 问题分析与算法设计

题外话1(算法):算法(Algorithm)是一组明确的解决问题的步骤,它产生结果并可在有限的时间内终止。可以用多种方式来描述算法,包括使用自然语言、伪代码(Pseudo Code)或流程图(Flow Chart)。

题外话2(流程图):流程图是算法的图形表示法,它用图的形式掩盖了算法的所有细节,只显示算法从开始到结束的整个过程。

1.3.2 编辑程序

当确定了解决问题的步骤后,就可以开始编写程序了。一般是在编译环境中,应用其中的编辑功能直接来编写程序,生成源程序(对 C 语言来说,一般源程序的后缀为 c)。

1.3.3 编译

当编辑好程序后,下一步的工作就是应用该语言的编译程序对其进行编译,以生成二进制代码表示的目标程序(一个二进制文件,文件后缀为 obj)。

实际上,还不能直接运行该目标程序,它需要与编程环境提供的库函数进行连接(Link),形成可执行的程序(文件后缀为 exe)。

当然,如果程序有语法错误,编译程序就会指出该语法错误所在,而不形成二进制代码。

1.4.4 运行与测试

当程序通过了语法检查、编译生成执行文件后,就可以在编译环境或操作系统环境中运行(Run)该程序。

当然,一旦程序中存在语义错误(逻辑错误),程序运行时所产生的结果有可能不是想要的结果。

如果程序有语义错误就需要对程序进行调试。调试是在程序中查找错误并修改错误的过程。调试最主要的工作是找出错误发生的地方。

一般程序的编程环境都提供相应的调试手段。调试最主要的方法是:设置断点并观察变量。

1)设置断点(Breaking Point Setting):可以在程序的任何一个语句上做断点标记,将来程序运行到这里时会停下来。

2)观察变量(Variable Watching):当程序运行到断点的地方停下来后,可以观察各种变量的值,判断此时的变量值是不是所希望的。如果不是,说明该断点之前肯定有错误发生。这样,就可以把找错的范围集中在断点之前的程序段上。

另外,还有一种常用的调试方法是单步跟踪(Trace Step by Step),即一步一步跟踪程序的执行过程,同时观察变量的变化情况。

2. 用 C 语言编写程序

2.1 在屏幕上显示 Hello World!

例 2-1:在屏幕上显示一个语句 “Hello World!”

1
2
3
4
5
6
7
/* 显示 "Hello World!" */				/* 注释文本 */
#include<stdio.h> /* 编译预处理命令 */
int main (void) /* 定义主函数 main() */
{
printf("Hello World!\n"); /* 调用 printf() 函数输出文字 */
return 0; /* 返回一个整数 0 */
}

注释(第一行):用来说明程序的功能,注释文本必须在 /* 和 */ 之间。注释是对程序的注解,它可以是任何可显示字符,不影响程序的编译和运行,程序编译时会忽略这些内容。

#include<stdio.h>(第二行):它是编译预处理命令,因为后面调用的 printf() 函数是 C 语言提供的标准输出函数,在系统文件 stdio.h 中声明。注意:编译预处理命令的末尾不加分号。

int main (void)(第三行):定义了一个名字为 main 的函数,该函数的返回值是整型数(int),参数在函数名后的一对括号中定义,这里的关键字 void 表示 main() 函数不需要参数。在 C 语言中,main() 是一个特殊函数,被称为“主函数”,任何一个程序都必须有而且只能有一个 main() 函数,当程序运行时,首先从 main() 函数开始执行。

一对大括号把构成函数的语句括起来,称为函数体。

例 2-1 的函数体共有两条语句(第五第六行):第一条语句为:printf(“Hello World!\n”); 它由两部分组成:函数调用和分号。printf(“Hello World!\n”) 是一个函数调用,它的作用是将双引号中的内容原样输出,\n 是换行符,即在输出 Hello World! 后换行;而分号表示该语句的结束。

C 语言中的所有语句都必须以分号结束。程序中所有的标点符号都是英文符号。

main() 函数的最后一条语句是:

**return 0;**它结束 main() 函数的运行,并向系统返回一个整数 0,作为程序的结束状态。由于 main() 函数的返回值是整型数,因此,任何整数都可以作为返回值。按照惯例,如果 main() 函数返回 0,说明程序运行正常,返回其他数字则用于表示各种不同的错误情况。系统可以通过检查返回值来判断程序的运行是否成功。

2.2 求华氏温度 100°F 对应的摄氏温度

2.2.1 程序解析

例 2-3:求华氏温度 100°F 对应的摄氏温度。计算公式如下:

c = 5 * ( f - 32 ) / 9 式中:c 表示摄氏温度,f 表示华氏温度。

源程序:

1
2
3
4
5
6
7
8
9
10
11
12
/* 将华氏温度转换为摄氏温度 */
#include<stdio.h>
int main (void)
{
/* 定义两个整型变量,celsius 表示摄氏度,fahr 表示华氏度 */
int celsius, fahr;
/* 空行,用于分隔变量定义和可执行语句 */
fahr = 100; /* 对变量 fahr 赋值 */
celsius = 5 * ( fahr -32 ) / 9; /* 温度转换计算 */
printf("fahr = %d, celsius = %d\n", fahr, celsius);
return 0;
}

运行结果:fahr = 100, celisius = 37;

程序中调用 printf() 函数输出结果时,将双引号内除 %d 以外的内容原样输出,并在第一个 %d 的位置上输出变量 fahr 的值,在第二个 %d 的位置上输出变量 celsius 的值。

可见,使用 printf() 函数不但能够输出固定不变的内容,如例 2-1 中的 Hello World!,还可以输出变量的值,如本例中变量 hahr 和 celsius 的值。

在程序中,适当地添加空行和空格,使程序清楚易读。

2.2.2 常量、变量和数据类型

例 2-3 中使用了哪些数据?它们的类型是什么?

在 C 语言中,数据有常量和变量之分。在程序运行过程中,其值不能被改变的量称为常量,其值可以被改变的量称为变量。

常量和变量都有类型,常量的类型通常由书写格式决定。

变量定义的一般形式是: 类型名 变量名表;

类型名必须是有效的数据类型,变量名表中可以有一个变量名或逗号间隔的多个变量名。

例如:

1
2
3
int celsius, fahr;				/* 定义两个整型变量 celsius 和 fahr,用于存放整数 */
float x; /* 定义一个单精度浮点型变量 x,用于存放实数 */
double area, length; /* 定义两个双精度浮点型变量 area 和 length,用于存放实数。 */

C 语言中最常用的数据类型包括 int (整型)、char(字符型)、float(单精度浮点型)和 double(双精度浮点型)。其中 float 和 double 都是浮点型(实型),用于存放浮点数(实数),区别在于 double 型数据占用空间更大,精度更高,取值范围更大。

tips:给变量起名尽量做到 “见名知义”,使别人一看就知道它的含义。变量名中的英文字母一贯用小写字母。

C 语言中的变量在使用前,都必须先定义。定义变量时要确定变量的名字和数据类型。每个变量必须有一个名字作为标识,变量名代表内存中的一个存储单元,用于存放该变量的值,而该存储单元的大小由变量的类型决定。

整型变量所需存储空间与编译系统有关,在 Visual C++ 中,int 型变量占用 4 个字节。定义变量后,就可以使用它,即使用该变量所代表的存储单元。

例如: fahr = 100;

表示将 100 写入 fahr 所代表的存储单元中。

C 语言中变量的含义和数学中变量的含义不同。C 语言中的变量代表保存数据的存储单元,而数学中的变量代表未知数。例如, x = x + 1 在数学上是错误的,在 C 语言中却表示把变量 x 的值加 1,然后再保存到 x 中。

C 语言区分大小写字母,它认为 fahr 和 Fahr 是不一样的。

2.2.3 算术运算和赋值运算

  • 1)算术运算
    • 算数运算包括加、减、乘、除、求余和其他一些操作,前者对应双目运算符,双目运算符需要两个操作数。
运算符 + - * / %
名称 模(求余)
优先级

用算数运算符将运算对象连接起来的符合 C 语言语法规则的式子称为算数表达式。例如:例 2-3 中进行温度转换计算时,就用到了算数表达式 5 * (fahr - 32) / 9。


在使用算数运算符时,请注意以下几点。

  • 1))如果两个整型数据作除法运算,其结果一定是整数

    • 例如,表达式 10 / 4 的值为 2,1 / 3 的值为 0。
  • 2))求余运算符取整型数据相除的余数,它不能用于实型数据的运算。

    • 例如:表达式 5 % 6 的值为 5,9 % 4 的值为 1, 100 % 4 的值为 0(表示 100 能被 4整除)。
  • 3))+ 和 - 还可以作为单目运算符,用于表达数值常量的符号,如 +10 和 -10。

  • 4))双目运算符两侧操作数的类型要相同,否则,系统自动进行类型转换,使它们具有相同的类型,然后再运算。

    • 例如,求解表达式 10.0 / 4 时,先自动将其转换为 10.0 / 4.0,再算出其值为 2.5。
  • 2)赋值运算

    • C 语言将赋值作为一种运算,定义了赋值运算符 = ,它的作用是把一个表达式的值赋给一个变量,如 fahr = 100。赋值运算符的优先级比算数运算符低。
    • 用赋值运算符将一个变量和一个表达式连接起来的式子称为赋值表达式
    • 赋值表达式的简单形式是:
      • 变量 = 表达式;
      • 在例 2-3 中,fahr = 100 和 celsius = 5 * (fahr -32) / 9 都是赋值表达式。
    • 赋值运算符的左边必须是一个变量。
    • 赋值表达式的基本运算过程是:
      • 1))计算赋值运算符右侧表达式的值。
      • 2))将赋值运算符右侧表达式的值给赋值运算符左侧的变量。
    • 在赋值运算时,如果赋值运算符两侧的数据类型不同,在上述运算过程的第 2))步,系统首先将赋值运算符右侧表达式的类型自动转换成赋值运算符左侧变量的类型,再给变量赋值。

2.2.4 格式化输出函数 printf()

在 C 语言中,数据的输出时用过函数调用实现的。这里先简要介绍常用的格式化输出函数 printf() 的基本用法,它是系统提供的库函数,在系统文件 stdio.h 中声明,所以在源程序开始时要使用编译预处理命令#include<stdio.h>、


函数 printf() 的一般调用格式为:

1
printf(格式控制字符串, 输出参数1, ..., 输出参数n);

格式控制字符串用双引号括起来,表示输出的格式;而输出参数则时一些要输出的数据,这些数据可以是常量、变量或表达式。

格式控制字符串中包含两种信息,格式控制说明普通字符。

  • 1)格式控制说明:按指定的格式输出数据,它包含以 % 开头的格式控制字符,不同类型的数据采用不同的格式控制字符。例如,int 型数据使用 %d,float 和 double 型数据都使用 %f。
  • 2)普通字符:在输出数据时,需要原样输出的字符。例如:
1
printf("fahr = %d, celsius = %d\n", fahr, celsius);

在格式控制字符串中包括格式控制说明(两个 %d)和一些普通字符(如等号、逗号和换行符)。输出时,所有普通字符都被原样输出,在两个 %d 的位置上,依次输出变量 fahr 和 celsius 的值。

printf() 函数的输出参数必须和格式控制字符串中的控制说明相对应,并且它们的类型、个数和位置要一一对应。例如,fahr 和 celsius 都是整型变量,输出时要用 %d,且 fahr 和第一个 %d 对应,celsius 和第二个 %d 对应。celsius 和 第二个 %d 对应。

2.3 计算分段函数

2.3.1 程序解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 计算二分段函数 */
#include<stdio.h>
int main (void)
{
double x, y; /* 定义两个双精度浮点型变量 */

printf("Enter x (x>=0);\n"); /* 输入提示 */
/* 调用 scanf() 函数输入数据,变量名 x 前加 &,%lf 中的 l 是 long 的首字母 */
scanf("%lf", &x);
/* if-else 语句 */
if(x<=15) {
y = 4 * x / 3;
}
else {
y = 2.5 * x = 10.5;
}
printf("y = f(%f) = %.2f \n", x, y); /* 调用 printf() 函数输出结果 */

return 0;
}

程序中调用 scanf() 函数读入 x;再根据事先设定的条件,选择分段函数中的相应公式计算 y 的值,这是经典的二分支结构,用 if-else 语句实现;最后调用 printf() 函数输出结果。

printf() 函数的格式控制说明 %f 指定以小数形式输出浮点型数据(保留 6 位小数),而 %.2f 则指定输出时保留两位小数。

2.3.2 关系运算

例 2-4 程序的 if-else 语句中,用 x <= 15 比较 x 和 15 的大小,这是一种关系运算。在 C 语言中,关系运算就是比较运算,对两个操作数进行比较,运算的结果是“真”或“假”。

C 语言共提供了 6 种关系运算符,它们都是双目运算符。用关系运算符将两个表达式连接起来的式子,称为关系表达式。例如,x <= 15、 x == 0 和 x != 10 都是合法的关系表达式。

运算符 < <= > >= == !=
名称 小于 小于或等于 大于 大于或等于 等于 不等于

== 是关系运算符,用于比较两个操作数是否相等;而 = 是赋值运算符,表示对变量赋值。

在 C 语言中,可以用关系表达式来描述给定的一些条件。

2.3.3 if-else 语句

if-else 语句的一般形式为:

1
2
3
4
if(表达式)
语句1;
else
语句2;

该语句用于实现分支结构,根据表达式的值选择语句 1 或 语句 2 中的一条执行。

针对给定的问题编写 C 语言程序后,需要通过运行程序来发现程序中存在的错误,并改正错误,也就是测试程序和调试程序。具体做法是,精心设计一批测试用例(包括输入数据和与之相应的预期输出结果),然后分别用这些测试用例运行程序,看程序的实际运行结果与预期输出结果是否一致,这就是软件测试的基本思想。如果发现运行结果有错误,就要调试程序,即查找并改正程序中的错误。程序的测试和调试需要反复进行。

显然,程序测试的时候,使用的测试用例越多,就越容易发现隐藏的错误,但是,穷举所有的测试用力在实际应用中是不可行的。因此通常可以根据程序的逻辑结果和功能,设计一些有代表性的测试用例,测试用例的格式是 [输入数据, 预期输出结果]。

2.3.4 格式化输入函数 scanf()

与 printf() 函数类似,scanf() 函数是系统提供的用于输入的库函数,也在系统 stdio.h 中声明。该函数用于从键盘输入数据,其调用格式与函数 printf() 类似:

1
scanf(格式控制字符串, 输入参数1, ..., 输入参数n);

格式控制字符串表示输入的格式,输入参数是变量地址(变量名前加 &)。

输入参数的形式为:变量名前加 &,如 &x。

格式控制字符串中包含两种信息:格式控制说明和普通字符。

  • 1)格式控制说明:按指定的格式读入数据,它包含以 % 开头的格式控制字符,不同类型的数据采用不同的格式控制字符。int 型数据使用 %d,float 型数据使用 %f,而 double 型数据使用 %lf。

double 型数据使用格式控制说明 %lf,其中的 l 是 long 的首字母,不是数字 1

scanf() 函数的输入参数必须和格式控制字符串中的格式控制说明相对应,并且它们的类型、个数和位置要一一对应。

  • 2)普通字符:在输入数据时,需要原样输入的字符。

2.3.5 常用数学函数

C 语言处理系统提供了许多事先编好的函数,供用户在编程时调用,这些函数称为库函数,其中一些必须的信息在相应的系统文件(头文件)中声明。所以,用户调用库函数时,一定要用 #include 命令将相应的头文件包含到源程序中。例如,调用输入输出函数,要加 #include<stdio.h>;调用数学函数,则需加入 #include<math.h>。


常用的数学函数有:

  • 1)平方根函数 sqrt(x):计算根号x。如 sqrt(4.0) 的值为 2.0。
  • 2)绝对值函数 fabs(x):计算 |x|。如 fabs(-3.56) 的值为 3.56。
  • 3)幂函数 pow(x, n):计算 x 的 n 次幂。如 pow(1.1, 2) 的值为 1.21(即 1.1²)。
  • 4)指数函数 exp(x):计算 e 的 x 次幂。如 exp(2.3) 的值为 9.974182。
  • 5)以 e 为底的对数函数 log(x):计算 lnx。如 log(123.45) 的值为 4.815836。

例 2.5 计算银行存款的本息。输入存款金额 money、存期 year 和年利率 rate,根据下列公式计算存款到期时的本息合计 sum(税前),输出时保留两位小数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* 计算存款的本息 */
#include<stdio.h>
#include<math.h> /* 程序中调用了数学函数,需包含头文件 math.h */
int main(void)
{
int money, year; /* 定义两个整型变量 */
double rate, sum; /* 定义两个双精度浮点型变量 */

printf("Enter money:"); /* 提示输入 money */
scanf("%d", &money); /* 调用 scanf() 函数输入 money */
printf("Enter year:"); /* 提示输入 year */
scanf("%d", &year); /* 调用 scanf() 函数输入 year */
printf("Enter rate:"); /* 提示输入 rate */
scanf("%lf", &rate); /* 调用 scanf() 函数输入 rate */
sum = money * pow(1+rate, year); /* 调用幂函数 pow() 计算 */
printf("sum = %.2f\n", sum);

return 0;
}

程序中调用了三次 scanf() 函数分别输入三个数据,也可以只调用一次 scanf() 函数:scanf(“%d %d %lf”, &money, &year, &rate);

调用 scanf() 函数输入多个数据时,需要多个输入参数和多个格式控制说明,而且输入参宿和的类型、个数和位置要与格式控制说明一一对应。

在程序运行时,输入的多个数据之间必须有间隔,可以用一个或多个空格作为间隔,也可以用回车或制表符(Tab)作为间隔。

2.4 输出华氏-摄氏温度转换表

2.4.1 程序解析

**例 2-6 **输入两个整数 lower 和 upper,输出一张华氏-摄氏温度转换表,华氏温度的取值范围时 [lower, upper],每次增加 1℉。计算公式如下:

c = 5 * (f - 32) / 9;

式中 c 表示摄氏温度,f 表示华氏温度。

1
// 略

在格式控制说明中,可以加宽度限定词,指定数据的输出宽度。例如,整型数据的输出格式控制说明 %md,指定了数据的输出宽度为 m(包括符号位)。若数据的实际位数(含符号位)小于 m,则左端补空格;若大于 m,则按实际位数输出。实型数据的输出格式控制说明 %m.nf,指定了输出浮点型数据时保留 n 位小数,切输出宽度是 m (包括符号位和小数点)。若数据的实际位数小于 m,左端补空格;若大于 m,按实际位数输出。

2.4.2 for语句

在 C 语言中,for 语句被称为循环语句,它可以实现 C 语句的重复执行。

for 语句的一般形式为:

1
2
for(表达式1; 表达式2; 表达式3)
循环体语句

for 语句中,用两个分号分隔三个表达式,但 for 的后面没有分号,因为 for 与其后的循环体语句合起来作为一条完整的语句。

![](E:\myblog\images\屏幕截图 2023-02-21 163515.png)

for 语句中的三个表达式以及循环体语句的执行顺序和书写顺序有所不同,计算表达式 3 在执行循环体语句之后。

图 2.3 清楚地表明:在 for 语句的执行过程中,表达式2、循环体语句和表达式 3 将重复执行,而表达式 1 只在进入循环前执行一次

在 for 语句中,常常通过改变和判断某个变量的值来控制循环的执行,这样的变量被称为循环控制变量

for 语句中三个表达式可以是任意合法的表达式,循环体语句只能是一条语句。

下面结合例 2-6 讨论 for 语句中三个表达式和循环体语句的含义和功能:

1
2
3
4
for(fahr = lower; fahr <= upper; fahr ++) {
celsius = (5.0 / 9.0) * (fahr - 32);
printf("%d%6.1f\n", fahr, celsius);
}
  • 1)表达式1:初值表达式,对循环变量赋初值,从而指定循环的起点。如 fahr = lower,置 fahr 的初值为温度取值范围的下限值 lower,即循环从 lower 开始。
  • 2)表达式2:条件表达式,给出循环的条件,通常判断循环是否超过循环的终点。若该表达式的值为“真”,则继续循环;为“假”,则结束循环。如 fahr <= upper,upper 作为温度取值范围的上限值,一旦 fahr 的值超过 upper,表达式 2 为“假”,循环随之结束。
  • 3)表达式3:步长表达式,设置循环的步长,改变循环变量的值,从而可改变表达式 2 的结果。如 fahr++,使 fahr 的值增 1,这样,最终 fahr > upper,表达式 2 为“假“,循环正常结束。
  • 4)循环体语句:被反复执行的语句,只能是一条语句

for 语句反映了循环(重复执行)的规则,从哪儿开始(起点),到哪儿结束(终点),每次跨多大的步子(步长),重复做什么。

上述 for 语句中,循环体语句用一对大括号括起来了,这是因为温度的转换和输出由两条语句实现,但根据 for 语句的语法规定,循环体语句只能是一条语句,故用大括号将这两条语句括起来,组成复合语句,复合语句在语法上被认为是一条语句。

如果将上述 for 语句改为:

1
2
3
4
for(fahr = lower; fahr <= upper; fahr ++) {
celsius = (5.0 / 9.0) * (fahr - 32); /* 语句1 */
printf("%d%6.1f\n", fahr, celsius); /* 语句2 */
}

则循环体语句只包括语句1,而语句2被当作 for 的下一条语句,不参加循环。因为按照 C 语言的语法规定,循环体语句只能是一条语句。这是初学者常犯的错误,而且在程序运行的时候系统没有任何错误提示。

如果循环体语句由多条语句组成,必须用大括号把它们括起来,变成一条复合语句。

在 C 语言中,仅由一个分号(;)构成的语句称为空语句,它什么也不做。

如果将上述 for 语句改为:

1
2
3
for(fahr = lower; fahr <= upper; fahr ++); // 分号代表空语句
celsius = (5.0 / 9.0) * (fahr - 32);
printf("%d%6.1f\n", fahr, celsius);

则循环体语句就是空语句,真正要反复执行的温度的转换和输出都被当作了 for 的下一条语句。

不要在 for 语句中随意加分号

2.4.3 指定次数的循环程序设计

例 2-7 输入一个正整数 n,求 (略)

为解决这个问题,首先抽取出具有共性的算式(称为循环不变式);

循环体语句向右缩进对齐,可以明确标识循环体的范围,这和 if 语句的风格一致。

从上述示例中可以看到,指定次数的循环程序设计一般包含 4 个部分。

  • 1)初始化:指定循环七点,给循环变量赋初值,如 i = 1,以及在进入循环之前设置相关变量的初值,如 sum = 0 等;
  • 2)条件控制:只要循环变量的值未到达指定的上限,就继续循环。如上例中,只要满足 i <= n,循环就继续。
  • 3)工作:指重复执行的语句(循环体)。它必须是一条语句,可以是复合语句或空语句,如 sum = sum + i。
  • 4)改变循环变量:在每次循环中改变循环变量的值,如 i++,从而改变循环条件的真假。

除初始化部分在进入循环之前执行(只执行一次),其他三个部分都会重复执行。

2.5 生成乘方表与阶乘表

例 2-10输入一个正整数 n,生成一张 2 的乘方表,输出 2⁰ 到 2的 n 次幂的值,可以调用幂函数计算 2 的乘方。

源程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* 调用幂函数 pow() 生成乘方表 */
#include<stdio.h>
#include<math.h>
int main(void)
{
int i, n;
double power;

printf("Enter n");

scanf("%d", &n);
for(i = 0; i <= n; i++) {
power = pow(2, i); /* 调用幂函数 pow(2, i) 计算 2 的 i 次方 */
printf("pow(2, %d) = %.0f\n", i, power);
}
return 0;
}

从 2-10 和 2-11(偷懒省略掉了,有一个自定义函数)中可以看到,C 语言中有两种类型函数——标准库函数自定义函数。函数可以做到一次定义、多次调用,这和数学上的函数概念是一致的。

使用自定义的函数程序框架如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include...
double fact(int n); /* 声明自定义函数,以分号结束 */
int main(void)
{
...
double result;
result = fact(i); /* 调用自定义函数 fact(i) 计算 i!(阶乘) */
...
}
/* 定义求 n! 的函数 */
double fact(int n) /* 函数首部,无需分号 */
{
double product; /* 定义变量 product 用于存放结果(阶乘的值) */
...
return product; /* 将结果回送主函数 */
}

自定义函数的概念和应用将在第 5 章详细说明,读者目前只需对此有初步的认识即可。

3.分支结构

本章要点

  • 什么是分支结构?它的作用是什么?
  • switch 语句中的 break 起什么作用?
  • 逻辑运算和关系运算的相同之处是什么?它们之间又有什么不同?
  • 字符型数据在内存中是如何存储的?

文章作者: Water monster
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Water monster !
评论
  目录