3. for语句
前两节我们在while
和do/while
循环中使用循环变量,其实使用循环变量最见的是for
循环这种形式。for
语句的语法是:
for (控制表达式1; 控制表达式2; 控制表达式3) 语句
如果不考虑循环体中包含continue
语句的情况(稍后介绍continue
语句),这个for
循环等价于下面的while
循环:
- 控制表达式1;
- while (控制表达式2) {
- 语句
- 控制表达式3;
- }
从这种等价形式来看,控制表达式1和3都可以为空,但控制表达式2是必不可少的,例如for (;1;) {...}
等价于while (1) {...}
死循环。C语言规定,如果控制表达式2为空,则认为控制表达式2的值为真,因此死循环也可以写成for (;;) {...}
。
上一节do/while
循环的例子可以改写成for
循环:
- int factorial(int n)
- {
- int result = 1;
- int i;
- for(i = 1; i <= n; ++i)
- result = result * i;
- return result;
- }
其中++i
这个表达式相当于i = i + 1
[9],++称为前缀自增运算符(Prefix Increment Operator),类似地,—称为前缀自减运算符(Prefix Decrement Operator)[10],--i
相当于i = i - 1
。如果把++i
这个表达式看作一个函数调用,除了传入一个参数返回一个值(等于参数值加1)之外,还产生一个Side Effect,就是把变量i
的值增加了1。
++
和--
运算符也可以用在变量后面,例如i++
和i--
,为了和前缀运算符区别,这两个运算符称为后缀自增运算符(Postfix Increment Operator)和后缀自减运算符(Postfix Decrement Operator)。如果把i++
这个表达式看作一个函数调用,传入一个参数返回一个值,返回值就等于参数值(而不是参数值加1),此外也产生一个Side Effect,就是把变量i
的值增加了1,它和++i
的区别就在于返回值不同。同理,--i
返回减1之后的值,而i--
返回减1之前的值,但这两个表达式都产生同样的Side Effect,就是把变量i
的值减了1。
使用++、—运算符会使程序更加简洁,但也会影响程序的可读性,[K&R]中的示例代码大量运用++、—和其它表达式的组合使得代码非常简洁。为了让初学者循序渐进,在接下来的几章中++、—运算符总是单独组成一个表达式而不跟其它表达式组合,从第 11 章 排序与查找开始将采用[K&R]的简洁风格。
我们看一个有意思的问题:a+++++b
这个表达式如何理解?应该理解成a++ ++ +b
还是a++ + ++b
,还是a + ++ ++b
呢?应该按第一种方式理解。编译的过程分为词法解析和语法解析两个阶段,在词法解析阶段,编译器总是从前到后找最长的合法Token。把这个表达式从前到后解析,变量名a
是一个Token,a
后面有两个以上的+号,在C语言中一个+号是合法的Token(可以是加法运算符或正号),两个+号也是合法的Token(可以是自增运算符),根据最长匹配原则,编译器绝不会止步于一个+号,而一定会把两个+号当作一个Token。再往后解析仍然有两个以上的+号,所以又是一个++运算符。再往后解析只剩一个+号了,是加法运算符。再往后解析是变量名b
。词法解析之后进入下一阶段语法解析,a
是一个表达式,表达式++还是表达式,表达式再++还是表达式,表达式再+b还是表达式,语法上没有问题。最后编译器会做一些基本的语义分析,这时就有问题了,++运算符要求操作数能做左值,a
能做左值所以a++
没问题,但表达式a++
的值只能做右值,不能再++了,所以最终编译器会报错。
C99规定了一种新的for
循环语法,在控制表达式1的位置可以有变量定义。例如上例的循环变量i
可以只在for
循环中定义:
- int factorial(int n)
- {
- int result = 1;
- for(int i = 1; i <= n; i++)
- result = result * i;
- return result;
- }
如果这样定义,那么变量i
只是for
循环中的局部变量而不是整个函数的局部变量,相当于第 1 节 “if语句”讲过的语句块中的局部变量,在循环结束后就不能再使用i
这个变量了。这个程序用gcc
编译要加上选项-std=c99
。这种语法也是从C++借鉴的,考虑到兼容性不建议使用这种写法。
[9] 这两种写法在语义上稍有区别,详见第 2.1 节 “复合赋值运算符”。
[10] increment和decrement这两个词很有意思,大多数字典都说它们是名词,但经常被当成动词用,在计算机术语中,它们当动词用应该理解为increase by one和decrease by one。现代英语中很多原本是名词的都被当成动词用,字典都跟不上时代了,再比如transition也是如此。