# [AIdrifter CS 浮生筆錄](https://hackmd.io/s/rypeUnYSb) <br> 2016 年,現代 C 語言的寫法 :::info 寫 C 的首要原則,就是避免去寫 C。 (The first rule of C is don’t write C if you can avoid it.) ::: ## c99 vs c11 - c99:「1999年制定的標準」 - c11:「2011年制定的標準」 ## Compiler C 的版本建議使用 C11,再來是 C99。編譯器建議使用 clang,再來是 gcc。編譯時可加上 `-std=c11` 選項使用 C11 編譯,例如: ```shell $ clang -std=c11 main.c -o justDoIt ``` - clang 默認是用`-std=c11` ### Optimzie - -O2,-O3 - 通常我們需要使用`-O2`優化級別,但是有的時候我們希望使用`-O3`優化級別。可以在這兩種優化級別(和跨編譯器)下的測試之後,保留最佳性能的二進制代碼。 - -Os - `-Os`優化級別能夠幫助提高緩存性能(它應該是) ### Warning - -Wall -Wextra -pedantic 新版本編譯器 提供了`-Wpedantic`開關,但是為了向下兼容,它們仍然支持古老的-pedantic開關。 - 在測試工程中,應該在全平台中添加`-Werror`和`-Wshadow`開關 - 在部署產品源碼時,使用-Werror開關可能會非常棘手,因為不同的平台和編譯器可能會發出不同的警告信息。我們可能不希望因為平台上的GCC版本有從未看見過的警告而中止用戶的整個構建。 - 其他花哨的選項`-Wstrict-overflow` `-fno-strict-aliasing` - 要麼通過`-fno-strict-aliasing`開關或者確保只使用對象創建時的類型來訪問對象。由於大量已經存在的C代碼使用了跨類型別名,如果我們無法控制源碼樹,使用`-fno-strict-aliasing`開關會更加安全。 - 截至目前,clang會將一些有效語法作為警告,因此我們應該增加-Wno-missing-field-initializers開關 - GCC在4.7.0版本後修復了這個無效的警告 ### CPU Architecture - -march=native - 授權編譯器使用CPU的所有特性指令集 - 同樣,性能測試和回歸測試(比較跨編譯器或者編譯器版本)非常重要,以確保啟動了優化之後沒有副作用。 - -msse2和-msse4.2對於需要使用非構建機器構建目標文件可能會有用。 ## Declare ### 最小類型 - 帶正負號的整數使用 `int8_t`、`int16_t`、`int32_t`、`int64_t` - 不帶號的整數使用 `uint8_t`、`uint16_t`、`uint32_t`、`uint64_t` - 32 與 64 位元長的浮點數使用 `float` 與 `double` ### 使用char場景 - `uint8_t` 取代 `char` 來宣告不帶號的 8 位元整數 - char 保留在對於字元或字串(char *)的宣告上就好。 ### 使用integer場景 - 整數的話直接宣告 `intmax_t` 或 `uintmax_t`,浮點數則用 `double` 即可 - 另外在 `stddef.h` 中有所有謂的 `size_t`,可以用來宣告==最大==的`「不」帶號整數`。依 C99 的定義,size_t 是個可以做為任何長度的陣列的索引(index)。至於要不要使用它則隨意了。 ### 使用pointer - 對於指針運算的正確方式是使用`<stdint.h>`頭文件中定義的`uintptr_t`類型,同時也可以使用`stddef.h`頭文件中定義的`ptrdiff_t`類型。 - Bad ```C long diff = (long)ptrOld - (long)ptrNew; ``` - Good ```C ptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew; ``` - 同時,如果需要輸出內容: ```C printf("%p is unaligned by %" PRIuPTR " bytes.\n", (void *)p, ((uintptr_t)somePtr & (sizeof(void *) - 1))); ``` ### 32 bits vs 64 bits - 在這種情況下,我們應該使用intptr_t類型——定義了當前平台上字長的整數。 - 在32位平台上,`intptr_t`是`int32_t`類型。 - 在64位平台上,`intptr_t`是`int64_t`類型。 - 同時,`intptr_t`還有對應的無符號類型`uintptr_t`。 - 對於指針偏移量,我們有一個更加恰當的類型:`ptrdiff_t`,它是存儲指針差值的正確類型。 ### 現在compiler支援 #paragma once - Bad ```C #ifndef PROJECT_HEADERNAME #define PROJECT_HEADERNAME . . . #endif /* PROJECT_HEADERNAME */ ``` - Good ```C #pragma once ``` ## 不要把判斷式寫在宣告local變數後面 - Bad ```C void test(uint8_t input) { uint32_t b; if (input > 3) { return; } b = input; } ``` - Good ```C void test(uint8_t input) { if (input > 3) { return; } uint32_t b = input; } ``` ## C99 允許在 for 迴圈宣告計數器 ```C for (uint32_t i = 0; i < 10; i++) ``` ## Array 初始化 - Bad ```C uint32_t numbers[64]; memset(numbers, 0, sizeof(numbers)); ``` - Good ```C uint32_t numbers[64] = {0}; ``` ## Structure ### Initailize ```C struct thing { uint64_t index; uint32_t counter; }; ``` - Bad ```C struct thing localThing; void initThing(void) { memset(&localThing, 0, sizeof(localThing)); } ``` - Good ```C struct thing localThing = {0}; ``` ### 如果需要重新初始化已經分配內存空間的結構體,可以定義一個全局空結構體,然後賦值: ```C struct thing { uint64_t index; uint32_t counter; }; static const struct thing localThingNull = {0}; . . . struct thing localThing = {.counter = 3}; . . . localThing = localThingNull; ``` ### 複合字面量(compound literals) ```C localThing = (struct thing){0}; ``` Reference: [The New C: Compound Literals](http://www.drdobbs.com/the-new-c-compound-literals/184401404) ## 讓fucntion可以輸入任意數值,別限制他的型別。 - input 宣告成 `void *` , 自己再用`uint8_t`重新去assign。 ```C void processAddBytesOverflow(void *input, uint32_t len) { uint8_t *bytes = input; for (uint32_t i = 0; i < len; i++) { bytes[0] += bytes[i]; } } ``` ## Return Type - true, false 請`incldue <stdbool.h>` - 用enum創造複合型態的回傳型別。 ```C typedef enum growthResult { GROWTH_RESULT_SUCCESS = 1, // Success GROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY, // failure 1 GROWTH_RESULT_FAILURE_ALLOCATION_FAILED // failure 2 } growthResult; ``` ## Coding Style - clang-format - translate main.c to clang format type ```shell clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, AllowShortFunctionsOnASingleLine: None, KeepEmptyLinesAtTheStartOfBlocks: false}" "$@" -i main.c ``` - modify `~/.bashrc` ``` alias clang-format='clang-format -style="{BasedOnStyle: llvm, IndentWidth: 4, AllowShortFunctionsOnASingleLine: None, KeepEmptyLinesAtTheStartOfBlocks: false}" "$@"' ``` - finally ```shell clang-format -i main.c ``` ## Memory - `calloc()` is replaced with `malloc()` - To be avoid to use `memset()` ## Reference https://matt.sh/howto-c http://www.infoq.com/cn/articles/c-language-2016