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)移位运算符的优先级比算术运算符要低,但是比关系运算符要高。