【C語(yǔ)言C++編程學(xué)習(xí)筆記】一種很酷的 C 語(yǔ)言技巧乙墙,靈活運(yùn)用編程技巧讓你寫代碼事半功倍!

C語(yǔ)言常常讓人覺得它所能表達(dá)的東西非常有限。它不具有類似第一級(jí)函數(shù)和模式匹配這樣的高級(jí)功能听想。但是C非常簡(jiǎn)單腥刹,并且仍然有一些非常有用的語(yǔ)法技巧和功能,只是沒有多少人知道罷了汉买。

☆ 指定的初始化

很多人都知道像這樣來(lái)靜態(tài)地初始化數(shù)組:

int fibs[] = {1,1,2,3,5};

C99標(biāo)準(zhǔn)實(shí)際上支持一種更為直觀簡(jiǎn)單的方式來(lái)初始化各種不同的集合類數(shù)據(jù)(如:結(jié)構(gòu)體衔峰,聯(lián)合體和數(shù)組)。

☆ 數(shù)組

我們可以指定數(shù)組的元素來(lái)進(jìn)行初始化蛙粘。這非常有用垫卤,特別是當(dāng)我們需要根據(jù)一組#define來(lái)保持某種映射關(guān)系的同步更新時(shí)。來(lái)看看一組錯(cuò)誤碼的定義组题,如:

/* Entries may not correspond to actual numbers. Some entries omitted. */

#define EINVAL 1

#define ENOMEM 2

#define EFAULT 3

/* ... */

#define E2BIG? 7

#define EBUSY? 8

/* ... */

#define ECHILD 12

/* ... */

現(xiàn)在葫男,假設(shè)我們想為每個(gè)錯(cuò)誤碼提供一個(gè)錯(cuò)誤描述的字符串抱冷。為了確保數(shù)組保持了最新的定義崔列,無(wú)論頭文件做了任何修改或增補(bǔ),我們都可以用這個(gè)數(shù)組指定的語(yǔ)法旺遮。

char *err_strings[] = {

? ? ? [0] = "Success",

[EINVAL] = "Invalid argument",

[ENOMEM] = "Not enough memory",

[EFAULT] = "Bad address",

/* ... */

[E2BIG ] = "Argument list too long",

[EBUSY ] = "Device or resource busy",

/* ... */

[ECHILD] = "No child processes"

/* ... */

};

這樣就可以靜態(tài)分配足夠的空間赵讯,且保證最大的索引是合法的,同時(shí)將特殊的索引初始化為指定的值耿眉,并將剩下的索引初始化為0边翼。

☆ 結(jié)構(gòu)體與聯(lián)合體

用結(jié)構(gòu)體與聯(lián)合體的字段名稱來(lái)初始化數(shù)據(jù)是非常有用的。假設(shè)我們定義:

struct point {

int x;

int y;

int z;

然后我們這樣初始化struct point:

struct pointp ={.x =3, .y =4, .z =5};

當(dāng)我們不想將所有字段都初始化為0時(shí)鸣剪,這種作法可以很容易的在編譯時(shí)就生成結(jié)構(gòu)體组底,而不需要專門調(diào)用一個(gè)初始化函數(shù)。

對(duì)聯(lián)合體來(lái)說(shuō)筐骇,我們可以使用相同的辦法债鸡,只是我們只用初始化一個(gè)字段。

☆ 宏列表

C中的一個(gè)慣用方法铛纬,是說(shuō)有一個(gè)已命名的實(shí)體列表厌均,需要為它們中的每一個(gè)建立函數(shù),將它們中的每一個(gè)初始化告唆,并在不同的代碼模塊中擴(kuò)展它們的名字棺弊。這在Mozilla的源碼中經(jīng)常用到,我就是在那時(shí)學(xué)到這個(gè)技巧的擒悬。例如模她,在我去年夏天工作的那個(gè)項(xiàng)目中,我們有一個(gè)針對(duì)每個(gè)命令進(jìn)行標(biāo)記的宏列表懂牧。其工作方式如下:

#define FLAG_LIST(_)? ? ? ? ? ? ? ? ? \

? ? _(InWorklist)? ? ? ? ? ? ? ? ? ? ? \

? ? _(EmittedAtUses)? ? ? ? ? ? ? ? ?? \

? ? _(LoopInvariant)? ? ? ? ? ? ? ? ?? \

? ? _(Commutative)? ? ? ? ? ? ? ? ? ?? \

? ? _(Movable)? ? ? ? ? ? ? ? ? ? ? ?? \

? ? _(Lowered)? ? ? ? ? ? ? ? ? ? ? ?? \

? ? _(Guard)

}

它定義了一個(gè)FLAG_LIST宏缝驳,這個(gè)宏有一個(gè)參數(shù)稱之為 _ ,這個(gè)參數(shù)本身是一個(gè)宏,它能夠調(diào)用列表中的每個(gè)參數(shù)用狱。舉一個(gè)實(shí)際使用的例子可能更能直觀地說(shuō)明問題运怖。假設(shè)我們定義了一個(gè)宏DEFINE_FLAG,如:

#define DEFINE_FLAG(flag) flag,

?? enum Flag {

? ? ?? None = 0,

? ? ?? FLAG_LIST(DEFINE_FLAG)

? ? ?? Total

?? };

#undef DEFINE_FLAG

對(duì)FLAG_LIST(DEFINE_FLAG)做擴(kuò)展能夠得到如下代碼:

enum Flag {

None = 0,

DEFINE_FLAG(InWorklist)

DEFINE_FLAG(EmittedAtUses)

DEFINE_FLAG(LoopInvariant)

DEFINE_FLAG(Commutative)

DEFINE_FLAG(Movable)

DEFINE_FLAG(Lowered)

DEFINE_FLAG(Guard)

Total

};

接著夏伊,對(duì)每個(gè)參數(shù)都擴(kuò)展DEFINE_FLAG宏摇展,這樣我們就得到了enum如下:

enum Flag {

None = 0,

InWorklist,

EmittedAtUses,

LoopInvariant,

Commutative,

Movable,

Lowered,

Guard,

Total

};

接著,我們可能要定義一些訪問函數(shù)溺忧,這樣才能更好的使用flag列表:

#define FLAG_ACCESSOR(flag) \

bool is##flag() const {\

? ? return hasFlags(1 << flag);\

}\

void set##flag() {\

? ? JS_ASSERT(!hasFlags(1 << flag));\

? ? setFlags(1 << flag);\

}\

void setNot##flag() {\

? ? JS_ASSERT(hasFlags(1 << flag));\

? ? removeFlags(1 << flag);\

}

FLAG_LIST(FLAG_ACCESSOR)

#undef FLAG_ACCESSOR

一步步的展示其過程是非常有啟發(fā)性的咏连,如果對(duì)它的使用還有不解,可以花一些時(shí)間在gcc –E上鲁森。

☆ 編譯時(shí)斷言

這其實(shí)是使用C語(yǔ)言的宏來(lái)實(shí)現(xiàn)的非常有“創(chuàng)意”的一個(gè)功能祟滴。有些時(shí)候,特別是在進(jìn)行內(nèi)核編程時(shí)歌溉,在編譯時(shí)就能夠進(jìn)行條件檢查的斷言垄懂,而不是在運(yùn)行時(shí)進(jìn)行,這非常有用痛垛。不幸的是草慧,C99標(biāo)準(zhǔn)還不支持任何編譯時(shí)的斷言。

但是匙头,我們可以利用預(yù)處理來(lái)生成代碼漫谷,這些代碼只有在某些條件成立時(shí)才會(huì)通過編譯(最好是那種不做實(shí)際功能的命令)。有各種各樣不同的方式都可以做到這一點(diǎn)蹂析,通常都是建立一個(gè)大小為負(fù)的數(shù)組或結(jié)構(gòu)體舔示。最常用的方式如下:

/* Force a compilation error if condition is false, but also produce a result

* (of value 0 and type size_t), so it can be used e.g. in a structure

* initializer (or wherever else comma expressions aren't permitted). */

/* Linux calls these BUILD_BUG_ON_ZERO/_NULL, which is rather misleading. */

#define STATIC_ZERO_ASSERT(condition) (sizeof(struct { int:-!(condition); })? ? )

#define STATIC_NULL_ASSERT(condition) ((void *)STATIC_ZERO_ASSERT(condition)? ? )

/* Force a compilation error if condition is false */

#define STATIC_ASSERT(condition) ((void)STATIC_ZERO_ASSERT(condition))

如果(condition)計(jì)算結(jié)果為一個(gè)非零值(即C中的真值),即! (condition)為零值电抚,那么代碼將能順利地編譯惕稻,并生成一個(gè)大小為零的結(jié)構(gòu)體。如果(condition)結(jié)果為0(在C真為假)喻频,那么在試圖生成一個(gè)負(fù)大小的結(jié)構(gòu)體時(shí)缩宜,就會(huì)產(chǎn)生編譯錯(cuò)誤。

它的使用非常簡(jiǎn)單甥温,如果任何某假設(shè)條件能夠靜態(tài)地檢查锻煌,那么它就可以在編譯時(shí)斷言。例如姻蚓,在上面提到的標(biāo)志列表中宋梧,標(biāo)志集合的類型為uint32_t,所以狰挡,我們可以做以下斷言:

STATIC_ASSERT(Total<=32)

它擴(kuò)展為:

(void)sizeof(struct { int:-!(Total <=32)? })

現(xiàn)在捂龄,假設(shè)Total<=32释涛。那么-!(Total <= 32)等于0,所以這行代碼相當(dāng)于:

(void)sizeof(struct { int:0 })

這是一個(gè)合法的C代碼【氩祝現(xiàn)在假設(shè)標(biāo)志不止32個(gè)唇撬,那么-!(Total <= 32)等于-1,所以這時(shí)代碼就相當(dāng)于:

(void)sizeof(struct {int: -1} )

因?yàn)槲粚挒樨?fù)展融,所以可以確定窖认,如果標(biāo)志的數(shù)量超過了我們指派的空間,那么編譯將會(huì)失敗告希。


自學(xué)C/C++不易扑浸,此路應(yīng)攜手前行。

歡迎關(guān)注我的編程公眾號(hào)【草莓味貍貓】燕偶!??

如果你想跟著小編一起學(xué)編程的話喝噪!

可以來(lái)下面的C語(yǔ)言C++編程學(xué)習(xí)基地

還有(源碼指么,零基礎(chǔ)教程酝惧,項(xiàng)目實(shí)戰(zhàn)教學(xué)視頻)!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末涧尿,一起剝皮案震驚了整個(gè)濱河市系奉,隨后出現(xiàn)的幾起案子檬贰,更是在濱河造成了極大的恐慌姑廉,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件翁涤,死亡現(xiàn)場(chǎng)離奇詭異桥言,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)葵礼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門号阿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鸳粉,你說(shuō)我怎么就攤上這事扔涧。” “怎么了届谈?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵枯夜,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我艰山,道長(zhǎng)湖雹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任曙搬,我火速辦了婚禮摔吏,結(jié)果婚禮上鸽嫂,老公的妹妹穿的比我還像新娘。我一直安慰自己征讲,他們只是感情好据某,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著诗箍,像睡著了一般哗脖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扳还,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天才避,我揣著相機(jī)與錄音,去河邊找鬼氨距。 笑死桑逝,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俏让。 我是一名探鬼主播楞遏,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼首昔!你這毒婦竟也來(lái)了寡喝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勒奇,失蹤者是張志新(化名)和其女友劉穎预鬓,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赊颠,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡格二,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了竣蹦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片顶猜。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖痘括,靈堂內(nèi)的尸體忽然破棺而出长窄,到底是詐尸還是另有隱情,我是刑警寧澤纲菌,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布挠日,位于F島的核電站,受9級(jí)特大地震影響驰后,放射性物質(zhì)發(fā)生泄漏肆资。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一灶芝、第九天 我趴在偏房一處隱蔽的房頂上張望郑原。 院中可真熱鬧唉韭,春花似錦、人聲如沸犯犁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)酸役。三九已至住诸,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間涣澡,已是汗流浹背贱呐。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留入桂,地道東北人奄薇。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像抗愁,于是被迫代替她去往敵國(guó)和親馁蒂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345