指定的初始化
int
fibs = ;
c99標準實際上支援一種更為直觀簡單的方式來初始化各種不同的集合類資料(如:結構體,聯合體和陣列)。
陣列
我們可以指定陣列的元素來進行初始化。這非常有用,特別是當我們需要根據一組#define來保持某種對映關係的同步更新時。來看看一組錯誤碼的定義,如:
/* 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
/* ... */
現在,假設我們想為每個錯誤碼提供乙個錯誤描述的字串。為了確保陣列保持了最新的定義,無論標頭檔案做了任何修改或增補,我們都可以用這個陣列指定的語法。
char
*err_strings = ;
這樣就可以靜態分配足夠的空間,且保證最大的索引是合法的,同時將特殊的索引初始化為指定的值,並將剩下的索引初始化為0。
結構體與聯合體
用結構體與聯合體的欄位名稱來初始化資料是非常有用的。假設我們定義:
struct
point
然後我們這樣初始化
struct
point:
struct
point p = ;
當我們不想將所有欄位都初始化為0時,這種作法可以很容易的在編譯時就生成結構體,而不需要專門呼叫乙個初始化函式。
對聯合體來說,我們可以使用相同的辦法,只是我們只用初始化乙個字段。
巨集列表
c中的乙個慣用方法,是說有乙個已命名的實體列表,需要為它們中的每乙個建立函式,將它們中的每乙個初始化,並在不同的**模組中擴充套件它們的名字。這在mozilla的原始碼中經常用到,我就是在那時學到這個技巧的。例如,在我去年夏天工作的那個專案中,我們有乙個針對每個命令進行標記的巨集列表。其工作方式如下:
#define flag_list(_) \
_(inworklist) \
_(emittedatuses) \
_(loopinvariant) \
_(commutative) \
_(movable) \
_(lowered) \
_(guard)
它定義了乙個flag_list巨集,這個巨集有乙個引數稱之為 _ ,這個引數本身是乙個巨集,它能夠呼叫列表中的每個引數。舉乙個實際使用的例子可能更能直觀地說明問題。假設我們定義了乙個巨集define_flag,如:
#define define_flag(flag) flag,
enum
flag ;
#undef define_flag
對flag_list(define_flag)做擴充套件能夠得到如下**:
enum
flag ;
接著,對每個引數都擴充套件define_flag巨集,這樣我們就得到了enum如下:
enum
flag ;
接著,我們可能要定義一些訪問函式,這樣才能更好的使用flag列表:
#define flag_accessor(flag)
bool
is##flag()
const
void
set##flag()
void
setnot##flag()
flag_list(flag_accessor)
#undef flag_accessor
一步步的展示其過程是非常有啟發性的,如果對它的使用還有不解,可以花一些時間在gcc –e上。
編譯時斷言
這其實是使用c語言的巨集來實現的非常有「創意」的乙個功能。有些時候,特別是在進行核心程式設計時,在編譯時就能夠進行條件檢查的斷言,而不是在執行時進行,這非常有用。不幸的是,c99標準還不支援任何編譯時的斷言。
但是,我們可以利用預處理來生成**,這些**只有在某些條件成立時才會通過編譯(最好是那種不做實際功能的命令)。有各種各樣不同的方式都可以做到這一點,通常都是建立乙個大小為負的陣列或結構體。最常用的方式如下:
/* 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 ) )
#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)計算結果為乙個非零值(即c中的真值),即! (condition)為零值,那麼**將能順利地編譯,並生成乙個大小為零的結構體。如果(condition)結果為0(在c真為假),那麼在試圖生成乙個負大小的結構體時,就會產生編譯錯誤。
它的使用非常簡單,如果任何某假設條件能夠靜態地檢查,那麼它就可以在編譯時斷言。例如,在上面提到的標誌列表中,標誌集合的型別為uint32_t,所以,我們可以做以下斷言:
static_assert(total <= 32)
它擴充套件為:
(
void
)
sizeof
(
struct
)
現在,假設total<=32。那麼-!(total <= 32)
等於0,所以這行**相當於:
(
void
)
sizeof
(
struct
)
這是乙個合法的c**。現在假設標誌不止32個,那麼-!(total <= 32)
等於-1,所以這時**就相當於:
(
void
)
sizeof
(
struct
)
因為位寬為負,所以可以確定,如果標誌的數量超過了我們指派的空間,那麼編譯將會失敗。
c語言程式設計技巧
c語言常常讓人覺得它所能表達的東西非常有限。它不具有類似第一級函式和模式匹配這樣的高階功能。但是c非常簡單,並且仍然有一些非常有用的語法技巧和功能,只是沒有多少人知道罷了。指定的初始化 很多人都知道像這樣來靜態地初始化陣列 c int fibs c99標準實際上支援一種更為直觀簡單的方式來初始化各種...
C語言程式設計技巧匯萃
程式設計函式集,詳細內容如下 讀鍵盤掃瞄碼 獲取機器日期 將浮點數轉化為字串 清除螢幕 顯示時間 列印乙個矩形 砍掉字串中所有空格 取子字串 從檔案中讀取字元 字串左靠齊 取左字串 向檔案寫資料 取右字串 開啟或關閉游標 喇叭發聲 時間延遲 正點報時 寫整數於檔案中 從檔案中讀取整數 報警字串右靠齊...
C語言 程式設計小技巧(二)
引數的書寫要完整,不要貪圖省事只寫引數的型別而省略引數名字。如果函式沒有引數,則用void填充。例如 voidsetvalue intwidth,intheight 良好的風格 voidsetvalue int int 不良的風格 floatgetvalue void 良好的風格 floatgetv...