表达式和基本语句

时间:2022-09-08 14:55:17

 

表达式和基本语句

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

+  -  *  &

从右至左

 

*  /  %

从左至右

+  -

从左至右

<<  >>

从左至右

<   <=   >  >=

从左至右

==  !=

从左至右

&

从左至右

^

从左至右

|

从左至右

&&

从左至右

||

从右至左

?:

从右至左

=  +=  -=  *=  /=  %=  &=  ^=

|=  <<=  >>=

从左至右

运算符的优先级与结合律