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 [1],++称为前缀自增运算符(Prefix Increment Operator),类似地,--称为前缀自减运算符(Prefix Decrement Operator)[2], --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++ 借鉴的,考虑到兼容性不建议使用这种写法。
这两种写法在语义上稍有区别,详见 第 2.1 节“复合赋值运算符”。 ↩︎
increment 和 decrement 这两个词很有意思,大多数字典都说它们是名词,但经常被当成动词用,在计算机术语中,它们当动词用应该理解为 increase by one 和 decrease by one。现代英语中很多原本是名词的都被当成动词用,字典都跟不上时代了,再比如 transition 也是如此。 ↩︎