C陷阱与缺陷代码分析之第2章语法陷阱
作者:劉昊昱?
博客:http://blog.csdn.net/liuhaoyutz
?
陷阱1 理解函數(shù)聲明
作者提出一個(gè)問題:有一個(gè)首地址為0的函數(shù),該函數(shù)返回值類型為void,沒有參數(shù)。怎樣用C語言的語句調(diào)用這個(gè)函數(shù)?
答案是(*(void? (*)())0)();
?
要理解這個(gè)調(diào)用形式,要清楚如下兩個(gè)問題:
一是函數(shù)指針。
假設(shè)fp是一個(gè)函數(shù)指針,則調(diào)用fp所指向的函數(shù)的方法是
(*fp)();
因?yàn)閒p是一個(gè)函數(shù)指針,所以*fp是該指針?biāo)赶虻暮瘮?shù),所以(*fp)()就是調(diào)用該函數(shù)的方式。ANSI C允許將(*fp)()簡寫為fp(),fp()也是我們比較常見的形式,但是一定要知道這種寫法是一種簡寫形式。例如prinf()函數(shù),printf就是函數(shù)指針,它的完整形式是(*printf)()。為了說明這個(gè)問題,我們來看一個(gè)測試程序page17.c,代碼如下:
?
1#include <stdio.h> 2 3int main() 4{ 5 printf("test1\n"); 6 (*printf)("test2\n"); 7 8 return 0; 9}?
?
編譯運(yùn)行結(jié)果如下:
?
二是強(qiáng)制類型轉(zhuǎn)換符的聲明方式。
某類型的強(qiáng)制類型轉(zhuǎn)換符,只需要把該類型變量聲明中的變量名和聲明末尾的分號(hào)去掉,再將剩余的部分用一個(gè)括號(hào)封裝起來即可。例如,聲明一個(gè)int型指針變量的方式是
int *p;
按照上面的原則,int型指針強(qiáng)制類型轉(zhuǎn)換符就是把變量名p和末尾的分號(hào)去掉,再把剩余的部分用一個(gè)括號(hào)封裝起來,即(int *)。
同理,聲明一個(gè)返回值為void,沒有參數(shù)的函數(shù)指針變量f的方式是:
void (*f)();
按照上面的原則,返回值為void,沒有參數(shù)的函數(shù)指針類型強(qiáng)制轉(zhuǎn)換符就是把變量名f和最后的分號(hào)去掉,再把剩余的部分用一個(gè)括號(hào)封裝起來,即(void (*)())。
?
有了上面的預(yù)備知識(shí),我們可以來看作者提出的問題了。首地址為0的函數(shù),也就是函數(shù)指針的值為0,函數(shù)返回值類型為void,沒有參數(shù)。所以我們把0強(qiáng)制轉(zhuǎn)換為(void (*)())類型就是該函數(shù)的函數(shù)指針,有了函數(shù)指針,要調(diào)用該函數(shù),則是(*(void (*)())0)();
如果使用typedef能夠使表述更加清晰:
typedef void (*funcptr)();
(*(funcptr)0)();
?
作者舉的第二個(gè)例子是signal函數(shù),其函數(shù)聲明如下:
void (*signal(int, void(*)(int)))(int);
怎樣來理解這個(gè)函數(shù)聲明呢?
signal函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)是一個(gè)整數(shù),代表需要“被捕獲”的特定信號(hào)。第二個(gè)參數(shù)是一個(gè)函數(shù)指針,它是信號(hào)處理函數(shù)指針,它的返回值類型為void,該信號(hào)處理函數(shù)同樣有一個(gè)int型參數(shù)代表要處理的信號(hào)。
讓我們從信號(hào)處理函數(shù)開始,信號(hào)處理函數(shù)的函數(shù)指針聲明如下:
void (*sfp)(int);
信號(hào)處理函數(shù)指針類型可以通過把指針變量名sfp和最后的分號(hào)去掉得到,即:
void (*)(int)
signal函數(shù)的返回值是原來的信號(hào)處理函數(shù)指針,即singnal函數(shù)的返回值類型是void(*)(int)。
綜上所述可知,signal函數(shù)的聲明形式應(yīng)該是:
void (*signal(int, void(*)(int)))(int);
同樣地,使用typedef可以簡化signal函數(shù)的聲明:
typedef void (*HANDLER)(int);
HANDLER signal (int, HANDLER);
?
陷阱二運(yùn)算符的優(yōu)先級(jí)
記住C語言運(yùn)算符的優(yōu)先級(jí)是非常有益的,但是,C語言運(yùn)算符優(yōu)先級(jí)多達(dá)15個(gè),記住它們并不是一件容易的事。完整的C語言運(yùn)算符優(yōu)先級(jí)如下表所示:
| 優(yōu)先級(jí) | 運(yùn)算符 | 名稱或含義 | 使用形式 | 結(jié)合方向 | 說明 |
| 1 | [] | 數(shù)組下標(biāo) | 數(shù)組名[常量表達(dá)式] | 左到右 | |
| () | 圓括號(hào) | (表達(dá)式)/函數(shù)名(形參表) | |||
| . | 成員選擇(對(duì)象) | 對(duì)象.成員名 | |||
| -> | 成員選擇(指針) | 對(duì)象指針->成員名 | |||
| 2 | - | 負(fù)號(hào)運(yùn)算符 | -表達(dá)式 | 右到左 | 單目運(yùn)算符 |
| (類型) | 強(qiáng)制類型轉(zhuǎn)換 | (數(shù)據(jù)類型)表達(dá)式 | |||
| ++ | 自增運(yùn)算符 | ++變量名/變量名++ | 單目運(yùn)算符 | ||
| -- | 自減運(yùn)算符 | --變量名/變量名-- | 單目運(yùn)算符 | ||
| * | 取值運(yùn)算符 | *指針變量 | 單目運(yùn)算符 | ||
| & | 取地址運(yùn)算符 | &變量名 | 單目運(yùn)算符 | ||
| ! | 邏輯非運(yùn)算符 | !表達(dá)式 | 單目運(yùn)算符 | ||
| ~ | 按位取反運(yùn)算符 | ~表達(dá)式 | 單目運(yùn)算符 | ||
| sizeof | 長度運(yùn)算符 | sizeof(表達(dá)式) | |||
| 3 | / | 除 | 表達(dá)式/表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| * | 乘 | 表達(dá)式*表達(dá)式 | 雙目運(yùn)算符 | ||
| % | 余數(shù)(取模) | 整型表達(dá)式/整型表達(dá)式 | 雙目運(yùn)算符 | ||
| 4 | + | 加 | 表達(dá)式+表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| - | 減 | 表達(dá)式-表達(dá)式 | 雙目運(yùn)算符 | ||
| 5 | <<? | 左移 | 變量<<表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| >>? | 右移 | 變量>>表達(dá)式 | 雙目運(yùn)算符 | ||
| 6 | >? | 大于 | 表達(dá)式>表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| >= | 大于等于 | 表達(dá)式>=表達(dá)式 | 雙目運(yùn)算符 | ||
| <? | 小于 | 表達(dá)式<表達(dá)式 | 雙目運(yùn)算符 | ||
| <= | 小于等于 | 表達(dá)式<=表達(dá)式 | 雙目運(yùn)算符 | ||
| 7 | == | 等于 | 表達(dá)式==表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| != | 不等于 | 表達(dá)式!= 表達(dá)式 | 雙目運(yùn)算符 | ||
| 8 | & | 按位與 | 表達(dá)式&表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| 9 | ^ | 按位異或 | 表達(dá)式^表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| 10 | | | 按位或 | 表達(dá)式|表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| 11 | && | 邏輯與 | 表達(dá)式&&表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| 12 | || | 邏輯或 | 表達(dá)式||表達(dá)式 | 左到右 | 雙目運(yùn)算符 |
| 13 | ?: | 條件運(yùn)算符 | 表達(dá)式1? 表達(dá)式2: 表達(dá)式3 | 右到左 | 三目運(yùn)算符 |
| 14 | = | 賦值運(yùn)算符 | 變量=表達(dá)式 | 右到左 | |
| /= | 除后賦值 | 變量/=表達(dá)式 | |||
| *= | 乘后賦值 | 變量*=表達(dá)式 | |||
| %= | 取模后賦值 | 變量%=表達(dá)式 | |||
| += | 加后賦值 | 變量+=表達(dá)式 | |||
| -= | 減后賦值 | 變量-=表達(dá)式 | |||
| <<= | 左移后賦值 | 變量<<=表達(dá)式 | |||
| >>= | 右移后賦值 | 變量>>=表達(dá)式 | |||
| &= | 按位與后賦值 | 變量&=表達(dá)式 | |||
| ^= | 按位異或后賦值 | 變量^=表達(dá)式 | |||
| |= | 按位或后賦值 | 變量|=表達(dá)式 | |||
| 15 | , | 逗號(hào)運(yùn)算符 | 表達(dá)式,表達(dá)式,… | 左到右 | 從左向右順序運(yùn)算 |
如果把這些運(yùn)算符恰當(dāng)分組,并且理解了各組運(yùn)算符之間的相對(duì)優(yōu)先級(jí),那么這張表其實(shí)不難記住。
第一:
優(yōu)先級(jí)最高者其實(shí)并不是真正意義上的運(yùn)算符,包括:數(shù)組下標(biāo)、函數(shù)調(diào)用操作符,結(jié)構(gòu)成員選擇符。它們的優(yōu)先級(jí)是1級(jí),都是自左向右結(jié)合,因此a.b.c的含義是(a.b).c,而不是a.(b.c)。
第二:
單目運(yùn)算符的優(yōu)先級(jí)僅次于前述運(yùn)算符,它們的優(yōu)先級(jí)是2級(jí)。在所有真正意義上的運(yùn)算符中,它們的優(yōu)先級(jí)最高。單目運(yùn)算符是從右向左結(jié)合的,因此*p++會(huì)被編譯器解釋成*(p++),即取指針p所指向的對(duì)象,然后將p遞增1,而不是(*p)++,即取指針p所指向的對(duì)象,然后將該對(duì)象的值加1。
第三:
優(yōu)先級(jí)比單目運(yùn)算符低的,接下來就是雙目運(yùn)算符。
在雙目運(yùn)算符中,算術(shù)運(yùn)算符優(yōu)先級(jí)最高(乘、除、取余為3級(jí),加、減為4級(jí)),
移位運(yùn)算符次之(左移>>、右移<<,為5級(jí)),
關(guān)系運(yùn)算符再次之(如>、<、<=等等,為6級(jí),==和!=,為7級(jí)),
接著是邏輯運(yùn)算符(如按位與&、按位或|、邏輯與&&、邏輯或||,等等),
接下來是條件運(yùn)算符(?:其實(shí)這是一個(gè)三目運(yùn)算符),
賦值運(yùn)算符(=,/=,*=等等),
優(yōu)先級(jí)最低的是逗號(hào)運(yùn)算符(,)。
?
我們需要記住的最重要的一點(diǎn)是:
算術(shù)運(yùn)算符(加減乘除)>
移位運(yùn)算符(左移>>、右移<<) >
關(guān)系運(yùn)算符(大于>、小于<、等于==,等等) >
邏輯運(yùn)算符(按位與&、按位或|、邏輯與&&、邏輯或||,等等)
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/dyllove98/p/3199076.html
總結(jié)
以上是生活随笔為你收集整理的C陷阱与缺陷代码分析之第2章语法陷阱的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么用命令开远程主机的telnet服务
- 下一篇: 枚举举例