4.2 if语句
4.2.1 语法格式及含义
if语句的语法格式如下:
由于C语言容许在单词之间加上空白符,为了使语句看起来更加美观易读,所以可以把这个语句改写成下面的样式而完全不影响其本来的含义:
if(表达式)
语句
比起单纯的表达式语句,或者简单的顺序结构组成的复合语句,这种所谓的控制语句表达的是相对较为复杂的思想。if语句要求计算机进行的工作如下。
(1)首先求出()内表达式的值。
(2)如果()内表达式的值不为0,则执行()后面的语句,语句执行完毕之后,意味着这条if语句执行完毕,然后继续执行if语句后面的语句。
(3)如果()内表达式的值为0,则也意味着这条if语句执行完毕,然后继续执行if语句后面的语句。
这种语言描述往往十分啰嗦,用图来表示if语句的执行步骤可能更为直观简明。如图4-1所示,显示了if语句的执行方式。
图4-1 if语句的N-S图
4.2.2 例题
1.简单的应用示范
下面的题目演示了if语句的用法。
题目:判断456与789的平方和是否大于911的平方。
程序代码4-1
C语言不认为乘方这种运算是必要的,所以没有乘方运算符。因此计算平方、立方等需要自己老老实实地写乘法。
这个代码还有许多可以改进的地方,请按照课后练习的提示和要求自己完成。
练习
程序代码4-1有一个很明显的缺点,那就是456456、789789、911911这几个值分别需要计算两次。如果使用变量记住这几个值可以避免这种重复计算。更好的写法是用变量记住456456+789789-911911的值。请自己对代码进行改进。
2.如何寻找算法
下面一个例题的注释非常详细,请自己阅读理解。
例题:输入3个整数,求最大的是多少。
程序代码4-2
程序运行结果:
仅仅从这个结果来看,程序的正确性还很不可靠。应该有更多的测试数据,至少还要有第一个和第二个数最大时两组测试数据,这是最基本的测试。请自己组织数据并测试。更进一步的测试应该考虑到有数据相等的情形。
本例题的目的和意义在于,它表明了在日常生活中我们能下意识地脱口而出的答案,往往我们是不清楚自己的真正的思考、计算过程的。然而编程所要求的恰恰是精确地把这个思考、计算的过程用计算机可以执行的方式一步一步地描述给出来。这是初学者的一个巨大的学习障碍。
要克服这种障碍,除了自觉地不断进行自我训练之外,似乎别无他法(至少我没发现,我也没发现有谁发现)。本书提供一些建议和看法供初学者自我训练时参考。
人类的思考时刻也离不开记忆,这种记忆体现在计算机中意味着内存、寄存器或磁盘文件等,在代码中则往往体现为常量、变量等技术手段。类似地,在用纸笔计算时那些写在纸上的东西也往往意味着代码中的常量或变量。
此外,在思考算法时,真正地在纸上一步一步地演算,往往有助于发现算法或把模糊的想法和步骤具体化、明确化。
把自己假设为盲人,可以使你克服具有视力这种缺陷。在思考算法时,视力是起反作用的。比如,如果把10个数写在纸上或黑板上,要求求出其中最大的值。你可能很难发现这个简单题目的算法。但是换一个场景,假设有另外一个人向你一个一个地报数,问你他报的数中哪个最大,你会很容易发现这个题目的算法。
练习
输入10个整数,输出其中的最大的值(本题目不要求重新输出这10个数,只输出其中的最大值即可。如果定义10个变量,不仅过于奢侈而且显得笨拙。建议仔细思考一下别人报数的时候你究竟应该记住的是什么)。
4.2.3 ()内的表达式
C语言要求迁语句的()内是一个有值的表达式(2),并没有对这个表达式作出更多的限制。下面的程序段,尽管看起来有些奇怪,但是合法的:
由于“()”内表达式的值为2(不为0),因此执行printf("2");语句,程序段的运行结果将会是输出:
再如:
这个例子中“()”内是一个赋值表达式,这个赋值表达式的值为0,因而printf("2");语句不被执行,程序段的运行结果将会是输出:
下面的例子中,if语句()内的表达式是一个函数调用表达式,这段代码可以用来判断程序使用者输入数据的格式是否正确。
这段代码的执行原理是这样的:由于执行if语句时需要求()内表达式的值,因而首先产生了对scanf()函数的调用,这要求程序使用者在键盘上输入数据。当输入的数据满足%d格式要求时,scanf()将把输入的字符序列转化为int类型数据存入i的内存,这样函数调用表达式的值为1(有1个数据被成功转换(3))。因而“()”后面的“printf("成功处理了输入字符序列");”语句就得到了执行。当输入数据不符合%d要求时(如输入了一个字母或标点符号),scanf()无法把输入的字符序列转化为int类型数据,更无法把结果写入i内存,这时函数调用表达式的值为0。此时"()"后面的“printf("成功处理了输入字符序列");”语句不被执行。
4.2.4 ()后面的语句
C语言对if语句“()”后面紧跟的语句并没有什么特别的限制,因此任何语句在这里都可以胜任。当然,原则上“()”后面只可以写一条语句。
1.()后面跟复合语句
由于复合语句可以把几句话“合成”为语法上的一条语句,因此常常用在()内的表达式不为0条件下需要执行若干条语句的情形。
在编程实践中,不少人,尤其是初学者,很容易忘记复合语句的一对“{}”,造成思想表达上的逻辑错误。通常这种错误查找起来并不那么容易。
2.怎样更好地写if语句
为了避免这样无谓的低级错误,养成把所有的if语句写成()后面是复合语句的习惯是值得推荐的一种良好的编码习惯。此外郑重建议在写if语句的时候首先写出其正确的和必要的框架,如下面所示:
在正确地写完这个框架之后再向()及{}之内填写必要的内容。这比信马由疆地从前向后那样的写法至少要好5倍。
3.冒泡法排序
题目:输入3个整数,要求按照非递减的次序输出。
这种把数据按照次序排好是程序设计中的一个基本问题,这种问题至少有上百种算法。这里的方法是首先使最后一个不小于前面任意一个,然后再使倒数第二个不小于前面任一个。这种思路较有条理,也很容易推广到多个数的情形。这种方法通常被称为“冒泡法”。
程序代码4-3
程序运行结果为:
练习
输入4个整数,要求按照非递减的次序输出。
4.()后面跟if语句
if语句的()后面同样可以是一个if语句,这种情况叫做if语句的嵌套。这种奇妙的组合往往可以用来表示较为复杂的算法。
题目:输入一个整数,判断这个数是否满足大于等于1且小于等于12。
程序代码4-4
测试问题:这种带有逻辑判断的代码一般都需要测试多组数据。以本题为例,至少应当测试这个数小于1,大于12,和在1和12之间这三种情形下程序的输出是否正确。此外,经验表明,代码中的很多错误发生于边界。对于本题目来说,1和12是其边界。对于这两个数,经常容易发生把zzs>=1、zzs<=12误写成zzs>1、zzs<12而带来的错误。所以除了前面3种情况,还应该测试输入为1和12的情况。请自己编辑代码并完成测试。
这段代码还可以写得更简洁些,比如把(zzs>=1)*(zzs<=12)作为if语句()中的表达式代替if语句的嵌套。在后面可以看到,这和使用逻辑运算&&的效果是等价的。此外这段代码还有不少地方有改进的余地,但目前差不多只能如此了。
练习
说出下面程序段的输出结果:
1.输入一字符,判断这个字符是否是英文字母(提示:输入的字符显然有5种可能:<'A';>='A'且<='Z';>'Z'且<'a';>='a'且<='z';>'z'。其中的“且”可以用if语句的嵌套实现)。
2.已知a、b、c为整系数一元二次方程ax2+bx+c=0各项的系数。编程输入这3不系数,判断对应的一元二次方程是否有两个实根(显然,a不可以为0且b2-4ac应不小于0)。