表达式和基本语句
1.表达式
1.1. 算术运算符
算术运算符包括一元运算符和二元运算符。
运算符的优先级和结合性
二元运算符(即*、/、%、+和-)都是左结合的,所以
i-j-k 等价于 (i-j)-k
一元运算符(+和-)都是右结合的,所以
-+i 等价于 -(+i)
1.2. 赋值运算符
1.2.1. 简单赋值
赋值运算符(=)是右结合的,所以
i=j=k=0;
等价于
i=(j=(k=0));
1.2.2. 左值
赋值运算符要求它左边的操作数必须是左值(lvalue)。左值表示存储在计算机内存中的对象,而不是常量和计算结果。变量是左值,而诸如10或2*1这样的表达式则不是左值。
下列赋值式子都是不合法的:
12 = i; /** WRORG**/
i + j = 0; /** WRORG**/
-i = j; /** WRORG**/
编译器将会检查出这个自然错误,并且显示出诸如”Lvalue required”这样的错误信息。
1.2.3. 复合赋值
复合赋值运算符:=、*=、/=、%=、+=、-=
例如:v+=e 表示v加上e,然后结果存在v中。
复合赋值运算符是右结合的,所以
i += j += k;
等价于
i += (j += k);
注意:表达式i += j与表达式 i = +j是不一样的。
1.3. 自增运算符和自减运算符
++(自增)和—(自减)
++i意味着“立即增一”,而i++则意味着“现在先用i的原始值,稍后再自增-”。
注意:后缀++和后缀—比一元的正号、负号优先级高,而且都是左结合的。前缀++和前缀—与一元的正号、负号优先级相同,而且都是右结合的。
1.4. 关系运算符
关系运算符:<、<=、>和>=
关系运算符在C语言的表达式时产生的结果是0或1。关系运算符可以用来比较整数和浮点数,以及允许的混合类型的操作数。关系运算符的优先级低于算术运算符,且是左结合的。比如
i<j<k
等价于
(i<j)<k
也就是说,表达式先检测i是否小于j,然后用比较后的结果1或0来和k进行比较。
1.5. 判等运算符
判等运算符(==和!=)也是左结合的,而且也是产生了0或1作为结果。判等运算符的优先级低于关系运算符。例如
i<j == j<k 等价于表达式 (i<j)==(j<k)
1.6. 逻辑运算符
!是一元运算符,而&&和||是二元运算符。逻辑运算符所产生的结果是0或1。
运算符&&和||都对操作数进行“短路”计算。也就是说首先计算出左侧操作数的值,然后是右侧操作数。如下:
(i != 0) && (j/i > 0)
为了得到此表达式的值,首先必须计算表达式(i != 0)的值。如果i不等于0,那么需要计算表达式(j/i > 0)的值,确定整个表达式的值为真还是为假。
运算符!的优先级和一元运算符正号、负号的优先级相同。运算符&&和||的优先级低于关系运算符和判等运算符。运算符!是右结合的,运算符&&和||是左结合的。
1.7. 条件表达式
表达式1?表达式2:表达式3
称为三元运算符,读作“如果表达式1成立,那么表达式2,否则表达式3“
条件运算符的优先级低于先前介绍过的所有运算符。
1.7. 逗号运算符
表达式1,表达式2
逗号表达式的计算通过两步来实现:第一步,计算表达式1并且扔掉计算出的值;第二步,计算表达式2的值,把这个值作为整个表达式的值。例如
++i,i+j时变量i先进行自增,然后在计算i+j,所以表达式的值为7。
逗号运算符的优先级低于所有其他运算符,是左结合的。
i=1,j=2,k=i+j;
等价于
((i=1,j=2),k=i+j;)
1.8. Q&A
问:如何对对数进行幂操作?
答:通过重复乘法运算的方法可以进行整数的小范围正整数次幂运算(i*i*i是i的立方运算)。如果想计算数的非正整数次幂运算,可以调用pow函数。
问:如果想吧%运算符用于浮点数,而程序又无法编译通过,怎么办?
答: %要求整数操作数,所以可以调用fmod函数。
问:为什么v+=e不等价于v=v+e?
答:计算v+=e只是导致求一次v的值,而v=v+e则会求两次v的值。任何副作用都能导致两次求v的值,在下面的例子中,i自增一次:
a[i++] += 2;
如果用=代替+=,则i会自增两次:
a[i++] = a[i++] +2;
问:++和—是否可以处理float型变量?
答:可以。自增和自减运算可以用于所有数值类型。
问:当用=代替==时编译器没有发出警告。是否有方法可以强制编译器注意这类问题?
答:可以。if(i == 0) …
习惯上改写成
if(0 == i) …
如果运算符==意外地写成了=;
if(0 = i) …
因为不可能给0赋值,所以编译器会产生错误信息。
2. 基本语句
2.1. if语句
if(表达式)
语句
else if(表达式)
语句
。。。
else if(表达式)
语句
else
语句
当嵌套if语句时,注意“悬空else“的问题。C语言遵循的规则是else子句应该属于离它最近的且还没有和其他else匹配的if语句。例如
if(y > 0)
if(x > 0)
result = x / y;
else
printf(“ERROR: y is equal to 0/n”);
为了使else子句属于外层的if语句,可以把内层的if语句用大括号括起来:
if(y > 0)
{
if(x > 0)
result = x / y;
}
else
printf(“ERROR: y is equal to 0/n”);
2.2. switch语句
switch (表达式){
case 常量表达式:多条语句
break;
。。。
case 常量表达式:多条语句
break;
case 常量表达式:多条语句
break;
default:多条语句
break;
}
switch语句等价于级联if语句。
2.3. while语句
while (表达式)语句
2.4. do语句
do 语句 while (表达式);
do语句本质上就是while语句,只不过do语句是在每次执行循环体之后对控制表达式进行判断。
2.5. for语句
for (表达式1;表达式2;表达式3)语句
除了极少数情况以外,for循环总可以用等价的while循环替换:
表达式1;
while(表达式2){
语句
表达式3;
}
当for循环体内含有continue语句时,for循环不再等价于while循环。
在for语句中省略表达式
如果忽略第一个表达式,那么在执行循环语句前没有初始化操作;
如果忽略第三个表达式,那么循环体有责任要确认第二个表达式的值最终会变为假。
如果忽略第一个和第三个表达式,循环结果和while语句没有任何分别。
如果忽略第二个表达式,那么它默认为真值,因此for语句不会终止。
2.6. break语句
break语句可以用来退出while、do和for循环,不过当出现循环嵌套时,只能跳出一层嵌套。
2.7. continue语句
continue语句把程序控制正好转移到循环体结束之前的一点,会把程序控制留在循环内。
2.8. goto语句
goto 标示符;
goto语句可以跳出多层嵌套。建议少用、慎用goto语句。
2.9. Q&A
问:如果i是int型变量,而f是float型变量,那么条件表达式(i>0?i:f)是哪一种类型的值?
答:当int和float型的值混合在一个条件表达式中,表达式的类型float型。如果i>0,那么变量i转化为float型后的值就是表达式的值。
for (row=0; row<100; row++) { for ( col=0; col<5; col++ ) { sum = sum + a[row][col]; } } |
for (col=0; col<5; col++ ) { for (row=0; row<100; row++) { sum = sum + a[row][col]; } } |
(a)长循环在最外层 (b)长循环在最内层
答:C++/C循环语句中,for语句使用频率最高,while语句其次,do语句很少用。在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU跨切循环层的次数。例如(b)的效率比(a)的高。
问:下面哪种写法,循环语句的效率更高?
for (i=0; i<N; i++) { if (condition) DoSomething(); else DoOtherthing(); } |
if (condition) { for (i=0; i<N; i++) DoSomething(); } else { for (i=0; i<N; i++) DoOtherthing(); } |
(c) 逻辑判断在循环体内 (d) 逻辑判断在循环体外
答:如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。示例(c)的程序比示例(d)多执行了N-1次逻辑判断。并且由于前者老要进行逻辑判断,打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。如果N非常大,最好采用(d)的写法,可以提高效率。如果N非常小,两者效率差别并不明显,采用示例(c)的写法比较好,因为程序更加简洁。
3. 运算符的优先级
C++/C语言的运算符有数十个,运算符的优先级与结合律下表所示。注意一元运算符 + - * 的优先级高于对应的二元运算符。
优先级 |
运算符 |
结合律 |
从
高
到
低
排
列 |
( ) [ ] -> . |
从左至右 |
! ~ ++ -- (类型) sizeof + - * & |
从右至左
|
|
* / % |
从左至右 |
|
+ - |
从左至右 |
|
<< >> |
从左至右 |
|
< <= > >= |
从左至右 |
|
== != |
从左至右 |
|
& |
从左至右 |
|
^ |
从左至右 |
|
| |
从左至右 |
|
&& |
从左至右 |
|
|| |
从右至左 |
|
?: |
从右至左 |
|
= += -= *= /= %= &= ^= |= <<= >>= |
从左至右 |
表 运算符的优先级与结合律