(本文为原创,版权归作者所有)
机器指令可以被分类为存取指令、运算指令和跳转指令。C语言的运算符和表达式封装了存取指令和运算指令,使得程序具有了读写内存和计算的能力。然而只能进行计算的程序是平庸而无聊的,它平铺直叙,无法根据环境的变化而变化。程序还需要逻辑判断的能力。
跳转指令赋予程序逻辑的能力。跳转指令又分为两种,一种是无条件跳转,一种是有条件跳转,有条件跳转指令可以使程序根据上一个指令的运算结果,来改变程序的执行路径。正是这种有条件的改变使得程序具有了活力且富于变化,程序员因此可以编出高效、灵活、有趣的代码。
C语言中的控制流语句,包括跳转、条件和循环,就是对机器语言的跳转指令进行了封装和扩展。
跳转语句
最简单的是跳转语句goto,它是对机器语言的无条件跳转指令的封装,可以使程序无条件的跳转到指定的位置执行。goto语句的语法格式如下:
lable
当程序执行到goto语句时,会跳转到语句标号label所标示的位置继续执行。机器语言中的跳转指令几乎可以跳转到程序的任意位置,但是C语言对goto语句做出了一定的限制,以避免程序出现不可预知的错误,比如,goto语句就不可以跳转到其他的函数中,因为这样会破坏栈的结构(我们会在后面的文章里介绍函数和栈的概念),甚至会使程序崩溃。
然而goto语句的能力依然十分强大,它可以在代码内任意穿梭。但是这种灵活性带来的坏处是,如果程序员滥用或者误用了goto语句,就会破坏程序的结构,也容易造成程序的运行错误,比如,使用goto语句甚至可以直接跳进条件和循环的结构中去,这显然不是一个正常的编程逻辑,而在C语言层面并没有禁止这样去做。因此,为了避免使用goto语句引发的错误,一个约定俗成的编程规范是,尽量不要使用goto语句!
当然,凡事都有特例,如果使用goto语句确实可以带来好处,并且程序员可以确信不会产生副作用时,也可以使用goto语句。例如,很多程序在进行错误处理时,会使用goto语句来避免冗长的条件语句嵌套。
跳转语句goto是一种无条件的跳转,函数是另一种对无条件跳转指令的封装。无条件跳转只能按照事先约定的方式改变程序的执行路径,无法赋予程序逻辑判断的能力。程序需要有条件的跳转。
条件语句
C语言的条件语句封装了机器语言的有条件跳转指令,给程序员提供了一个友好的方式,用以实现如果…就…否则…的程序逻辑,赋予程序产生逻辑分支的能力。
条件语句根据表达式的结果来控制程序的执行顺序,它的形式如下:
if
条件语句if-else if-else以表达式的结果是否为真作为条件,用来判断程序是应该继续执行,还是应该跳转到下一个代码块。
开关语句switch-case可以被看作是一种特殊的条件语句,它的形式是这样的:
switch
实际上开关语句完全可以被条件语句替代,上面的代码可以非常容易的用条件语句重新写一遍,不过如果开关的值很多,使用开关语句可以让代码显得更加简洁精炼。因此开关语句可以被看作是条件语句的特殊版本,用于简化对某个变量进行多值判断的情形。
条件语句另一个简化版本是条件运算符,它可以将某一类使用条件语句写成的一段代码精简为一个语句。比如下面求a和b的最大值的代码:
if
如果使用条件运算符则可以简化为:
max
条件运算符的表达式包括3个元素(因此又被称为三元运算符),其中?之前的表达式为判断条件,条件为真则返回:之前的值,条件为假则返回:之后的值。
循环语句
C语言的另一种控制流语句是循环语句,程序使用循环语句来实现重复性的工作。循环语句有三种形式:while,do…while,for。
循环语句while:
int
循环语句do…while:
int
循环语句for:
int
虽然形式不同,但上面的三段代码做的是同一件事情,就是计算从0加到99的和。三段代码中的 i 为循环控制变量,i<100是循环条件,循环条件为真则进入循环体执行,循环条件为假则退出循环,循环的代码就是要通过改变循环控制变量来决定何时可以退出循环,完成计算。不同的是do…while至少会执行一次循环体内的代码,它对循环条件的判别是在循环结束的位置;而while和for则会在循环开始的时候进行循环条件的判定,在循环结束时会无条件向前跳转到循环开始。因此do…while的每次循环只执行一次条件跳转指令,而while和for则会执行一次条件跳转指令和一次无条件跳转指令,所以从这个角度来讲,do…while的运行效率似乎是最高的,但这种区别实在是微乎其微,我们不用纠结于此。在大多数情况下,这三种循环方式是等价的,使用何种形式取决于程序员的喜好,其中for语句显得更加精炼一些,因为它将循环起始状态(i=0)、循环条件(i<100)和对循环控制变量的改变(i++)都集成在一个语句内了。
条件语句和循环语句是命令式语言中最基本最重要的控制流语句,它们简单易用,但却是程序逻辑和算法实现的关键,理解并灵活掌握它们的使用方法是作为一个程序员应该具备的最基本的素质。C语言对控制流语句的使用几乎没有任何限制,程序员可以对它们进行任意的嵌套、组合,只要符合语法规则都是被允许的,因此用它们可以实现异常复杂的逻辑和算法,这也是C语言之所以强大和灵活的原因之一。
C语言(1)- 变量和类型:
蓝彼得:C语言(1)- 变量和类型zhuanlan.zhihu.comC语言(2)- 定点数和浮点数:
蓝彼得:C语言(2)- 定点数和浮点数zhuanlan.zhihu.comC语言(3)- 运算符与表达式:
蓝彼得:C语言(3)- 运算符与表达式zhuanlan.zhihu.comC语言(4)- 控制流:跳转、条件和循环:
蓝彼得:C语言(4)- 控制流:跳转、条件和循环zhuanlan.zhihu.comC语言(5)- 内存模型与作用域:
蓝彼得:C语言(5)- 内存模型与作用域zhuanlan.zhihu.comC语言(6)- 函数调用和栈:
蓝彼得:C语言(6)- 函数调用和栈zhuanlan.zhihu.comC语言(7)- 递归:
蓝彼得:C语言(7)- 递归zhuanlan.zhihu.com