1.2 相加过程的实现
以上,我们已经声明了两个标识符n和sum,它们各自指示或者说代表一个变量,但这两个变量只有在程序运行时才会从存储器里分配。
因为我们是要从1加到100,所以,变量n的初始存储值应当为1(以后在此基础上递增即可),而变量sum的初始存储值应当为0。既然我们是在纸上谈兵,那么,尽管我们还是在写程序,变量还没有分配,也可以在程序中表达这样的意图:在程序运行的时候修改变量n和sum的存储值,让它们分别为1和0。
这当然是可以的,在C语言里,这种事情需要用语句来完成。在生活中,语句是一个能够表达完整意思的句子;C语言借用了这个术语,用来描述程序在实际执行时应当完成的动作。例如,往变量里存储一个数值,这就是一个动作。为了向变量n写数值1,往变量sum写数值0,可以使用下面两条语句:
n = 1; sum = 0;
注意,你数学学得再好,也不可以把它们理解为“n等于1”和“sum等于0”,因为这并不是在解方程,而n和sum也不是未知数。
实际上,如图1-4所示,第一条语句的意思是“在程序实际运行时,往变量n里写入数值1”;第二条语句的意思是“在程序实际运行时,往变量sum里写入数值0”。
图1-4 语句用于描述程序运行时的动作
注意,这两条语句仅仅是在用文本来“表达”一个动作,只有当程序真正在电脑中运行时,这个动作才可能实实在在地执行。用文本描述程序在运行时将要实际执行的动作,这就是编程的本质。
问题在于,语句是如何构建的呢?符号“=”是什么意思?而“n = 1”又是什么意思呢?为什么它的后面要加个“;”呢?
在C语言里,语句用于指示一个在程序执行时应当完成的动作——可以是算术或者逻辑计算动作,也可以是改变程序执行流程的动作,比如有选择地执行程序的某一部分,或者重复执行程序的某一部分。完成不同的动作需要使用不同的语句,就像写文章时要用到陈述句、疑问句和感叹句一样。
生活中,文章里的语句虽然是一个整体,但它是由字、词组成的。在C语言里,语句也是一个粗粒度的语法单位,也是由不同的成分组成的。如果语句指示的是算术或者逻辑计算动作,那么,它应当由表达式和一个分号“;”组成,称为表达式语句,表达式决定了执行的是什么样的算术和逻辑计算。
典型地,上面那两条语句就是表达式语句,如果去掉这两条语句末尾的分号“;”,剩下的部分,也就是“n = 1”和“sum = 0”,就是表达式。
表达式是C语言的重要语法成分,用来表达不同的计算意图,是由运算符及其操作数组成的序列,例如1 + 2。在表达式里,运算符指明了要进行何种运算和操作,而操作数则是运算符操作的对象。
因此,对于1 + 2这个例子,“+”是运算符,而1和2是操作数,这个表达式所表达的意图是把1和2加起来,得到3这个结果。
回到表达式n = 1和sum = 0,这里的“=”也是运算符,而n、1、sum和0都是操作数。不要以为0和1这类的数字才是操作数,这样理解太片面了。操作数可以是数字,但也可以是指示某个实体的符号,比如代表变量的标识符。
运算符“=”不是数学课里的“等于”,它的真实意思是保存、存储或者“赋予”,这在编程语言里叫作赋值,所以该运算符称为赋值运算符。实际上,应该把它换成“<-”才更形象更直观,但是没办法,C语言的发明者选择了“=”,我们只能接受这个现实。
运算符“=”需要一左一右两个操作数,而且左操作数必须代表一个变量,右操作数必须计算出一个数值。该运算符所指定的操作是将右边那个操作数的值赋给(存储到)左边那个操作数所指示和代表的变量。正是因为这个,像n = 1和sum = 0这样的表达式称为赋值表达式。
尽管每个表达式都已经清楚地“表达”了自己所要进行的运算和操作,但是很遗憾,它们无法独立存在于程序中,而只能是某些声明和语句的组成部分。因此,如果一个语句是由表达式和末尾的分号“;”组成的,则此语句称为表达式语句。但是反过来,C语言里的语句种类很多,并不都是表达式语句,很快你就会看到各种各样的语句。
大的、复杂的表达式都是由小的、简单的表达式组成的,组成大表达式的小表达式称为子表达式。比如表达式n = 1里的n和1既是运算符=的操作数,又是子表达式;再比如表达式sum = 0里的sum和0既是运算符=的操作数,也是子表达式。显然,很多表达式并不包含运算符。尽管我们说表达式是运算符和操作数组成的序列,但是如果一个表达式里没有运算符也不是什么大不了的事。
说了这么多,不能再说了,再说你就记不住了,甚至会糊涂。为了不让你糊涂,我们帮你理一理,总结一下刚才都讲了什么:
✓在C语言里,语句用于指定程序运行时应当执行的动作;
✓有多种类型的语句,表达式语句是其中的一类;
✓表达式语句由表达式和末尾的分号“;”组成;
✓表达式是由子表达式通过运算符连接而成,它们被视为运算符的操作数;
✓有各种不同类型的表达式,赋值表达式是其中的一类。
不管你是在纸上写程序,还是在文字处理器中写程序,现在,我们的程序清单中已经有了以下这些内容:
unsigned long long int n, sum; n = 1; sum = 0;
这段代码声明了标识符n和sum并分别用两条语句给运算符=的左操作数n和sum赋值,它们用来指定当程序实际运行时应当完成的任务和动作:在内存储器里分配变量n和sum,然后把数值1存储到变量n;把数值0存储到变量sum。
既然已经完成了声明和赋值的编码工作,接下来的编程任务就是描述从1加到100的过程了。怎么加呢?该不会是写100行语句,结结实实地从1加到100吧?如果是这样的话,我们何必大费周章地写程序来做这件事。
既然是编写程序来做这件事,那肯定得有既省事,效率又高的方法,C语言必定要准备这样的方法供我们使用。
说得不错,我们知道,语句用于指定程序运行时应当执行的动作,如果是一个表达式语句,那么,它所指定的动作实际上是表达式所描述的算术或者逻辑运算。但是我们已经讲过,语句的动作未必都是表达式所描述的操作,有些动作是表达式“表达”不了的,比如改变程序的执行流程,这些并不是算术或者逻辑运算。举个例子来说,在我们现有的程序里,是先执行语句
n = 1;
再执行语句
sum = 0;
而且不会掉转头去重复执行。如果想要改变程序的执行流程,比如重复执行某些语句,这就不是表达式语句所能胜任的了。
好在C语言为我们提供了好多种不同类型的语句,比如循环语句,我们可以用循环语句来做这件事。循环语句又可以继续细分为好几种,我们先来介绍while语句。如果使用while语句,从1加到100的写法可以是这样的:
while (n <= 100) { sum = sum + n; n = n + 1; }
看样子while语句很复杂呀,但实际上并非如此。while语句有固定的格式,其语法结构为
while(表达式)语句
在C语言里,写程序就像造句和填字游戏。在这里,正常字体的部分意味着它是固定不变的成分,例如“while”“(”和“)”,它们具有固定的拼写和相对位置;斜体意味着它只是一个用来占位子的名称,又叫占位符,需要用具体的内容来填充和取代,比如这里的“表达式”和“语句”,它们需要用具体的表达式和语句来代替。显然,while语句在语法组成上是迭代的,它本身是语句,但还要由其他语句组成(甚至可能是另一个while语句)。
英语单词“while”的意思是“当……的时候”,所以,C语言用它来表示当某个条件成立的时候,重复做某些事。而且呢,因为条件会发生变化,所以每次重复做之前,都要重新检查一下。
在这里,所谓的“条件”是指表达式n<= 100的运算结果。根据直觉,循环的前提条件是变量n的值小于等于100,因为我们都认识这个小于等于号“<=”。说得不错,正是如此。
在这里,“<=”是运算符,用于比较两个数值的大小关系,表示“小于等于”,在写这个运算符时,“<”和“=”必须连写而不得分开。
每个表达式所定义的运算和操作都应当合乎逻辑。在赋值表达式n = 1中,运算符=的左操作数(子表达式)n代表一个程序运行时的变量,这是合乎逻辑的,因为只有变量才能容纳一个值。如果是3 = 1,这就不合法了,你不能把一个数值保存到另一个数值中去。
运算符<=需要一左一右两个操作数,问题是,左操作数n代表一个变量,而右操作数100是一个数值,变量和数值怎么能比较大小呢?不要着急,且听我慢慢道来。