2_Syntactic Pitfalls 语法陷阱

                                第二章: 语法陷阱

  

  2.1 Understanding function declarations: (函数声明)


      1. 变量声明:

          可以声明一个浮点型变量为 : float  f;

例: 

          float ((f));            // 也是声明一个浮点型变量 f ;

          float ff();            // 声明一个函数,返回值类型为 float;

           float *pf;          // 声明一个指向 float 类型变量的指针;

          float *g();           // 声明一个返回值为 float 指针的函数;

          float (*h)();        // 声明一个函数指针,指向返回值为 float 类型的函数;


注:

          *g()  means  *( g() );   ( ) binds more tightly than *;


     2. 写一个表达式的 cast :


          将变量名和声明语句的分号去掉,并加上括号:

例:

          float ( *h )();


          ( float( * ) () );


所以对一个表达式为 :      ( *( void( * ) ( ) ) 0 ) ( )


     我们可以分析:


          1. 对于一个函数指针 fp 我们有:

               调用函数格式为 :      ( *fp ) ( )    简写为  fp( )

 注:

           函数结合优先级  >  一元操作符 , 所以有:

对于 *fp()  means  :  *( ( *fp ) ( ) ) 即为一个函数指针;


          2. 对于( *0 ) ( ) :

               把 *0 cast 成为 一个 pointer to function returning void;


所以由1.2. 我们有:


           void (*fp ) ( );    ==   ( void ( * ) ( ) ) 0


所以对于原式子,我们可以用上式子代替 fp 得:( *( void( * ) ( ) ) 0 ) ( )


用 typedef 定义有 :     typedef  void ( *funcptr ) ( );             ( *( funcptr ) 0 ) ( ) ;


          

  2.2 Operators dont always have the precedence you want : (运算优先级陷阱)


     1. if 中的判断优先级:

例:

     对一个 if( flags & FLAG ! = 0 )     

     

     目的是为了判断 flag 是否为零;但是是错误的!!!


因为 != 的优先级要高于 &, 所以原句等价于: if( flags & ( FLAT != 0 ) )



     2. 假设两个数 low 和 hi ( 取值为 0~15 ), 想用一个整数 r 高四位表示 hi ,第四位表示 low:


有:  r = hi << 4 + low;


     结果错误,因为加法运算的优先级要高于位移优先级,原句等价为: r = hi << ( 4 + low );


Code:

#include
typedef void (*funcptr)();

int arr[50];
int num = 0;

int main(){
	
	int low = 6,hi = 10;	//	low = 0110 , hi = 1010;
	
	int r = 0;
	
	r = hi;
	r = (r << 4) + low;
	
	BineryOutput(r);
	
	for(--num;	num >= 0;	num--)
	{
		printf("%d",arr[num]);
	}
	
	printf("\n");
} 

void BineryOutput(int n)
{
	while(n)
	{
		if((n%2) == 1)
		{
			arr[num] = 1;
		}
		else
		{
			arr[num] = 0;
		}
		num++;
		n = n/2;
	}
}

     3. 运算符优先顺序的规律:


          1. 优先级最高的,往往不是真正的运算符:


                   注释,函数调用,成员运算符 .  (左结合)


          2. 一元运算符运算顺序次之:


                  一元运算符在所有真正运算符中优先级最高;


          因为函数调用比一元运算符优先: 必须用 ( *p ) ( ) 调用函数;


          因为一元运算符为右结合性:      有 *p++ 等价于 *(p++);[ 先指向p ,之后p再自加 ]


          3. 二元运算符:


                    数学运算符的优先级最高,之后是位移运算符,关系,逻辑,赋值,条件运算符;

注:

               一、 关系运算符要大于逻辑运算符;

               二、 运算 > 位移 > 关系;


              乘法,除法和取余有着相同的优先级;  加法和减法有着相同的优先级;

例:

          1/2*a;     等价于  (1/2)*a ;


              六个关系运算符不等价,其中 != 和 == 为其中的低优先级;

例:

           a < b == c < d; 可以实现两个判别式的关系运算!!


              逻辑运算符的优先顺序全都不同;


          4. 三元运算符:(仅次于赋值运算 和 最低的逗号运算符)


 因为三元运算符的优先级不高,我们可以实现:


          tax_rate = income > 40000 && residency < 5 ? 3.5 : 2.0;

equals to:

          tax_rate = ( (income > 40000) && (residency < 5) ) ? 3.5 : 2.0;


          5. 赋值运算符(仅大于逗号运算符):


               home_score = visitor_score = 0; 可以省略一句;


          6. 逗号运算符(优先级别最低的运算符):


例: 复制一个文件的内容到另一个文件:


     while( ( c = gerc( in ) ) != EOF )

               putc( c, out );


看起来这个程序像是先把getc 的值传递给 c 之后 再判断是不是应该为 EOF。


但事实上: 赋值的优先级很低,所以将先会和EOF比较,之后将值( 取值为1 ) 赋值给 c ,一直到EOF。


例2:

          if( ( t = BTYPE( pt1 ->aty ) == STRTY )  || t = UNIONTY )  {   }


原本目的是判断 t 的值是不是和 STRTY or UNIONTY 相同,但是赋值优先级较低,产生错误;


  2.3 Watch those semicolons : (分号陷阱)


      通常,在C语言中多余的分号,将作为一条空语句,但是还有一些特殊情况:


          1. if 语句中的多加分号陷阱:

例:

               if( x[ i ] > big );

                    big = x[ i ];

将等价于:

               if(...) {  };

                    big = x[ i ];          即无论if判断如何,都执行下一条语句;


          2. if语句中的少加分号陷阱:

例:

                    if( n < 3 )

                         return ; 

                    logrec.data = x[ 0 ];

                    logrec.time = x[ 1 ];

                    logrec.code= x[ 2 ];

     这里少加了一个分号,所以将变为:


                    if( n < 3 )

                         return logrec.data = x[ 0 ];

                    logrec.time = ........


此时:

     如果函数的返回值为 void,编译系统会提示 error;

     但是如果未标明返回值,或者返回为int, 则会出现逻辑错误(但是编译正常通过);


          3. 函数声明之前少加分号:

例:

               struct logrec {

                         int data;

                         int time;

                         int code;

               .....

          } ;

               main( )

          {

               ....

          }

此时将造成 main函数的返回值为 struct logrec 类型,造成严重错误!



  2.4 The switch statement : (switch陷阱)


      在C语言中,switch 语句在没有break时,会连续执行之后的所有语句!

例:

      switch( color ){

     case 1 : printf("red");

               break;

     case 2 : printf( " yellow " );

                break;

     ..........

}


C语言中 需要break语句的原因是:


      case labels in C behave as true labels, in that control flows unimpeded right through a case label.



  2.5 Calling functions : (函数调用陷阱)


      函数调用的格式为 : f( );     对于 :  f;仅仅表示函数 f 的地址,但是不可以调用函数;


  2.6 The dangling else problem : (else函数调用陷阱)


例:

           if( x == 0 )

               if( y == 0 ) error();

          else{

               z = x + y;

               f( &z );

          }

编程的目的是有两个主要的判断 x = 0 或者 x ≠ 0;


但是:

          在C 语言中:

                     else 永远跟随在离他最近的一个在同一个大括号中的 if !

所以源程序应当修改为:

           if( x == 0 ){

               if( y == 0 )

                    error();

           }

          else{

               z = x + y;

               f( &z );

          }




你可能感兴趣的:(C,trap,and,pitfalls,C语言,C语言复习)