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)移位運算符的優先級比算術運算符要低,但是比關係運算符要高。