C語言list標頭檔案 超級炫酷的C語言技巧!

2021-10-10 17:40:23 字數 3780 閱讀 7581

百問科技

c語言常常讓人覺得它所能表達的東西非常有限。

它不具有類似第一級函式和模式匹配這樣的高階功能。

但是c非常簡單,並且仍然有一些非常有用的語法技巧和功能,只是沒有多少人知道罷了。

指定的初始化

很多人都知道像這樣來靜態地初始化陣列:

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  )
因為位寬為負,所以可以確定,如果標誌的數量超過了我們指派的空間,那麼編譯將會失敗。-end-

C語言的標頭檔案

1.在c檔案中在函式外部宣告變數,稱為為全域性變數。1 全域性變數如果加static關鍵字,表示只能在本檔案內訪問,而且只能從宣告處到檔案末尾的範圍內訪問。宣告時如果沒有初始化,會自動初始化乙個值,例如static int x,x預設值為0。如果a 函式中用到變數x,而x的定義 static int...

C語言的標頭檔案

標頭檔案 a.h ifndef a h define a h 定義方法,不提供實現 int add int a,int b endif add.c include a.h 實現標頭檔案a中定義的方法 int add int a,int b main.c include include a.h 若想在...

c語言標頭檔案

c語言標頭檔案 每個c c程式通常分為兩個檔案。乙個檔案用於儲存 程式的宣告 declaration 稱為標頭檔案。另乙個檔案用於儲存 程式的實現 implementation 稱為定義 definition 檔案。c c程式的標頭檔案以 h 為字尾,c程式的定義檔案以 c 為字尾,c 程式的定義檔...