profit = 2150.48f;
不加f可能会触发编译器的警告。
正常情况下,要将int型的值赋给int型的变量,将float型的值赋给float型的变量。混合类型赋值(例如把int型的值赋给float型变量,或者把float型的值赋给int型变量)是可以的,但不一定安全,参见4.2节。
变量一旦被赋值,就可以用它来辅助计算其他变量的值:
height = 8; length = 12; width = 10; volume = height * length * width; /* volume is now 960 */
在C语言中,符号*表示乘法运算,因此上述语句把存储在height、length和width这3个变量中的数值相乘,然后把运算结果赋值给变量volume。通常情况下,赋值运算的右侧可以是一个含有常量、变量和运算符的公式(在C语言的术语中称为表达式)。
2.4.4 显示变量的值
用printf可以显示出变量的当前值。以
Height: h
为例,这里的h表示变量height的当前值。我们可以通过如下的printf调用来实现输出上述信息的要求:
printf("Height: %d\n", height);
占位符%d用来指明在显示过程中变量height的值的显示位置。注意,因为在%d后面放置了\n,所以printf在显示完height的值后会跳到下一行。
%d仅用于int型变量。如果要显示float型变量,则要用%f来代替%d。默认情况下,%f会显示出小数点后6位数字。如果要强制%f显示小数点后p位数字,可以把.p放置在%和f之间。例如,为了显示信息
Profit: $2150.48
可以把printf写为如下形式:
printf("Profit: $%.2f\n", profit);
C语言没有限制调用一次printf可以显示的变量数量。为了同时显示变量height和变量length的值,可以使用下面的printf调用语句:
printf("Height: %d Length: %d\n", height, length);
程序 计算箱子的空间重量
运输公司特别不喜欢又大又轻的箱子,因为箱子在卡车或飞机上运输时要占据宝贵的空间。事实上,对于这类箱子,运输公司常常要求按照箱子的体积而不是重量来支付额外的费用。在美国,通常的做法是把体积除以166(这是每磅允许的立方英寸1数)。如果除得的商(也就是箱子的“空间”重量或“体积”重量)大于箱子的实际重量,那么运费就按照空间重量来计算。(除数166是针对国际运输的,计算美国国内运输的空间重量时通常用194代替。)
11磅约为453.59克;1英寸约为2.54厘米。——编者注
假设运输公司雇你来编写一个计算箱子空间重量的程序。因为刚刚开始学习C语言,所以你决定先编写一个计算特定箱子空间重量的程序来试试身手,其中箱子的长、宽、高分别是12英寸、10英寸和8英寸。C语言中除法运算用符号/表示。所以,显然计算箱子空间重量的公式如下:
weight = volume / 166;
这里的weight和volume都是整型变量,分别用来表示箱子的重量和体积。但是上面这个公式并不是我们所需要的。在C语言中,如果两个整数相除,那么结果会被“截短”:小数点后的所有数字都会丢失。12英寸×10英寸×8英寸的箱子体积是960立方英寸,960除以166的结果是5而不是5.783,这样使得重量向下舍入,运输公司则希望结果向上舍入。一种解决方案是在除以166之前把体积数加上165:
weight = (volume 165) / 166;
这样,体积为166立方英寸的箱子的空间重量就为331/166,舍入为1;而体积为167立方英寸的箱子的空间重量则为332/166,舍入为2。下面给出了利用这种方法编写的计算空间重量的程序。
dweight.c
/* Computes the dimensional weight of a 12" x 10" x 8" box */ #include <stdio.h> int main(void) { int height, length, width, volume, weight; height = 8; length = 12; width = 10; volume = height * length * width; weight = (volume 165) / 166; printf("Dimensions: %dx%dx%d\n", length, width, height); printf("Volume (cubic inches): %d\n", volume); printf("Dimensional weight (pounds): %d\n", weight); return 0; }
这段程序的输出结果是
Dimensions: 12x10x8 Volume (cubic inches): 960 Dimensional weight (pounds): 6
2.4.5 初始化
当程序开始执行时,某些变量会被自动设置为零,而大多数变量则不会(➤18.5节)。没有默认值并且尚未在程序中被赋值的变量是未初始化的(uninitialized)。
如果试图访问未初始化的变量(例如,用printf显示变量的值,或者在表达式中使用该变量),可能会得到不可预知的结果,如2568、-30 891或者其他同样没有意义的数值。在某些编译器中,可能会发生更坏的情况(甚至是程序崩溃)。我们当然可以总是采用赋值的方法给变量赋初始值,但还有更简便的方法:在变量声明中加入初始值。例如,可以在一步操作中声明变量height并同时将其初始化:
int height = 8;
按照C语言的术语,数值8是一个初始化器(initializer)。
在同一个声明中可以对任意数量的变量进行初始化:
int height = 8, length = 12, width = 10;
注意,上述每个变量都有属于自己的初始化器。在接下来的例子中,只有变量width拥有初始化器10,而变量height和变量length都没有(也就是说这两个变量仍然未初始化):
int height, length, width = 10;
2.4.6 显示表达式的值
printf的功能不局限于显示变量中存储的数,它可以显示任意数值表达式的值。利用这一特性既可以简化程序,又可以减少变量的数量。例如,语句
volume = height * length * width; printf("%d\n", volume);
可以用以下形式代替:
printf("%d\n", height * length * width);
printf显示表达式的值的能力说明了C语言的一个通用原则:在任何需要数值的地方,都可以使用具有相同类型的表达式。
2.5 读入输入
程序dweight.c并不十分有用,因为它仅可以计算出一个箱子的空间重量。为了改进程序,需要允许用户自行输入箱子的尺寸。
为了获取输入,就要用到scanf函数。它是C函数库中与printf相对应的函数。scanf中的字母f和printf中的字母f含义相同,都表示“格式化”的意思。scanf函数和printf函数都需要使用格式串(format string)来指定输入数据或输出数据的形式。scanf函数需要知道将获得的输入数据的格式,而printf函数需要知道输出数据的显示格式。
为了读入一个int型值,可以使用下面的scanf函数调用:
scanf("%d", &i); /* reads an integer; stores into i */
其中,字符串"%d"说明scanf读入的是一个整数,而i是一个int型变量,用来存储scanf读入的输入。&运算符(➤11.2节)在这里很难解释清楚,因此现在只说明它在使用scanf函数时通常是(但不总是)必需的。
读入一个float型值时,需要一个形式略有不同的scanf调用:
scanf("%f", &x); /* reads a float value; stores into x */
%f只用于float型变量,因此这里假设x是一个float型变量。字符串"%f"告诉scanf函数去寻找一个float格式的输入值(此数可以含有小数点,但不是必须含有)。
程序 计算箱子的空间重量(改进版)
下面是计算空间重量程序的一个改进版。在这个改进的程序中,用户可以输入箱子的尺寸。注意,每一个scanf函数调用都紧跟在一个printf函数调用的后面。这样做可以提示用户何时输入,以及输入什么。
dweight2.c
/* Computes the dimensional weight of a box from input provided by the user */ #include <stdio.h> int main(void) { int height, length, width, volume, weight; printf("Enter height of box: "); scanf("%d", &height); printf("Enter length of box: "); scanf("%d", &length); printf("Enter width of box: "); scanf("%d", &width); volume = height * length * width; weight = (volume 165) / 166; printf("Volume (cubic inches): %d\n", volume); printf("Dimensional weight (pounds): %d\n", weight); return 0; }
这段程序的输出显示如下(用户的输入用下划线标注):
Enter height of box: 8 Enter length of box: 12 Enter width of box: 10 Volume (cubic inches): 960 Dimensional weight (pounds): 6
提示用户输入的消息(提示符)通常不应该以换行符结束,因为我们希望用户在同一行输入。这样,当用户敲回车键时,光标会自动移动到下一行,因此就不需要程序通过显示换行符来终止当前行了。
dweight2.c程序还存在一个问题:如果用户输入的不是数值,程序就会出问题。3.2节会更详细地讨论这个问题。
2.6 定义常量的名字
当程序含有常量时,建议给这些常量命名。程序dweight.c和程序dweight2.c都用到了常量166。在以后阅读程序时也许会有人不明白这个常量的含义,因此可以采用称为宏定义(macro definition)的特性给常量命名:
#define INCHES_PER_POUND 166
这里的#define是预处理指令,类似于前面所讲的#include,因而在此行的结尾也没有分号。
当对程序进行编译时,预处理器会把每一个宏替换为其表示的值。例如,语句
weight = (volume INCHES_PER_POUND - 1) / INCHES_PER_POUND;
将变为
weight = (volume 166 - 1) / 166;
效果就如同在前一个地方写的是后一条语句。
此外,还可以利用宏来定义表达式:
#define RECIPROCAL_OF_PI (1.0f / 3.14159f)
当宏包含运算符时,建议用括号(➤14.3节)把表达式括起来。
注意,宏的名字只用了大写字母。这是大多数C程序员遵循的规范,但并不是C语言本身的要求。(至今,C程序员沿用此规范已经几十年了,希望读者不要背离此规范。)
程序 华氏温度转换为摄氏温度
下面的程序提示用户输入一个华氏温度,然后输出一个对应的摄氏温度。此程序的输出格式如下(跟前面的例子一样,用户的输入信息用下划线标注出来):
Enter Fahrenheit temperature: 212 Celsius equivalent: 100.0
这段程序允许温度值不是整数,这也是摄氏温度显示为100.0而不是100的原因。首先来阅读一下整个程序,随后再讨论程序是如何构成的。
celsius.c
/* Converts a Fahrenheit temperature to Celsius */ #include <stdio.h> #define FREEZING_PT 32.0f #define SCALE_FACTOR (5.0f / 9.0f) int main(void) { float fahrenheit, celsius; printf("Enter Fahrenheit temperature: "); scanf("%f", &fahrenheit); celsius = (fahrenheit - FREEZING_PT) * SCALE_FACTOR; printf("Celsius equivalent: %.1f\n", celsius); return 0; }
语句
celsius = (fahrenheit – FREEZING_PT) * SCALE_FACTOR;
把华氏温度转换为相应的摄氏温度。因为FREEZING_PT表示的是常量32.0f,而SCALE_FACTOR表示的是表达式(5.0f / 9.0f),所以编译器会把这条语句看成
celsius = (fahrenheit – 32.0f) * (5.0f / 9.0f);
在定义SCALE_FACTOR时,表达式采用(5.0f / 9.0f)的形式而不是(5 / 9)的形式,这一点非常重要,因为如果两个整数相除,那么C语言会对结果向下舍入。表达式(5 / 9)的值为0,这并不是我们想要的。
最后的printf函数调用输出相应的摄氏温度:
printf("Celsius equivalent: %.1f\n", celsius);
注意,使用%.1f显示celsius的值时,小数点后只显示一位数字。
2.7 标识符
在编写程序时,需要对变量、函数、宏和其他实体进行命名。这些名字称为标识符(identifier)。在C语言中,标识符可以含有字母、数字和下划线,但是必须以字母或者下划线开头。[
在C99中,标识符还可以使用某些“通用字符名”(➤25.4节)。]下面是合法标识符的一些示例:
times10 get_next_char _done
接下来这些则是不合法的标识符:
10times get-next-char
不合法的原因:符号10times是以数字而不是以字母或下划线开头的;符号get-next-char包含了减号,而不是下划线。
C语言是区分大小写的;也就是说,在标识符中C语言区分大写字母和小写字母。例如,下列8个标识符全是不同的:
job joB jOb jOB Job JoB JOb JOB
上述8个标识符可以同时使用,且每一个都有完全不同的意义。(看起来使人困惑!)除非标识符之间存在某种关联,否则明智的程序员会尽量使标识符看起来各不相同。
因为C语言是区分大小写的,所以许多程序员会遵循在标识符中只使用小写字母的规范(宏命名除外)。为了使名字清晰,必要时还会插入下划线:
symbol_table current_page name_and_address
而另外一些程序员则避免使用下划线,他们的方法是把标识符中的每个单词用大写字母开头:
symbolTable currentPage nameAndAddress
(第一个字母有时候也用大写。)前一种风格在传统C中很常见,但现在后面的风格更流行一些,这主要归功于它在Java和C#(以及C )中的广泛使用。当然还存在其他一些合理的规范,只要保证整个程序中对同一标识符按照同一种方式使用大写字母就行。
C对标识符的最大长度没有限制,因此不用担心使用较长的描述性名字。例如current_page这样的名字比cp之类的名字更容易理解。关键字
表2-1中的所有关键字(keyword)对C编译器而言都有着特殊的意义,因此这些关键字不能作为标识符来使用。注意,有些关键字是C99新增的,还有一些是C1X 新增的。
表2-1 关键字