C語言常常讓人覺得它所能表達的東西非常有限。它不具有類似第一級函數(shù)和模式匹配這樣的高級功能。但是C非常簡單,并且仍然有一些非常有用的語法技巧和功能,只是沒有多少人知道罷了。
一、指定的初始化 很多人都知道像這樣來靜態(tài)地初始化數(shù)組:
int fibs[] = {1, 1, 2, 3, 5};
C99標準實際上支持一種更為直觀簡單的方式來初始化各種不同的集合類數(shù)據(jù)(如:結(jié)構(gòu)體,聯(lián)合體和數(shù)組)。 二、數(shù)組 我們可以指定數(shù)組的元素來進行初始化。這非常有用,特別是當我們需要根據(jù)一組#define來保持某種映射關(guān)系的同步更新時。來看看一組錯誤碼的定義,如:
/* Entries may not correspond to actual numbers. Some entries omitted. */
/* ... */
/* ... */
/* ... */
現(xiàn)在,假設(shè)我們想為每個錯誤碼提供一個錯誤描述的字符串。為了確保數(shù)組保持了最新的定義,無論頭文件做了任何修改或增補,我們都可以用這個數(shù)組指定的語法。
這樣就可以靜態(tài)分配足夠的空間,且保證最大的索引是合法的,同時將特殊的索引初始化為指定的值,并將剩下的索引初始化為0。 三、結(jié)構(gòu)體與聯(lián)合體 用結(jié)構(gòu)體與聯(lián)合體的字段名稱來初始化數(shù)據(jù)是非常有用的。假設(shè)我們定義:char *err_strings[] = {
["Success", ] =
["Invalid argument", ] =
["Not enough memory", ] =
["Bad address", ] =
/* ... */
["Argument list too long", ] =
["Device or resource busy", ] =
/* ... */
["No child processes" ] =
/* ... */
};
struct point {
int x;
int y;
int z;
}
然后我們這樣初始化struct point:
struct point p = {.x = 3, .y = 4, .z = 5};
當我們不想將所有字段都初始化為0時,這種作法可以很容易的在編譯時就生成結(jié)構(gòu)體,而不需要專門調(diào)用一個初始化函數(shù)。
對聯(lián)合體來說,我們可以使用相同的辦法,只是我們只用初始化一個字段。 四、宏列表 C中的一個慣用方法,是說有一個已命名的實體列表,需要為它們中的每一個建立函數(shù),將它們中的每一個初始化,并在不同的代碼模塊中擴展它們的名字。這在Mozilla的源碼中經(jīng)常用到,我就是在那時學到這個技巧的。例如,在我去年夏天工作的那個項目中,我們有一個針對每個命令進行標記的宏列表。其工作方式如下:
#define FLAG_LIST(_)
_(EmittedAtUses)
_(LoopInvariant)
_(Commutative)
_(Movable)
_(Lowered)
_(Guard)
它定義了一個FLAG_LIST宏,這個宏有一個參數(shù)稱之為 _ ,這個參數(shù)本身是一個宏,它能夠調(diào)用列表中的每個參數(shù)。舉一個實際使用的例子可能更能直觀地說明問題。假設(shè)我們定義了一個宏DEFINE_FLAG,如:
對FLAG_LIST(DEFINE_FLAG)做擴展能夠得到如下代碼:enum Flag {
None = 0,
FLAG_LIST(DEFINE_FLAG)
Total
};
接著,對每個參數(shù)都擴展DEFINE_FLAG宏,這樣我們就得到了enum如下: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
};
接著,我們可能要定義一些訪問函數(shù),這樣才能更好的使用flag列表:enum Flag {
None = 0,
InWorklist,
EmittedAtUses,
LoopInvariant,
Commutative,
Movable,
Lowered,
Guard,
Total
};
bool is
return hasFlags(1 << flag);
}
void set
JS_ASSERT(!hasFlags(1 << flag));
setFlags(1 << flag);
}
void setNot
JS_ASSERT(hasFlags(1 << flag));
removeFlags(1 << flag);
}
FLAG_LIST(FLAG_ACCESSOR)
一步步的展示其過程是非常有啟發(fā)性的,如果對它的使用還有不解,可以花一些時間在gcc –E上。 五、編譯時斷言 這其實是使用C語言的宏來實現(xiàn)的非常有“創(chuàng)意”的一個功能。有些時候,特別是在進行內(nèi)核編程時,在編譯時就能夠進行條件檢查的斷言,而不是在運行時進行,這非常有用。不幸的是,C99標準還不支持任何編譯時的斷言。
但是,我們可以利用預處理來生成代碼,這些代碼只有在某些條件成立時才會通過編譯(最好是那種不做實際功能的命令)。有各種各樣不同的方式都可以做到這一點,通常都是建立一個大小為負的數(shù)組或結(jié)構(gòu)體。最常用的方式如下:
如果(condition)計算結(jié)果為一個非零值(即C中的真值),即! (condition)為零值,那么代碼將能順利地編譯,并生成一個大小為零的結(jié)構(gòu)體。如果(condition)結(jié)果為0(在C真為假),那么在試圖生成一個負大小的結(jié)構(gòu)體時,就會產(chǎn)生編譯錯誤。/* 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))
它的使用非常簡單,如果任何某假設(shè)條件能夠靜態(tài)地檢查,那么它就可以在編譯時斷言。例如,在上面提到的標志列表中,標志集合的類型為uint32_t,所以,我們可以做以下斷言:
STATIC_ASSERT(Total <= 32)
它擴展為:
(void)sizeof(struct { int:-!(Total <= 32) })
現(xiàn)在,假設(shè)Total<=32。那么-!(Total <= 32)等于0,所以這行代碼相當于:
(void)sizeof(struct { int: 0 })
這是一個合法的C代碼?,F(xiàn)在假設(shè)標志不止32個,那么-!(Total <= 32)等于-1,所以這時代碼就相當于:
(void)sizeof(struct { int: -1 } )
因為位寬為負,所以可以確定,如果標志的數(shù)量超過了我們指派的空間,那么編譯將會失敗。
原文標題:幾點實用的C語言技巧
文章出處:【微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
審核編輯:彭菁
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。
舉報投訴
-
C語言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136824 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4331瀏覽量
62618 -
編譯
+關(guān)注
關(guān)注
0文章
657瀏覽量
32871
原文標題:幾點實用的C語言技巧
文章出處:【微信號:strongerHuang,微信公眾號:strongerHuang】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
Linux內(nèi)核中GNU C擴展的一些常用C語言語法分析
13.1 總結(jié)前面12節(jié)的課程,主要針對 Linux 內(nèi)核中 GNU C 擴展的一些常用 C 語言語法進行了分析。GNU C 的這些擴展語法
發(fā)表于 12-14 06:29
C語言的特點有哪些呢
1.從語言特點來說①C語言有出色的可移植性,能在多種不同體系結(jié)構(gòu)的軟/硬平臺上運行。②簡潔緊湊,使用靈活的語法機制,并能直接訪問硬件能夠直接
發(fā)表于 12-15 08:16
C++語法的外圍基礎(chǔ)
程序?qū)?nèi)存空間的動態(tài)分配的頻繁程度要求簡化語言的相關(guān)語法的格式。C++語法規(guī)定new算符等效于C語言
發(fā)表于 03-15 16:55
?10次下載
C語言誤用易錯知識點與基本語法匯總
語言之所以能稱之為語言,它肯定是一種工具一種相互交流相互通信相互傳達之間的意圖的工具,作為語言那肯定得有自己的語法,要想相互交流肯定得先學好它的語法
發(fā)表于 05-05 15:32
?2052次閱讀
嵌入式C語言中的union語法的作用是什么
C語言中的結(jié)構(gòu)體語法是非常重要,也是非常有用的,相信看了最近幾節(jié)的讀者應該明白。事實上,在實際的C語言項目開發(fā)中,為了代碼的簡潔性,描述問題
發(fā)表于 08-29 17:10
?1849次閱讀
為什么要用C語言實現(xiàn)面向?qū)ο?/a>
不知道有多少人去了解過語言的發(fā)展史,早期C語言的語法功能其實比較簡單。隨著應用需求和場景的變化,
C語言是如何實現(xiàn)面向?qū)ο蟮?/a>
? ? 不知道有多少人去了解過語言的發(fā)展史,早期C語言的語法功能其實比較簡單。隨著應用需求和場景
Prel語法與C語言語法的異同綜述
Prel語法與C語言語法的異同綜述
發(fā)表于 05-25 11:44
?6次下載
為什么要用C語言實現(xiàn)面向?qū)ο?/a>
不知道有多少人去了解過語言的發(fā)展史,早期C語言的語法功能其實比較簡單。隨著應用需求和場景的變
C語言語法擴展
大家在看一些 GNU 開源軟件,或者閱讀 Linux 內(nèi)核、驅(qū)動源碼時會發(fā)現(xiàn),在 Linux 內(nèi)核源碼中,有大量的 C 程序看起來“怪怪的”。說它是C語言吧,貌似又跟教材中的寫法不太一
C語言實現(xiàn)面向?qū)ο蟮姆椒?/a>
不知道有多少人去了解過語言的發(fā)展史,早期C語言的語法功能其實比較簡單。隨著應用需求和場景的變化,
評論