看了以下幾份文件之後,還是不要自稱自己是C語言專家好了
10 C Tricks
雖然原來名稱叫10 C99 tricks,不過有些跟C99沒關係,不過還是有參考價值。
Ternary operator without middle operand (gnu extension)
1 | // Instead of |
個人覺得, 這個樣子更不容易看出程式在寫什麼
Unamed struct for compound type
根據實驗之後,這也不需要C99,C89就能正常運作了
在一般的情形之下,這樣子Compilier會提出警告
1 | struct { |
Clang發出這樣的警告
1 | demo.c:5:1: warning: declaration does not declare anything [-Wmissing-declarations] |
但是如果再union / struct當中這樣使用的話就沒有問題,可以依照自己喜歡的方式使用
1 | typedef union { |
IS_DEFINED macro
無法理解為什麼要設計的這麼複雜..
1 | // As used in the linux kernel. |
Convenience macro for Debuging
其實這個方法不限於OpenGL,像GetLastError
或errno
都可以用類似的方法來確認狀態。
1 | // Not really special, but so useful I thought |
Array size macro
這個也不世新玩意了
1 | // Is there any C project that does not use it? |
Safe-type macro (uses a gnu extension)
當然這也不是只用於min, max, 還可以用於swap等…弱化版的Template。
1 | #define min(a, b) ({ \ |
Passing pointer to unnamed variables to function.
這是一般的寫法
1 | void func(const int *arg); |
不過可以寫成這樣
1 | // We can write. |
更進一步
1 | // Can be useful with a helper macro. |
如果搭配上designated initializers威力更大
1 | typedef struct { |
Named initializer, with default values
designated initializers跟Variadic Macros的組合技
1 | // I use this one all the time when writing |
X macros
即使在C++當中,這也是個非常重要的技巧之一,利用Macro來進行Code Generation。
X Macro分成兩部分,一個是彼此相相關連的List,另外一個是巨集,對這個List進行展開動作,而這點Template無能為力。
例如
1 | #define COLORS \ |
當要新增一種顏色的時候,只需要在COLORS那邊修改,減少了維護和犯錯的可能性。
如果需要更進一步的學習,可以參考
- X Macro
- The X Macro
- The New C: X Macros
- Reduce C-language coding errors with X macros Part 1 Part 2 Part 3
- Real-world use of X-Macros
State machine helper using LINE
Generator的簡易實現,將S__LINE__來當做State之一。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34// This is a great trick.
// Instead of:
int iter(int state) {
switch (state) {
case 0:
printf("step 0\n");
return 1;
case 1:
printf("step 1\n");
return 2;
case 2:
printf("step 2\n");
return 3;
case 3:
return -1;
}
}
// We can define:
#define START switch(state) { case 0:
#define END return -1; }
#define YIELD return __LINE__; case __LINE__:;
// And now the function can be written
int iter(int state) {
START
printf("step 0\n");
YIELD
printf("step 1\n");
YIELD
printf("step 2\n");
END
}
Introduction to biicode
Introduce to LLVM C API
這篇是How to get started with the LLVM C API的讀後感,不過用我自己的方式表達。
先講結論
我重寫了程式碼,放在整篇文章的最後面,先看輸出結果。再回頭看程式碼。
1 | $ cc `llvm-config --cflags` -c sum.c |
這邊可以看兩個部份, Bitcode內容,以及JIT技術。之前介紹過LLVM的Bitcode,利用LLVM API可以生成Bitcode
建立Module
這邊就不特別提了,原來的連結寫得比較清楚。
1 | LLVMModuleRef mod = LLVMModuleCreateWithName("my_module"); |
Type safe handles in C++
看到這篇Type safe handles in C++覺得很有意思。原來可以這樣用。兩個同樣type,不過代表不同意義的Handle,要怎麼區別才安全。
Tag solution
1 | template<class Tag, class impl, impl default_value> |
Strong typedef
根據未來的C++ Proposal Toward Opaque Typedefs for C++1Y,用Boost_StrongTypedef可以達到類似的效果
1 | #include <boost/serialization/strong_typedef.hpp> |
Memory Model for C/C++11
其實是看了The C++ Memory Model
之後,對於之前懵懂的點有點茅塞頓開,寫下來記錄。
Pre-C/C++11
先來看以下這段Code
1 | int count = 0; |
就直覺上來說,r0拿到的值會是1,而事實往往不會這麼簡單。Compiler有可能把(1)和(2)的指令重排,因為對Single thread來說,如此重排不匯兌結果產生任何影響,如果我們就算強迫Compiler禁止指令重排,CPU也會有機會做這件事。
Singleton implementation and discussion in C++
Singleton這個題目酸燃被出到爛了,不過變化實在千變萬化。列出幾種不錯的解決方式。
Meyers version
1 | class Singleton { |
非常有名的實作方式,在C++11的環境下是Thread-safe的,而C++98沒有這種保證。可以參考Is Meyers implementation of Singleton pattern thread safe?這個討論串。
GCC預設編譯時開啟static threadsafe的選項,所以C++11的程式碼可以正確運行,可以強制使用-fno-threadsafe-statics
關閉這功能。可以參考Are function static variables thread-safe in GCC?
Online learning Resource
Output intermediate format for GCC/CLANG
為了很多因素(降低Playform depdent / Optimization等。 GCC 跟 CLANG 都引進了一層中間層,這曾的目的是定義一個平台無關的指令集, 以老朋友hello來示範如何輸出中間產物。
1 | #include <stdio.h> |
Some misleading concept in C/C++
看到濟濟篇文章的總結,寫起來,免得忘了。
do-while & continue
以下這段Code應該一堆人猜錯
1 | int i = 0; |
continue
會跳到邏輯判斷的地方,而不是Block的最前端。
sizeof
sizeof是compile-time的operator,所以以下這段code的結果會是4 0
1 | int i = 0; |
另外sizeof在不同的地方會有不同的表現
1 | int arr[SIZE][SIZE][SIZE]; |
裡面最令人驚奇的是print_sizeof中的第一個輸出值是4,因為第一維的index是pointer。
Static class variable in class
1 | class A { |
這邊的輸出結果沒有呼叫A的Constructor,因為a只宣告沒定義。
需要加上
1 | A B::a; |
如果我們沒有加上定義,這樣子是沒有問題的。
1 | class B { |
因為A是個empty class
所以就算沒定義也沒問題,如果不是的話就會出現編譯錯誤。
NULL pointer to an object
這段Code沒有問題
1 | class A { |
因為member function不涉及這物件的屬性任何操作,因此什麼都沒發生。