第02章 語法"陷阱"

《C陷阱與缺陷》 Andrew Koenig 讀書筆記 附錄來自網絡


2.1 理解函數的聲明

2.1.1 如何聲明一個給定類型的變量

任何c變量的聲明都由兩部分組成:類型以及一組類似表達式的聲明符

float f,g;  //f和g的類型為float

float ((f));//((f))的類型為浮點型

float ff(); //表達式ff()求值結果為float

float *pf;  //*pf是一個浮點數,也就是pf指向一個浮點數的指針
float *g(),(*h)();

表示*g( )與(*h)( )是浮點表達式。

因為( )結合優于*,*g( )也就是*(g( ));
g是一個函數,返回值為float的指針
h是一個函數指針,h指向的函數的返回值為浮點類型

2.1.2 如何得到一個類型的轉換符

一旦知道了如何聲明一個給定類型的變量,那么該類型的類型轉換符就是:
把聲明中的變量名和聲明末尾的分號去掉,再將剩下的部分用一個括號整個“封裝”起來即可
float (*h)();
表示h是一個返回值為float的函數指針,因此,
(float (*)())
表示一個“指向返回值為浮點類型的函數的指針”的類型轉換符

2.1.3 分析(*(void(*)())0)();

上面的程序是調用首地址為0位置的例程。

  1. 第一步 前導知識
    假定fp是一個函數的指針,如何調用fp所指的函數?(*fp)()

fp指向一個函數,*fp就是該指針指向的函數,(*fp)( )就是函數的調用了
注意:在ANSI C 標準中可以簡寫為fp( )但這只是一種簡寫形式

注意*fp兩側的括號,非常重要,因為()的優先級高于*,所以在沒有括號的情況下就變成了*(fp( )),由于fp()(*fp)()的簡寫,故也變為了*((*fp)())

  1. 第二步 分析
    (*0)();
    上面的式子是錯誤的,因為*操作的對象必須是一個指針,而0是一個常量
    那么就需要將常數0類型轉換為函數的指針

指針實際上是一個4個字節用來保存地址的數,如果將0轉化為函數的指針,由于指針的值為0,故轉換后會變成指向0地址的函數的指針

由2.1.1可以知道,轉化為返回值為void類型的函數的指針加上(void(*)( ))即可

(* (void(*)()) 0 )()

  1. 進一步擴展

利用typede可以更加簡化函數的聲明
例如:void (*signal(int,void(*)(int)))(int)
可以簡化為:

typedef void (*HANDLER)(int);

HANDLER signal (int,HANDLER);

關于上面typedef的解讀,見下面的附錄

2.2 運算符的優先級問題

記住優先級很好,因為太多的括號會影響閱讀,如果記不住,還是加括號吧。

7985c30f-5537-4e87-8506-14c026ba478b.png

記住三點:

  1. 任何一個邏輯運算符的優先級低于任何一個關系運算符(加減乘除...)
  2. 位移運算符的優先級比算數運算符要低,但比關系運算符(與或非...)要高。
  3. 三目運算符優先級最低

2.3 注意語句結束的;

據說90%的錯誤都是因為;

2.4 switch 語句

注意其中的break;語句。
漏掉后會造成不可預計的錯誤,當然有意的漏寫,可以實現特別的控制結構。

2.5 函數調用

不帶參數也需要加參數列表;
f();
而不是
f;

2.6 "懸掛"else引發的問題

1a62cb7e-4ca9-4f58-a847-0a3dee3fc8f7.png

這段代碼中的else實際作用是在第二個if之后。

建議:使用{ }哪怕只有一句執行語句。


附錄

關于typedef

在signal函數中有這樣的定義:

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

此處由于加了typedef自定義了一個新類型sighandler_t,所以第二行的函數原型看起來順眼多了,形式跟int func(char c, int i)無異,但是如果看不懂typedef語句,這兩句話仍然是噩夢。

要理解typedef,只要記住一句話就差不多了,那就是:typedef在語句中所起的作用只不過是把語句原先定義變量的功能變成了定義類型的功能而已。我們只消看幾個例子立即明白。

例如語句 typedef int *apple; 理解它的正確步驟是這樣的:先別看typedef,就剩下int *apple; 這個語句再簡單不過,就是聲明了一個指向整型變量的指針apple (注意:定義只是一種特殊的聲明),加上typedef之后就解釋成聲明了一種指向整型變量指針的類型apple 。

現在,回過來看上面的這個函數原型 typedef void (*sighandler_t)(int),蓋住 typedef不看 ,再簡單不過,sighandler_t就是一個函數指針,指向的函數接受一個整型參數并返回一個無類型指針 。加上typedef之后sighandler_t就是一種新的類型,就可以像int一樣地去用它,不同的是它聲明是一種函數指針,這種指針指向的函數接受一個整型參數并返回一個無類型指針 。怎么樣?簡單吧。

例子:

再來做一個更酷的練習,請看:typedef char *(* c[10])(int **p);

去 掉typedef就變成char *(* c[10])(int **p),先不管這個語句有多難看,它一定是聲明了一個擁有10個元素的數組c對不對?okay沒什么了不起的,只不過這個數組c的元素有點特別,它們都 是函數指針,并且它們所指向的這些函數統統都接受一個二級指針然后返回一直指向字符型的指針。加上typedef之后,c就不是一個數組了,而是一種類型 了,什么類型現在你能說出來了吧。 _

說到typedef就不能不把它跟宏替換比較,typedef相對于宏替換是一種徹底的“替換”,#define之所以被稱為宏替換,是因為它就是簡單地照搬替換字符串。來看個例子:

  例1    typedef int x[10];

  例2    #define x int[10]

例1定義了類型x,此時我們就可以用它來定義別的變量了,比如x y; 此時y是一個擁有10個整型變量的數組,效果與語句int y[10]無異。typedef帶給我們的是一種徹底的封裝

例2用了宏定義的方式,將來在該宏的作用域范圍內的任何地方遇到的x都將被簡單地替換成int [10]。

注意到,宏替換結尾是不帶分號的,不同于typedef語句

對于typedef和宏可以有以下總結:

1、宏定義可以擴展,徹底封裝的typedef不可以。

//以下代碼完全沒問題:

#define apple int;

unsigned apple i;

//以下代碼則完全行不通:

typedef int apple;

unsigned apple i;

2、在連續聲明幾個變量的時候typedef可以完全保證變量是同一種類型,而宏替換無法保證。

#define apple int *;

apple a, b;     //a和b類型完全不同,a是指向整型變量的指針,b是整型變量。

typedef int *apple;

apple a, b;    //a和b的類型完全相同,都是指向整型變量的指針。

永遠要記住的是,typedef定義的是一種類型而不是變量,不能指望用它來定義一個變量

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,563評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,694評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,672評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,965評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,690評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,019評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,013評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,188評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,718評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,438評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,667評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,149評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,845評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,252評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,590評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,384評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,635評論 2 380

推薦閱讀更多精彩內容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,533評論 1 51
  • 指針是C語言中廣泛使用的一種數據類型。 運用指針編程是C語言最主要的風格之一。利用指針變量可以表示各種數據結構; ...
    朱森閱讀 3,470評論 3 44
  • 我一直覺得寫作就只是一個人的事情,所以我剛加入簡書的時候只是一個人悶頭瞎寫,也不認真看其它作者寫的文章,還自以為寫...
    九汝竹書閱讀 809評論 13 11
  • 打開這部電影時,我沒預料到一部動畫片可以讓我不寒而栗。 《瑪麗與馬克思》,只有不同明暗度的棕色畫面,黏土制作的人物...
    斷黛閱讀 386評論 0 0
  • 上海繁華的的表面下總是透著一種浸入心脾的冷漠,讓人感覺這個城市的孤獨,覺得自己與這個城市格格不入,而這只不過是人自...
    琪兒西西閱讀 2,175評論 0 3