---
# System prepended metadata

title: C 語言黑魔法

---

# C 語言黑魔法

learn from [Few lesser known tricks, quirks and features of C](https://jorenar.com/blog/less-known-c)

**斟酌使用**

**有些章節沒有詳細說明通常是因為在原文中也沒詳細說明，有時間再補**

## Array Pointer

用 pointer 表示 array(decay-to-pointer):

```c
int arr[10];

// 1.
int *p_arr = arr;
p_arr[2] = 1;

// 2.
int (*p_arr2)[10] = &arr;
(*p_arr2)[2] = 1;
```

如果是單純要存取 arr 則直接用 arr[] 即可，也比較不容易搞錯

第二種做法有個好處是在撰寫多維陣列空間分配相當好用：

```c
int (*p_arr)[100][100] = malloc(sizeof(*p_arr));
```

這樣分配出來就是一個平坦的 100 x 100 一維空間，透過 p_arr 可以用二維方式去存取

初學的用法應該是像這種：

```c
int **a = malloc(sizeof(int*)*100);
for(int i = 0;i < 100;i++) {
    *(a + i) = malloc(sizeof(int)*100);
}
```

抽象來說兩者差不多，但實際上兩者的記憶體分布長的不太一樣

用 decay-to-pointer 還可以結合 VLA 來分配空間：

```c
int (*a)[N] = malloc(sizeof(*a));
```

另外一個使用 pointer to array 的優點是如果直接把 array 當作參數傳給 function ，則 array 會退化成 pointer 並且無法保存大小的資訊，但是如果是用 pointer to array ，那在 dereference 的時候可以拿到該 array 的大小：

```c=
void foo(int arr[10]) {
    // sizeof(*arr) == 4
    // sizeof(arr) == 8
}

void bar(int (*arr)[10]) {
    // sizeof(*arr) == 40
    // sizeof(arr) == 8
}
```

## Comma operator

介紹 , 當作 operator 的特性， wiki 有更多介紹 (https://en.wikipedia.org/wiki/Comma_operator#Examples) 

使用上也有一些常見的陷阱：

* assignment operator 優先權 > comma operator

一種比較酷但是不太直覺的寫法：

```c
if(/* ... */) {
    return (errno = ERRORCODE, -1);
}
```

這樣 errno 會被設定成 ERRORCODE 然後 function 會 return -1

等價於 

```c
if(/* ... */) {
    errno = ERRORCODE;
    return -1;
}
```

或是拿來避免撰寫 {} 用，不過我個人覺得避免 {} 沒啥太大意義


## Digraphs, trigraphs and alternative tokens

基本上鍵盤只要有 ASCII 字符集就能寫 code ，但不是每個鍵盤都有相對應的符號，所以 C 語言允許某些字元組合來表示必要的符號：

* <: -> [
* :> -> ]
...
諸如此類，當出現這些字元組合， preprocessor 會將其替換成相對應的字元

所以 `int a[10];` 可以寫成 `int a<:10:>;`

其他還有

三元表示或是直接用字串表示 `iso646.h`

## Designated initializer

C99 中可以在 {} intializer 指定要 intial 哪個 index (for array)或是 intial 哪個 field(for struct)

```c
int a[] = {0,1,2,[5] = 10, [9] = 11};

struct Foo {
    int x, y, z;
};

struct Foo f = {.x = 10, .z = 20, .y = 30};

// mix

struct Foo f_a[] = {[10] = {.x = 10, .z = 20, .y = 30}};

```

## Compound literals 

透過 cast 成特定結構 + brace intializer 即可不宣告而定義一個匿名的結構：

```c
struct Foo {
    int x, y;
};
void bar(struct Foo f) {

}

int main() {
    bar((struct Foo){.x = 10, .y = 20});
}
```

另外要注意的是 compound literal 是 lvalue ，可以對他 assign 或是取地址之類的操作

```c
(struct Foo){}.x = 10; // valid
&(struct Foo){}; // valid
```

## Multi-character constants

可以用 '' 包住多個字元當作 enum 的 value

```c
enum state {
    WAIT = 'WAITING',
    END = 'END',
};
```

這個技巧具體實作要看編譯器怎麼決定，開發中極不推薦使用，但是可以拿來定位一些記憶體位置

## Bit fields

可以在 struct 內指定變數要用幾個 bit 來保存，常用於一些網路封包設計或是對記憶體空間斤斤計較的場景

```c
struct Foo {
    int a: 2;
    int b: 2;
};
```

## 0 bit fields 

拿來對齊用，比如用 `char: 0` 是用 byte 來對齊，如果是 `int: 0` 就是對齊 4bytes ，或者是當作邊界使用，像是

```c
struct A {
    char a: 1;
    char b: 1;
}
```

這樣 a b 會處在同個 byte 中，但是如果改成：

```c
struct A {
    char a: 1;
    char : 0;
    char b: 1;
}
```

這樣 a 和 b 中間就會空一格變成 2 bytes ， `char : 0` 實際上是 a + 7 個 bit 組成

換個例子：
```
struct bar {
    unsigned char x : 5;
    unsigned short  : 0;
    unsigned char y : 7;
}
```

此時 layout 會是：

*  第一個 byte 的 5 個 bit for x
*  第一個 byte 的 5 個 bit + 剩下的 3 bit + 8 個 bit = 16bit 屬於 `unsigned short`
    * 雖然 `unsigned short` 將第一個 byte 的 5 bit 算在內，但使用上沒什麼問題
*  第三個 byte 的 7 個 bit 屬於 y

## volatile

告知 compiler 此變數有可能以其他方式訪問，禁止對其進行優化或 reorder 

## restrict

告知 compiler 此 pointer 在其生命週期內都不會有任何其他 pointer 進行訪問，可以安心的進行最佳化

## register

告知 compiler 建議將此變數存放到 register 中，不要放到 memory ，副作用是無法得到該變數的地址但是可以計算該變數的大小，現代 compiler 不太需要這種提示，通常 compiler 都會做的比你還好，只有在 embedded 的環境下可能還有點用

## Flexible array member

透過將 array 變數放到結構最後並且不加上 subscript 來表示這是任意長度的 array

```
struct A {
    int a;
    char b[];
};
```

如果什麼都不加那 struct A 就只有 a 的大小，但如果是

```
void foo() {
    struct A *a = malloc(sizeof(struct A) + 16);
}
```

大小還是 a 的大小，但可以透過 b 來訪問後面 16 bytes ，不過感覺超容易越界 XD

這種技巧在結構是：

* header
    * 固定欄位大小
* data 
    * 根據 header 決定 data 長度

組合應該很好用

flexible array 在 GNU extension 也可以用 
```
struct A {
    int a;
    char b[0];
}
```

表示，更早期的作法是

```
struct A {
    int a;
    char b[1];
}
```

跟以下方式相比有個優點是可以減少 allocate 次數和訪問不同記憶體的次數

```
struct A {
    int a;
    char *b;
}
```

當然這種方式也有幾個缺點，望慎用之：

* 表達式變複雜
* 容易浪費空間
    * 如果 array 其實可以塞進 struct 裡面但是用 flexible array 的話就會浪費掉那些沒在用的空間
* 讓靜態分析工具誤會


## %n format specifier

%n 可以得到當前字串長度的位址

```
int pos1, pos2;
const char* str_of_unknown_len = "we don't care about the length of this";

printf("Write text of unknown %n(%s)%n length\n", &pos1, str_of_unknown_len, &pos2);
printf("%*s\\%*s/\n", pos1, " ", pos2-pos1-2, " ");
printf("%*s", pos1+1, " ");
for (int i = pos1+1; i < pos2-1; ++i) {
    putc('-', stdout);
}
putc('\n', stdout);
```

輸出成：
```
Write text of unknown (we don't care about the length of this) length
                      \                                      /
                       --------------------------------------
```

但我第一次認識 %n 是在 format string vulnerability 中，用法是利用 %n 寫值到參數的特性，如果有 fmt bug 我們就可以利用 %n 把字串長度寫到某個 stack 上的變數達到 memory corruption

更屌的用法可以參考 [printf-tac-toe](https://github.com/carlini/printf-tac-toe/issues/1)，簡單講是利用 printf 做一個圖靈完備機，其中利用 %n + puts 會把 register 值 push 到 stack 的特性做到 loop 的效果

## %.* (minimum field width) format specifier

設定精度可以用質樸的：

```
char fmt_buf[MAX_BUF];
snprintf(fmt_buf, MAX_BUF, "%%.%df", prec);
printf(fmt_buf, num);
```

寫好最小精度後透過 snprintf 組裝成 `%.2f` 然後送到 printf 裡面( `%%` 應該是把 % 字串畫)

可以用以下方式完成一樣的事情
```
printf("%.*f", prec, num);
```

## Other less known format specifiers

請參照 [ §7.21.6.1](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#%5B%7B%22num%22%3A703%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C0%2C792%2C0%5D) [§7.21.6.2](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf#%5B%7B%22num%22%3A719%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C0%2C792%2C0%5D)

可以找到 %#, %e, %-, %+, %j, %g, %a 之類的格式化字串

## Interlacing syntactic constructs

switch 的 case 其實相當於 label ，因此可以實際上在 switch 裡面任意跳轉，經典的例子是 Duff's Device ，好像是 80 年代的最佳化方式：

```
send(to, from, count)
    register short *to, *from;
    register count;
{
    register n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
            } while (--n > 0);
    }
}
```

## --> "operator"

這是個無聊的笑話可以騙別人說 C 有 `-->` 這個 operator ，實際上是 `--` + `>` ，當然這可以編譯

## idx[arr]

array 中的 square braces 實際是語法糖，完成 `*(arr + i)` 的工作，實際上他可以用這種形式來寫(但極不推薦)：

```c
// arr[5] = 0;
5[arr] = 0;
```

## Negative array indexes

可以用負數當作 index 取 array 的值，有些檢查副檔名的寫法會這樣寫：

```c
if(filename_end[-4] == '.' && filename_end[-3] == 'r' && filename_end[-2] == 'a' && filename_end[-1] == 'r')
```

## Constant string concatenation 

字串常數可以用 `""` `""` 兩個雙引號連在一起即可：

```c
const char a[] = "aaa" \
                 "bbb";
```

## Using && and || as conditionals

```
#include <stdio.h>
#include <stdbool.h>

int main(void)
{
    1 && puts("Hello");
    0 && puts("I won't");
    1 && puts("World!");
    0 && puts("be printed");
    1 || puts("I won't be printed either");
    0 || puts("But I will!");

    true && (9 > 2) && puts("9 is bigger than 2");

    isdigit('9') && puts("9 is a digit");
    isdigit('n') && puts("n is a digit") || puts("n is NOT a digit!");

    return 0;
}
```
可以把 `&&` 和 `||` 不用 if 括號直接使用，但是這樣好難看...


## Compile time assumption checking using enums

這個意義不明...
```c
#define D 1
#define DD 2

enum CompileTimeCheck
{
    MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
    MAKE_SURE_DD_IS_POW2    = 1/((((DD) - 1) & (DD)) == 0)
};
```

## 任意位置定義 struct

```c
struct A {
    int a;
};
```

struct A 是一種 data type 

```c
struct {
    int a;
};
```
這樣也是一種 type ，只是之後用起來很麻煩要一直 `struct { int a;} A;` 這樣宣告

第一種宣告也可以宣告在 return value 的位置：

```
struct Foo {int a;} foo() {
    struct Foo x = {.a = 10;};
    return x;
}
```

## "Nested" struct definition is not kept nested

nested struct 宣告：
```c
struct Foo {
    int x;
    struct Bar {
        int y;
    };
};
```

這樣其實只有宣告 datatype 但是沒有定義，所以實際上無法從 Foo 去訪問 y ，但是宣告在 Foo 裡面的 Bar 也可以拿來使用這點沒問題

## Flat initializer lists

透過 {} 可以在定義的時候順便 assign value 到 array 或是 struct

```c
int arr[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; // array

struct Foo {
    int a;
    char *name;
};

struct Foo f[2] = {1, "abcd", 2, "efgh", 3, "ijkl"};
```

## Static array indices in function parameter declarations

C99 之後可以在 array index 宣告的時候寫上 static const 等修飾詞

```c
int n;
void foo(int arr[static 10]);       // arr points to the first of at least 10 ints
void foo(int arr[const 10]);        // arr is a const pointer
void foo(int arr[const]);           // const pointer to int
void foo(int arr[static const n]);  // arr points to at least n ints (VLA)
```

貌似可以幫助 compiler 進行最佳化

文中有一段敘述是這樣的：

如果單單用 array 名字不加上 [] 則該名字表示 array 第一個元素的地址，或者將 array[] 宣告在參數中也是會轉型成 pointer ，但這樣會丟失掉 array size 等資訊， C99 允許 array 宣告在 parameter 時對 index 加上 static 保存其長度大小的資訊，加上 const 則保存，

有這些資訊可以幫助 compiler 做 optimize ，但是要切記這個只是提醒，如果 caller 沒遵守的話是 UB

## Macro Overloading by Argument List Length

[CMObALL](https://github.com/Jorengarenar/CMObALL)
[BOOST_PP_OVERLOAD](https://www.boost.org/doc/libs/master/libs/preprocessor/doc/ref/overload.html)
[Can macros be overloaded by number of arguments? - Stack Overflow](https://stackoverflow.com/q/16683146/10247460)
```c
#include <stdio.h>
#include "cmoball.h"

#define NoA(...) CMOBALL(FOO, __VA_ARGS__)
#define FOO_3(x,y,z) "Three"
#define FOO_2(x,y)   "Two"
#define FOO_1(x)     "One"
#define FOO_0()      "Zero"


int main()
{
    puts(NoA());
    puts(NoA(1));
    puts(NoA(1,1));
    puts(NoA(1,1,1));
    return 0;
}
```

## Function types

這個感覺不算黑魔法，我覺得滿常見的，在寫 handler 就會用到：

```c
typedef void (*func)(int);
// now you can use func to represent void (*)(int);
func f;
```

## X-Macros

[X Macro - Wikipedia](https://en.wikipedia.org/wiki/X_Macro)
[Wikibooks on X macros](https://en.wikibooks.org/wiki/C_Programming/Preprocessor#X-Macros)
[C Programming/Serialization/X-Macros](https://en.wikibooks.org/wiki/C_Programming/Serialization#X-Macros)
[Real-world use of X-Macros - Stack Overflow](https://stackoverflow.com/q/6635851/10247460)
[What are X-macros? – Arthur O'Dwyer](https://quuxplusone.github.io/blog/2021/02/01/x-macros/)
[X macro: most epic C trick or worst abuse of preprocessor? / Arch Linux Forums](https://bbs.archlinux.org/viewtopic.php?id=272242)
[The Most Elegant Macro – Phillip Trudeau](https://philliptrudeau.com/blog/x-macro)

## Named function parameters

```c
struct _foo_args {
    int num;
    const char* text;
};

#define foo(...) _foo((struct _foo_args){ __VA_ARGS__ })
int _foo(struct _foo_args args)
{
    puts(args.text);
    return args.num * 2;
}

int main(void)
{
    int result = foo(.text = "Hello!", .num = 8);
    return 0;
}
```

這個用意我猜是在呼叫 function 給參數的時候可以加上參數變數名稱，增加可讀性(?)，原理是利用 compound literal 建立一個匿名的 arg struct (`(struct _foo_args){}`)， struct 內放上參數名稱，這樣使用上就可以用 struct 的 designed initializer 去 assign (`{.text="Hello!", .num=8}`)，另外因為 arg struct 內的 field name, 數量不是固定，因此用 `__VA_ARGS__` 去拿到所有參數並當作 compound literal 的值

[Variadic-Macros](https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html)
[更多關於 macro 的用法大全](https://gcc.gnu.org/onlinedocs/cpp/Macros.html)

## Combining default, named and positional arguments 

寫過 C++ 就知道 C++ 函數的參數可以放上 default 值：

```c++
void foo(int a = 1, int b = 2, int c = 3) {}

int main() {
    // all valid
    foo(1);
    foo(1,2);
    foo(1,2,3);
}
```

C 的話可以透過 macro 做到類似的事情：

* 參數預設值
```c
typedef struct { int a,b,c,d; } FooParam;
#define foo(...) foo((FooParam){ __VA_ARGS__ })
void (foo)(FooParam p);
// same with above

#define foo(...) foo((FooParam){ .a=1, .b=2, .c=3, .d=4, __VA_ARGS__})
```

透過先指定 FooParam 的 field value 後再用 __VA_ARGS__ 去覆蓋掉先前給的 value 

但是這樣有個問題，要使用 foo function 就一定要先給參數名字，不能像從前：

`foo(1,2,3,4);`

但是如果再額外加上一個沒用到的 dummy variable 就可以這樣寫：

```c
typedef struct { int _; int a,b,c,d; } FooParam;
#define foo(...) foo((FooParam){ .a=1, .b=2, .c=3, .d=4, ._=0, __VA_ARGS__})
```

而且一定要從 abcd 中區分開來，單獨拿出來寫一個 statement

```
foo();           // a=1, b=2, c=3, d=4
foo(.a=4, .b=5); // a=4, b=5, c=3, d=5
foo(4, 5);       // a=4, b=5, c=3, d=5
foo(4, 5, .d=8); // a=4, b=5, c=3, d=8
```


## Abusing unions for grouping things into namespaces

如果 struct 有一坨 field 需要一次性的移動，可以用 union + nested structure 達成：

```c
struct a {
    int field1;
    union {
        struct {
            int field2;
            int field3;
        };
        struct {
            int field2;
            int field3;
        } sub;
    };
};
```

猜測原本是長這樣：

```c
struct a {
    int field1;
    int field2;
    int field3;
};
```

但我們需要綁定 field2 和 field3 ，但他們彼此之間操作上不太適合綁定? ，所以用 union + nested structure ，這樣可以透過 a.sub 一次移動 field2 和 field3 ，使用時也可以改成 a.field2 不必用 a.sub.field2

## Matching character classes with sscanf()

sscanf 可以做到簡易的 regex (僅限於 charactor)

```c
int len = 0;
char buf[256];
int read_token = sscanf(input, "%255[a-zA-Z_]", buf, &len);
if (read_token) { /* do something */ }
```

```c
int len = 0;
char buf[256];
sscanf(input, "%255[\r\n]%n", buf, &len);
input += len;
```

## Garbage collector

[Boehm GC](https://www.hboehm.info/gc/)

## Cosmopolitan Libc

CORE library ，不用 virtual machine 或是 interpreter ，他把常見在各作業系統的執行檔格式一次編譯出來然後組合在一起

## Inline assembly

在 C 中使用 assembly ，不過這是 compiler extension

  <li><a href="https://wiki.osdev.org/Inline_assembly">Inline Assembly - OSDev Wiki</a></li>
  <li><a href="https://en.cppreference.com/w/c/language/asm">Inline assembly - cppreference.com</a></li>
  <li><a href="https://learn.microsoft.com/en-us/cpp/c-language/inline-assembler-c">Inline Assembler (C) | Microsoft Learn</a></li>
  <li><a href="https://developer.arm.com/documentation/100748/0619/Using-Assembly-and-Intrinsics-in-C-or-C---Code/Writing-inline-assembly-code">Writing inline assembly code - Arm Compiler for Embedded User Guide</a></li>
  <li><a href="https://www.cs.uaf.edu/courses/cs301/2014-fall/notes/inline-assembly/">Inline Assembly in C/C++ - University of Alaska Fairbanks</a></li>

## Object Oriented Programming

用 OOP 方式寫 C ，文中有提供數個連結供參考，不過我覺得其實只要知道 C++ 背後的實作原理那不用參考文章也能弄得出 OOP 的 C ，有時間再補

* [Object-Oriented Programming in C - Quantum Leaps](https://www.state-machine.com/oop)
* [Object-orientation in C - Stack Overflow](https://stackoverflow.com/q/415452/10247460)
* [Object-Oriented Programming With ANSI C](https://www.cs.rit.edu/~ats/books/ooc.pdf)
* [C Traps and Pitfalls](https://www.cs.tufts.edu/comp/40/docs/CTrapsAndPitfalls.pdf)
* ["you can have something like interfaces and virtual methods by using function pointers"](https://www.reddit.com/r/C_Programming/comments/mqk338/interesting_ways_to_use_c/guhenr5/)


## Metaprogramming

C11 有 _Generic ，但 C99 可以用 datatype99.h 達成類似的事情(還是用 preprocessor 達成)

```c
#include <datatype99.h>

datatype(
    BinaryTree,
    (Leaf, int),
    (Node, BinaryTree *, int, BinaryTree *)
);

int sum(const BinaryTree *tree) {
    match(*tree) {
        of(Leaf, x) return *x;
        of(Node, lhs, x, rhs) return sum(*lhs) + *x + sum(*rhs);
    }

    return -1;
}
```

> 這我還沒仔細研究要怎麼用

## Evaluate sizeof at compile time by causing duplicate case error 

用以下寫法讓編譯器在編譯階段就透過 error 告知 data type 大小

```
int foo(int c)
{
    switch (c) {
        case sizeof (struct Foo): return c + 1;
        case sizeof (struct Foo): return c + 2;
    }
}
```

> 不確定意義何在...

## Coroutines

[Coroutines in c](https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html)
[Coroutines#C - Wikipedia](https://en.wikipedia.org/wiki/Coroutine#C)
[Does C support Coroutines ? - Quora](https://www.quora.com/Does-C-support-coroutines)
[c-coroutine](https://zelang-dev.github.io/c-coroutine/)
[libaco](https://libaco.org/)
[libtask](https://swtch.com/libtask/)
[neco](https://github.com/tidwall/neco)
[How to get fired using switch and statement](https://blog.robertelder.org/switch-statements-statement-expressions/)

不確定是不是跟 setcontext 之類的 function 相關

## Preprocessor

[awesome-c-preprocessor](https://github.com/hirrolot/awesome-c-preprocessor)

[preprocessor 冰山梗圖](https://jadlevesque.github.io/PPMP-Iceberg/)

## CCAN 

[CCAN](https://ccodearchive.net/) 貌似跟 perl 的 CPAN 有關，後者是一個紀錄各種 perl code snippets 的網站

## Function pointers to match arrays in _Generic

現階段 Generic 不能匹配 array 但是有個 trick 是把 array 用 function pointer 包裝:

```clike=
#include <stdio.h>

#define LENGTHOF(arr) (sizeof (arr) / sizeof (arr)[0])

#define FOO(n,m) \
        _Generic(void (*)(int(*)[n][m]), \
                 void (*)(int(*)[3][6]) : 1, \
                 void (*)(int(*)[3][3]) : 2, \
                 void (*)(int(*)[2][8]) : 3, \
                 void (*)(int(*)[4][*]) : 4, \
                 void (*)(int(*)[8][*]) : 5 \
        )

#define BAR(arr) FOO(LENGTHOF(arr), LENGTHOF(arr[0]))

int main()
{
    printf("%d\n", FOO(3,6)); // 1
    printf("%d\n", FOO(3,3)); // 2
    printf("%d\n", FOO(2,8)); // 3
    printf("%d\n", FOO(4,0)); // 4
    printf("%d\n", FOO(4,1)); // 4
    printf("%d\n", FOO(4,2)); // 4

    int arr[8][2];
    printf("%d\n", BAR(arr)); // 5

    // printf("%d\n", FOO(5,2)); // error

    return 0;
}
```