1、函数声明
对于
<span style="font-size:18px;">(* (void(*) () 0));这样的函数,怎样理解呢?</span>
首先,我们都知道任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符。声明符从表面上看与表达式有些类似,对它求值应该返回一个声明中给定类型的结果。
最简单的声明符就是单个变量,例如:
<span style="font-size:18px;"><span style="font-size:18px;">float f, g;</span></span>
这个声明的含义是:当对其求值时,表达式f和g的类型为浮点型。因为声明符与表达式相似,所以我们也可以在声明符中任意使用括号,例如
<span style="font-size:18px;">float ((f));</span>
意义同上。
同样的逻辑也适用于函数和指针类型的声明。例如:
<span style="font-size:18px;">float ff();</span>
这个声明的含义是:表达式ff()求值结果时一个浮点数,即ff是一个返回值为浮点类型的函数。类似的,还有,
<span style="font-size:18px;">float *pf;</span>
这个声明的含义是:*pf是一个浮点数,即pf是一个指向浮点数的指针。
对于如下的:
<span style="font-size:18px;">float *g(), (*h)();</span>
表示*g()和(*h)()是浮点数表达式。因为()结合优先级高于*,*g()也就是*(g()):g是一个函数,该函数的返回值类型为指向浮点数的指针。对于(*h)(),h是一个函数指针,h所指向函数的返回值为浮点类型。
一旦我们知道了如何声明一个给定类型的变量,那么该类型的类型转换符就很容易得到了:只要把声明中变量名和声明末尾的分号去掉,再将剩余的部分用一个括号整个“封装”起来即可。例如:
<span style="font-size:18px;">float (*h)();</span>
<span style="font-size:18px;">(float (*) () )</span>
它表示一个“指向返回值为浮点类型的函数指针”的类型转换符。
现在来解释
<span style="font-size:18px;">(* (void)(*)() 0)()</span>
对于这个表达式,分两步解释:
第一步:假定变量fp是一个函数指针,那么如何调用fp所指向的函数呢?
调用方法如下:
<span style="font-size:18px;">(*fp) ();</span>
因为fp一个函数指针,那么*fp就是该指针所指向的函数,所以(*fp)()就是调用该函数的方式。
在上面的表达式中,两侧的括号非常重要,因为函数运算符()的优先级高于单目运算符*。如果*fp两侧没有括号,那么*fp()实际上与*(fp())的含义完全一致。
第二步:找到一个恰当的表达式来替换fp,
对于(*0) ();并不能生效,因为运算符*必须要一个指针来做操作数。这个指针还应该是一个函数指针,这样经运算符*作用后的结果才能作为函数被调用。因此,上式中必须对0做类型转换,转换后的类型可以大致描述为:“指向返回值为void类型的函数的指针”。
2、运算符优先级
例如:
我们想判断flag变量与FLAG变量的二进制表示中相同位置的数字是否一样
<span style="font-size:18px;">if(flag & FLAG)...</span>
上式的含义对大多数C程序员来说是显而易见的。if语句的判断括号内表达式的值是否为0。考虑到可读性,如果对表达式的值是否为0的判断能够显示地加以说明,无疑使得代码自身就起到了注释该段代码意图的作用。其写法如下:
<span style="font-size:18px;">if(flag & FLAG != 0)...</span>
但是这是一个错误的语句因为!=运算符的优先级要高于&运算符,所以上式实际上被解释为:
<span style="font-size:18px;">if(flag & (FLAG != 0))...</span>
C语言运算符优先级如下
优先级 | 运算符 | 实例 | 结合性 | 优先级 | 运算符 | 实例 | 结合性 |
1 | () | (a+b)/4; | 从 左 向 右 | 7 | < | if(i<42)… | 从 左 向 右 |
[] | array[4]=2; | <= | if(i<=42)… | ||||
-> | ptr->age=34; | > | if(i>42)… | ||||
. | obj.age=34; | >= | if(i>=42)… | ||||
:: | Class::age=2; | 8 | == | if(i==42)… | 从左向右 | ||
++ | for(i=0;i<10;i++).. | != | if(i!=42)… | ||||
-- | for(i=0;i<10;i--)… | 9 | & | flags=flags&42; | 从左向右 | ||
2 | ! | if(!done)… | 从 右 向 左 | 10 | ^ | flags=flags^42; | 从左向右 |
~ | flags = ~flags; | 11 | | | flags=flags|42; | 从左向右 | ||
++ | for(i=0;i<10;++i)… | 12 | && | if(a && b)… | 从左向右 | ||
-- | for(i=0;i<10;--i)… | 13 | || | if(a || b)… | 从左向右 | ||
- | int i = -1; | 14 | ?: | int i=(a>b)?a:b | 从右向左 | ||
+ | int i = +1; | 15 | = | int a=b; | 从 右 向 左 | ||
* | data = *ptr; | += | a+=3; | ||||
& | address=&obj; | -= | b-=4; | ||||
(type) | int i=(int)floatNum; | *= | a*=5; | ||||
sizeof | int size=sizeof(floatNum); | /= | a/=2; | ||||
3 | ->* | ptr->*var=24; | 从左向右 | %= | a%=3; | ||
.* | obj.*var=24; | &= | a&=new_a; | ||||
4 | * | int i=2*4; | 从左向右 | ^= | a^=new_a; | ||
/ | float f=10/3; | |= | a|=new_a; | ||||
% | int ren=4%3; | <<= | flag<<=2; | ||||
5 | + | int i=2+3; | 从左向右 | >>= | flag>>=2; | ||
- | int i=5-1; | 16 | , | for(i=0;i<10;i++,j++) | 从左向右 | ||
6 | << | int flags=33<<1; | 从左向右 | ||||
>> | int flags=33>>1; |
注:(1)任何一个逻辑运算符的优先级低于任何一个关系运算符
(2)移位运算符的优先级比算术运算符要低,但是比关系运算符要高。