1.2.2 表达式的值
C语言提供了很多运算符,可组成多种多样的表达式,这些表达式描述了运算符如何作用于操作数并得到一个什么样的结果。例如运算符<=属于关系运算符,它需要一左一右两个操作数,并组成关系表达式。这两个操作数是左值的,要先进行左值转换。
表达式的作用是算术或者逻辑运算,既然是运算,必然得算出一个结果来。因此,每个表达式都可以计算出一个值。对表达式n<= 100来说,它由子表达式n和100组成,子表达式n要计算一个值,这个值是左值转换后的值;子表达式100也要计算一个值,这个值是数值意义上的100。
你可能觉得奇怪,这里的100本来就是个数值啊,还用得着计算吗?事实上,尽管你一眼就看出它是个数字,但翻译器并不认得它,还需要经过分析和转换。在你编写程序的时候,你输入的只是三个代表数字的字符“1”“0”“0”,它们组成了符号“100”。在程序翻译期间,翻译器会将它识别为代表整数的常量,并将它从符号转换为真正的数字。换句话说,对常量值的计算是在程序翻译期间完成的,而不是在执行的时候。
所有程序的任务和功能都不外乎是操作数字、加工文本,数字和文本的内容可能是来自变量,但也可能在程序中直接给出,就像当前程序中的0、1和100。这些在程序中直接给出的数字和文本在翻译和执行期间不会改变,也没有什么办法改变,故称之为常量。
当一个常量出现在表达式应该出现的地方时,它也是常量表达式。确切地说,常量表达式是指那些值为常量的表达式。所以100是常量表达式,201也是常量表达式,而705+201也是常量表达式,它的值在程序翻译期间计算,其结果为906,也是常量,因为两个常量相加的结果总是常量。
不单单是n和100需要计算出数值,表达式n<= 100作为一个整体,同样要计算出一个结果(值)。对于关系表达式来说,如果相应的关系成立,则表达式的值是1,否则表达式的值为0。具体到这个表达式,如果左值n经左值转换后的值的确小于或者等于100,则该表达式的值是1;否则,结果是0。
而对于while来说,它正需要这个结果。圆括号内的表达式用于控制while语句如何执行,是终止循环呢,还是继续下一轮循环,称为控制表达式。while语句并不关心括号内的表达式是什么,它只关心该表达式的值。
如图1-5所示,在第一次进入while语句时,以及每次执行完循环体之后,要先计算控制表达式的值。这里所谓的循环体,是指组成while语句的“语句”。如果控制表达式的结果(值)是0,就退出while语句;否则,如果控制表达式的结果(值)不是0,就执行循环体。
图1-5 while语句的执行过程
在这里,组成while语句的“语句”,也就是循环体,看起来很奇怪,因为它带有一对花括号(注意,这里的缩进并不是必须的,而仅仅是为了看起来直观一些而做的排版):
{ sum = sum + n; n = n + 1; }
组成while语句的那个语句称为循环体,是每次循环都要执行的。就语法上来说,循环体是直接位于圆括号“)”之后的那个语句。如果没有花括号,则while语句将会是这个样子(同样,这里的缩进是为了看起来直观一些而做的排版):
while (n <= 100) sum = sum + n; n = n + 1;
在这种情况下,循环体仅仅是由语句
sum = sum + n;
组成,而并不包括
n = n + 1;
也就是说,如果没有花括号,则只有第一条语句是while语句的组成部分,而第二条语句不是。也就是说,只有语句
sum = sum + n;
才会循环执行,而语句
n = n + 1;
并不会,该语句只在退出while语句之后才开始执行。
但是,如果循环体的工作需要多条语句才能完成,那怎么办呢?好在C语言为我们准备了另一种语句——复合语句。
复合语句是由一对花括号“{”和“}”,以及可选地,位于这对花括号中间的声明和语句组成。之所以是“可选”,是因为复合语句可以只是一对花括号而中间为空,比如下面这个复合语句,它什么也不做:
{}
空的复合语句似乎没有什么用,但实际上它很有用。往后你就会看到,有些地方从语法上来说需要一个复合语句,但我们又没什么事可做,只能使之为空。
现在,让我们看看这个while语句的循环体都干了些什么。先来看第一个,它实际上是一个表达式语句,由表达式sum = sum + n和分号“;”组成。对于刚接触编程的人来说,这令人十分困惑,他们很自然地把这当成一个方程式,并自作聪明地认为n的值理应是0,不然的话两边怎么会相等呢?
然而实际上,这是表达式,而不是方程。同时,正如前面所说,符号“=”并非等号或者等于,而是赋值运算符。这里还出现了另一个运算符“+”,它的功能倒是符合数学里的本义,就是相加的意思,用于把它两边的值加起来求和。