1. if语句

目前我们写的简单函数中可以有多条语句,但这些语句总是从前到后顺序执行的。除了顺序执行之外,有时候我们需要检查一个条件,然后根据检查的结果执行不同的后续代码,在C语言中可以用分支语句(Selection Statement)实现,比如:

if (x != 0) {
	printf("x is nonzero.\n");
}

其中x != 0表示“x不等于0”的条件,这个表达式称为控制表达式(Controlling Expression)如果条件成立,则{}中的语句被执行,否则{}中的语句不执行,直接跳到}后面。if和控制表达式改变了程序的控制流程(Control Flow),不再是从前到后顺序执行,而是根据不同的条件执行不同的语句,这种控制流程称为分支(Branch)。上例中的!=号表示“不等于”,像这样的运算符有:

表 4.1. 关系运算符和相等性运算符

运算符含义
==等于
!=不等于
>大于
<小于
>=大于或等于
<=小于或等于

注意以下几点:

  1. 这里的==表示数学中的相等关系,相当于数学中的=号,初学者常犯的错误是在控制表达式中把==写成=,在C语言中=号是赋值运算符,两者的含义完全不同。

  2. 如果表达式所表示的比较关系成立则值为真(True),否则为假(False),在C语言中分别用int型的1和0表示。如果变量x的值是-1,那么x>0这个表达式的值为0,x>-2这个表达式的值为1。

  3. 在数学中a<b<c表示b既大于a又小于c,但作为C语言表达式却不是这样。以上几种运算符都是左结合的,请读者想一下这个表达式应如何求值。

  4. 这些运算符的两个操作数应该是相同类型的,两边都是整型或者都是浮点型可以做比较,但两个字符串不能做比较,在第 1.5 节 “比较字符串”我们会介绍比较字符串的方法。

  5. ==和!=称为相等性运算符(Equality Operator),其余四个称为关系运算符(Relational Operator),相等性运算符的优先级低于关系运算符。

总结一下,if (x != 0) { ... }这个语句的计算顺序是:首先求x != 0这个表达式的值,如果值为0,就跳过{}中的语句直接执行后面的语句,如果值为1,就先执行{}中的语句,然后再执行后面的语句。事实上控制表达式取任何非0值都表示真值,例如if (x) { ... }if (x != 0) { ... }是等价的,如果x的值是2,则x != 0的值是1,但对于if来说不管是2还是1都表示真值。

if语句相关的语法规则如下:

语句 → if (控制表达式) 语句
语句 → { 语句列表 }
语句 → ;

在C语言中,任何允许出现语句的地方既可以是由;号结尾的一条语句,也可以是由{}括起来的若干条语句或声明组成的语句块(Statement Block),语句块和上一章介绍的函数体的语法相同。注意语句块的}后面不需要加;号。如果}后面加了;号,则这个;号本身又是一条新的语句了,在C语言中一个单独的;号表示一条空语句(Null Statement)。上例的语句块中只有一条语句,其实没必要写成语句块,可以简单地写成:

if (x != 0)
	printf("x is nonzero.\n");

语句块中也可以定义局部变量,例如:

void foo(void)
{
	int i = 0;
	{
		int i = 1;
		int j = 2;
		printf("i=%d, j=%d\n", i, j);
	}
	printf("i=%d\n", i); /* cannot access j here */
}

和函数的局部变量同样道理,每次进入语句块时为变量j分配存储空间,每次退出语句块时释放变量j的存储空间。语句块也构成一个作用域,和例 3.6 “作用域”的分析类似,如果整个源文件是一张大纸,foo函数是盖在上面的一张小纸,则函数中的语句块是盖在小纸上面的一张更小的纸。语句块中的变量i和函数的局部变量i是两个不同的变量,因此两次打印的i值是不同的;语句块中的变量j在退出语句块之后就没有了,因此最后一行的printf不能打印变量j,否则编译器会报错。语句块可以用在任何允许出现语句的地方,不一定非得用在if语句中,单独使用语句块通常是为了定义一些比函数的局部变量更“局部”的变量。

习题

1、以下程序段编译能通过,执行也不出错,但是执行结果不正确(根据第 3 节 “程序的调试”的定义,这是一个语义错误),请分析一下哪里错了。还有,既然错了为什么编译能通过呢?

int x = -1;
if (x > 0);
	printf("x is positive.\n");