第02章 語(yǔ)法"陷阱"

《C陷阱與缺陷》 Andrew Koenig 讀書(shū)筆記 附錄來(lái)自網(wǎng)絡(luò)


2.1 理解函數(shù)的聲明

2.1.1 如何聲明一個(gè)給定類(lèi)型的變量

任何c變量的聲明都由兩部分組成:類(lèi)型以及一組類(lèi)似表達(dá)式的聲明符

float f,g;  //f和g的類(lèi)型為float

float ((f));//((f))的類(lèi)型為浮點(diǎn)型

float ff(); //表達(dá)式ff()求值結(jié)果為float

float *pf;  //*pf是一個(gè)浮點(diǎn)數(shù),也就是pf指向一個(gè)浮點(diǎn)數(shù)的指針
float *g(),(*h)();

表示*g( )與(*h)( )是浮點(diǎn)表達(dá)式。

因?yàn)? )結(jié)合優(yōu)于*,*g( )也就是*(g( ));
g是一個(gè)函數(shù),返回值為float的指針
h是一個(gè)函數(shù)指針,h指向的函數(shù)的返回值為浮點(diǎn)類(lèi)型

2.1.2 如何得到一個(gè)類(lèi)型的轉(zhuǎn)換符

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

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

上面的程序是調(diào)用首地址為0位置的例程。

  1. 第一步 前導(dǎo)知識(shí)
    假定fp是一個(gè)函數(shù)的指針,如何調(diào)用fp所指的函數(shù)?(*fp)()

fp指向一個(gè)函數(shù),*fp就是該指針指向的函數(shù),(*fp)( )就是函數(shù)的調(diào)用了
注意:在ANSI C 標(biāo)準(zhǔn)中可以簡(jiǎn)寫(xiě)為fp( ),但這只是一種簡(jiǎn)寫(xiě)形式

注意*fp兩側(cè)的括號(hào),非常重要,因?yàn)?code>()的優(yōu)先級(jí)高于*,所以在沒(méi)有括號(hào)的情況下就變成了*(fp( )),由于fp()(*fp)()的簡(jiǎn)寫(xiě),故也變?yōu)榱?code>*((*fp)())

  1. 第二步 分析
    (*0)();
    上面的式子是錯(cuò)誤的,因?yàn)?操作的對(duì)象必須是一個(gè)指針,而0是一個(gè)常量
    那么就需要將常數(shù)0類(lèi)型轉(zhuǎn)換為函數(shù)的指針

指針實(shí)際上是一個(gè)4個(gè)字節(jié)用來(lái)保存地址的數(shù),如果將0轉(zhuǎn)化為函數(shù)的指針,由于指針的值為0,故轉(zhuǎn)換后會(huì)變成指向0地址的函數(shù)的指針

由2.1.1可以知道,轉(zhuǎn)化為返回值為void類(lèi)型的函數(shù)的指針加上(void(*)( ))即可

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

  1. 進(jìn)一步擴(kuò)展

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

typedef void (*HANDLER)(int);

HANDLER signal (int,HANDLER);

關(guān)于上面typedef的解讀,見(jiàn)下面的附錄

2.2 運(yùn)算符的優(yōu)先級(jí)問(wèn)題

記住優(yōu)先級(jí)很好,因?yàn)樘嗟睦ㄌ?hào)會(huì)影響閱讀,如果記不住,還是加括號(hào)吧。

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

記住三點(diǎn):

  1. 任何一個(gè)邏輯運(yùn)算符的優(yōu)先級(jí)低于任何一個(gè)關(guān)系運(yùn)算符(加減乘除...)
  2. 位移運(yùn)算符的優(yōu)先級(jí)比算數(shù)運(yùn)算符要低,但比關(guān)系運(yùn)算符(與或非...)要高。
  3. 三目運(yùn)算符優(yōu)先級(jí)最低

2.3 注意語(yǔ)句結(jié)束的;

據(jù)說(shuō)90%的錯(cuò)誤都是因?yàn)?code>;

2.4 switch 語(yǔ)句

注意其中的break;語(yǔ)句。
漏掉后會(huì)造成不可預(yù)計(jì)的錯(cuò)誤,當(dāng)然有意的漏寫(xiě),可以實(shí)現(xiàn)特別的控制結(jié)構(gòu)。

2.5 函數(shù)調(diào)用

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

2.6 "懸掛"else引發(fā)的問(wèn)題

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

這段代碼中的else實(shí)際作用是在第二個(gè)if之后。

建議:使用{ }哪怕只有一句執(zhí)行語(yǔ)句。


附錄

關(guān)于typedef

在signal函數(shù)中有這樣的定義:

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

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

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

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

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

例子:

再來(lái)做一個(gè)更酷的練習(xí),請(qǐng)看:typedef char *(* c[10])(int **p);

去 掉typedef就變成char *(* c[10])(int **p),先不管這個(gè)語(yǔ)句有多難看,它一定是聲明了一個(gè)擁有10個(gè)元素的數(shù)組c對(duì)不對(duì)?okay沒(méi)什么了不起的,只不過(guò)這個(gè)數(shù)組c的元素有點(diǎn)特別,它們都 是函數(shù)指針,并且它們所指向的這些函數(shù)統(tǒng)統(tǒng)都接受一個(gè)二級(jí)指針然后返回一直指向字符型的指針。加上typedef之后,c就不是一個(gè)數(shù)組了,而是一種類(lèi)型 了,什么類(lèi)型現(xiàn)在你能說(shuō)出來(lái)了吧。 _

說(shuō)到typedef就不能不把它跟宏替換比較,typedef相對(duì)于宏替換是一種徹底的“替換”,#define之所以被稱(chēng)為宏替換,是因?yàn)樗褪呛?jiǎn)單地照搬替換字符串。來(lái)看個(gè)例子:

  例1    typedef int x[10];

  例2    #define x int[10]

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

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

注意到,宏替換結(jié)尾是不帶分號(hào)的,不同于typedef語(yǔ)句

對(duì)于typedef和宏可以有以下總結(jié):

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

//以下代碼完全沒(méi)問(wèn)題:

#define apple int;

unsigned apple i;

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

typedef int apple;

unsigned apple i;

2、在連續(xù)聲明幾個(gè)變量的時(shí)候typedef可以完全保證變量是同一種類(lèi)型,而宏替換無(wú)法保證。

#define apple int *;

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

typedef int *apple;

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

永遠(yuǎn)要記住的是,typedef定義的是一種類(lèi)型而不是變量,不能指望用它來(lái)定義一個(gè)變量

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,675評(píng)論 1 51
  • 指針是C語(yǔ)言中廣泛使用的一種數(shù)據(jù)類(lèi)型。 運(yùn)用指針編程是C語(yǔ)言最主要的風(fēng)格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,614評(píng)論 3 44
  • 我一直覺(jué)得寫(xiě)作就只是一個(gè)人的事情,所以我剛加入簡(jiǎn)書(shū)的時(shí)候只是一個(gè)人悶頭瞎寫(xiě),也不認(rèn)真看其它作者寫(xiě)的文章,還自以為寫(xiě)...
    九汝竹書(shū)閱讀 848評(píng)論 13 11
  • 打開(kāi)這部電影時(shí),我沒(méi)預(yù)料到一部動(dòng)畫(huà)片可以讓我不寒而栗。 《瑪麗與馬克思》,只有不同明暗度的棕色畫(huà)面,黏土制作的人物...
    斷黛閱讀 416評(píng)論 0 0
  • 上海繁華的的表面下總是透著一種浸入心脾的冷漠,讓人感覺(jué)這個(gè)城市的孤獨(dú),覺(jué)得自己與這個(gè)城市格格不入,而這只不過(guò)是人自...
    琪兒西西閱讀 2,367評(píng)論 0 3

友情鏈接更多精彩內(nèi)容