1.2 变量和算术表达式
下一个程序使用公式oC=(5/9)(oF-32)来打印以下华氏温度和它们对应的摄氏温度表格:
1 -17
20 -6
40 4
60 15
80 26
100 37
120 48
140 60
160 71
180 82
200 93
220 104
240 115
260 126
280 137
300 148
程序本身仍然由单个main函数定义组成,它比打印“hello, world”的程序要长,但是并不复杂。它引进了几个新的概念,包括注释、声明、变量、算术表达式、循环和格式化输出。
#include <stdio.h>
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300 */
main()
{
int fahr, celsius;
int lower, upper, step;
lower = 0; /* lower limit of temperature scale */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) {
celsius = 5 * (fahr-32) / 9;
printf("%d/t%d/n", fahr, celsius);
fahr = fahr + step;
}
}
这两行:
/* print Fahrenheit-Celsius table
for fahr = 0, 20, …, 300 */
是一个注释,用这种方式来简明的解释这段程序所完成的工作。“/*”和“*/”之间的任何字符会被编译器所忽略,它们可以随意的使用来使程序更容易理解。注释可以出现在空格、制表符和换行符可以出现的任何地方。
C语言中,所有的变量必须在使用之前定义,通常是在函数的任何可执行语句之前的开头部分。声名说明了变量的特征,它由一个名字和一个变量列表组成,如:
int fahr, celsius;
int lower, upper, step;
类型int说明列出的变量是整数,相反,类型float说明是浮点数,也就是带有小数部分的数。int型和float型的范围依赖于你所使用的机器,位于-32768~32767之间16位整型比较常见,32位整型同样如此,典型的浮点数是32位,至少带有6位有效数字且大小范围通常在10-38和1038之间。
除了int和float类型外,C语言还提供了一些其他的数据类型,包括:
char |
字符 – 单字节 |
short |
短整型 |
long |
长整型 |
double |
双精度浮点类型 |
这些类型的大小也是与机器有关的。C语言还有由这些基础类型构成的数组、结构体和共用体,指向它们的指针以及返回它们的函数,所有这些不久后我们都将遇到。
温度转换程序的计算从如下的赋值语句开始:
lower = 0;
upper = 300;
step = 20;
这些语句初始化了对应的变量,每条语句都以分号结束。
表格中每行的计算方法相同,所以我们使用一个循环,它每输出一行就重复一次,这正是while循环的功能:
while (fahr <= upper) {
…
}
while循环按照如下方式进行操作:测试圆括号里的条件,如果为真(fahr小于等于upper),则执行循环体(大括号里面的三条语句);然后再次测试条件,如果仍为真,则再次执行循环体;当条件测试为假(fahr大于upper)时循环结束,继续执行循环后面的那条语句。因为这个程序中没有更多的语句了,所以程序结束。
while循环体可以是用大括号括起来的一条或多条语句,就像温度转换程序中的一样,或者是没有大括号的一条语句,像:
while (i < j)
i = 2 * j;
无论何种情况下,我们总是将由while控制的语句缩进一个制表位(我们展示的是4个空格),这样你可以一眼就看得出在循环里面的语句了。这种缩进增强了程序的逻辑结构性。尽管C语言编译器并不关心程序的样式,但是适当的缩进和空格在使得程序更便于阅读来说是至关重要的。我们建议编写代码时,一行只写一条语句,并在操作符两边使用空格表示分组。大括号的位置其时并不是太重要,即使人们对此坚持着有些自作多情的信念。我们选择了几种比较流行的风格中的一种。选择一种适合你的风格,然后坚持使用它。
大部分工作在循环体内完成,摄氏温度通过如下语句计算得来并赋值给变量“celsius”:
celsius = 5 * (fahr - 32) / 9;
用先乘以5在除以9取代乘以5/9的原因是:在C语言中,和其他语言一样,采用整数除法截短,即任何小数部分将被忽略。因为5和9是整数,5/9将被截短为0,如果这样的话所有的摄氏温度都将返回为0。
这个例子同时也显示了关于printf是怎样工作的更多内容。printf是一个多功能的格式化输出函数,我们将在第七章更详细的介绍。它的第一个参数是一个将要被打印的字符串,其中每个“%”都标记着另一个(两个、三个……)参数将要被替换,和以何种形式来打印。例如,“%d”指一个整型参数,所以这条语句:
printf(“%d/t%d/n”, fahr, celsius);
导致两个整数fahr和celsius的值被打印出来,并且有一个制表符(/t)在它们之间。
printf函数的第一个参数中的每一个“%”结构都对应着第二个参数、第三个参数等等,它们必须在个数和类型上正确的得以匹配,否则你将会得到错误的答案。
顺便提一下,printf函数并不是C语言的一部分,C语言本身没有定义输入与输出。printf函数仅仅是来自标准函数库中的一个有用的函数,通常这个函数在C语言程序中能直接使用。然而,printf函数的行为动作是在ANSI标准中定义的,所以它的特征应该在任何遵循标准的编译器和库中都是一样的。
为了把精力集中在C语言本身,我们在讲到第七章之前不会太多的介绍关于输入与输出方面的内容。特别的,我们直到那个时候才介绍格式化输入。如果你需要输入数据,请阅读7.4节中关于scanf函数的讨论部分。scanf和printf类似,但是是实现读取输入而不是写输出。
温度转换程序中有几个问题。相对简单点的是输出并不是很美观,因为数字并不是右对齐的。这很容易修改,如果我们增加printf语句中的每一个“%d”的宽度,数字将在它们的空间内按照右对齐打印。例如,我们可以用:
printf(“%3d %6d/n”, fahr, celsius);
每行按照三个数字宽度的空间打印第一个数字,六个数字宽度的空间打印第二个数,像这样:
0 -17
20 -6
40 4
60 15
80 26
100 37
...
更严重的问题是因为我们使用了整数计算,摄氏温度并不是很精确,例如,0oF事实上大约是-17.8oC,而不是-17。为了得到更精确的答案,我们应该使用浮点计算来代替整型计算,这就要求对源程序做些改动。这里给出第二个版本:
#include <stdio.h>
/* print Fahrenheit-Celsius table
for fahr = 0, 20, ..., 300; floating-point version */
main()
{
float fahr, celsius;
float lower, upper, step;
lower = 0; /* lower limit of temperatuire scale */
upper = 300; /* upper limit */
step = 20; /* step size */
fahr = lower;
while (fahr <= upper) {
celsius = (5.0/9.0) * (fahr-32.0);
printf("%3.0f %6.1f/n", fahr, celsius);
fahr = fahr + step;
}
}
这跟之前的版本很相似,除了fahr和celsius被声明为float类型和那个转换公式以更自然的方式来表达。我们不能在前一个版本中使用“5/9”是因为整数除法将截短结果为零。然而,常量中的小数点标识它为浮点数,所以“5.0/9.0”不会被截短,因为它是两个浮点数的比值。
如果一个算术操作符只有整型操作数,则整型操作是允许的;然而,如果一个算术操作符有一个浮点操作数和一个整型操作数,那么这个整型操作数将在完成该操作前被转换为浮点型。如果我们写成“(fahr - 32)”,则32将自动被转换为浮点型。尽管如此,即使当浮点数具有整数值时,也要明确写出浮点数的小数点来向读者强调它们的浮点类型。
整型转换为浮点型的更详细的规则将在第二章介绍。现在,注意这条赋值语句:
fahr = lower;
和这个条件测试语句:
while (fahr <= upper)
也是以自然的方式来处理的——在操作完成之前整数被转换成浮点数。
printf函数的转换说明“%3.0f”表示一个浮点数(这里是fahr)将在至少三个字符空间范围内打印,没有小数点和小数部分;“%6.1f”描述了另一个数据(celsius)将在至少6个字符空间范围内打印,小数点后有一个数字。输出像这样:
0 -17.8
20 -6.7
40 4.4
…
宽度和精度可以从说明部分中删除:“%6f”表示数字将在至少六个字符宽度范围内打印;“%.2f”指小数点后有两个字符,但是对宽度没有约束;“%f”仅仅表示把数字作为浮点数来打印。
%d |
作为 十进制整数 打印 |
%6d |
作为 十进制整数 打印, 至少6个字符宽度 |
%f |
作为 浮点数 打印 |
%6f |
作为 浮点数 打印, 至少6个字符宽度 |
%.2f |
作为 浮点数 打印, 小数点后有2个字符 |
%6.2f |
作为 浮点数 打印, 至少6个字符宽度和小数点后有2个字符 |
除此以外,printf同样识别“%o”为八进制,“%x”为十六进制,“%c”为字符,“%s”为字符串和“%%”为“%”。
练习 1-3:修改温度转换程序以在表格顶部打印一个标题。
练习 1-4:编写一个程序打印将摄氏温度转换为华氏温度的表格。