文章目錄
- 屬性
- __cleanup
- `__attribute_malloc__ 用于標記函數返回一個新分配的內存塊`
- `__attribute_alloc_size__` 用于指定分配的內存大小
- __attribute__((const)) 標記為純函數(pure function)
- __attribute__((__externally_visible__)) 使其在編譯器優化過程中保持對外部模塊的可見性
- __attribute__((__fallthrough__)) 帶有空語句的 fallthrough 屬性用作穿透語句
- __must_check __warn_unused_result__
- noinline 禁止內聯函數
- __attribute__((__noreturn__)) 標記函數不會返回到調用者
- __attribute__((__no_sanitize_address__)) 不需要執行內存錯誤檢測
- 總結
- __attribute__((__no_stack_protector__))
- __no_profile 編譯器不應使用與配置文件相關的工具對函數進行操作
- notrace 禁止跟蹤函數調用
- __attribute__((__pure__)) 標記為純函數
- `__pure` 的定義
- 什么是純函數?
- `__pure` 的作用
- 使用場景
- 注意事項
- 總結
- __printf 像 `printf` 一樣檢查格式字符串
- __cleanup 在變量的作用域結束時自動調用指定的清理函數
- **工作原理**
- 聲明
- `__fortify_function` 用于啟用額外的安全檢查,以防止常見的編程錯誤,如緩沖區溢出
- **`__LEAF`** 告訴編譯器,該函數是一個葉函數(leaf function),即它不會調用其他函數
- `__error__` 屬性告訴編譯器,如果該函數被調用,則會觸發編譯錯誤,并輸出指定的錯誤消息 `msg`
- __wur 表示函數的返回值不應被忽略
- __THROW && __NTH 聲明函數不會拋出任何異常
- __nonnull 用于指示函數的某些參數不能為空指針
- __errordecl 用于聲明編譯時錯誤信息
- __REDIRECT 將一個函數重定向到另一個函數
- 關鍵字
- __restrict 用于指示指針或引用所指向的內存區域在當前作用域內不會被其他指針或引用所別名
- __auto_type 自動推導變量的類型
- **工作原理**
- **限制**
- **使用場景**
- **總結**
- 全局變量
- `stderr` 標準錯誤輸出流
- 內置函數
- __builtin_clz __builtin_clzll 計算一個無符號整數的前導零的數量
- __builtin_constant_p 檢查一個表達式是否是編譯時常量
- __builtin_expect 用于分支預測
- __builtin_return_address 獲取當前函數的返回地址
- __builtin_unreachable 標記不可達代碼
- `__builtin_object_size` 用于在編譯時確定指針所指向對象的大小
- __builtin_prefetch 用于在程序運行時向處理器發出預取指令
- __va_arg_pack 用于處理可變參數函數
- __va_arg_pack_len 用于獲取可變參數的數量
- 函數
- exit
- `perror` 接受一個字符串參數,并將該字符串作為前綴輸出到標準錯誤流
- `fprintf` 將格式化的輸出寫入指定的文件流
- 函數原型
- 返回值
- 源碼
- fstat 獲取文件描述符 `fd` 對應文件的狀態信息
- isalnum 檢查字符是否為字母或數字
- isatty 檢查文件描述符是否指向一個終端設備
- 參數
- 返回值
- getopt_core.h
- getopt_long 處理長選項(即以雙破折號 `--` 開頭的選項)和短選項(即以單破折號 `-` 開頭的選項)
- getenv **獲取環境變量**
- 宏
- dummy 虛擬變量
- `##__VA_ARGS__` 用于處理可變參數宏
- _Static_assert 編譯時斷言
- 語法
- ...
- _Generic 用于實現類型安全的多態函數
- **`_Generic` 簡介**
- **使用場景**
- **工作原理**
- **實現細節**
- **優缺點**
- **優點**:
- **缺點**:
- **總結**

https://github.com/wdfk-prog/linux-study
屬性
__cleanup
__attribute_malloc__ 用于標記函數返回一個新分配的內存塊
- 這有助于編譯器進行優化,例如避免不必要的內存檢查。
__attribute_alloc_size__
用于指定分配的內存大小
attribute_alloc_size__ ((1))
是另一個 GCC 特定的擴展,用于指定分配的內存大小。參數(1)
表示第一個參數__size
是分配的內存大小。這有助于編譯器進行優化和靜態分析
attribute((const)) 標記為純函數(pure function)
純函數具有以下特性:
- 無副作用:純函數不依賴于任何可變的全局狀態,也不會修改任何全局狀態。這意味著函數的輸出僅依賴于其輸入參數,不會對程序的其他部分產生影響。
- 可優化:由于純函數的輸出僅依賴于輸入參數,編譯器可以進行更激進的優化。例如,編譯器可以將純函數的多次調用合并為一次調用,或者在調用純函數時進行常量傳播和代碼移動等優化。
attribute((externally_visible)) 使其在編譯器優化過程中保持對外部模塊的可見性
- 用于標記函數或變量,使其在編譯器優化過程中保持對外部模塊的可見性。通常情況下,編譯器可能會優化掉未被直接引用的符號(函數或變量),但使用這個屬性可以防止這種優化。
- 作用: 它確保標記的符號在鏈接時不會被移除,即使編譯器認為它沒有被使用。這對于動態鏈接庫(shared libraries)或模塊化程序設計非常重要,因為其他模塊可能需要訪問這些符號。
attribute((fallthrough)) 帶有空語句的 fallthrough 屬性用作穿透語句
- 帶有空語句的 fallthrough 屬性用作穿透語句。它向編譯器暗示,在 switch 語句中跳轉到另一個 case 標簽或用戶定義標簽的語句是故意的,因此不應觸發 -Wimplicit-fallthrough 警告。穿透屬性在每個屬性列表中最多只能出現一次,并且不能與其他屬性混合。它只能在 switch 語句中使用(否則編譯器將發出錯誤),在先前的語句之后和邏輯上后續的 case 標簽或用戶定義標簽之前。
__must_check warn_unused_result
- warn_unused_result屬性如果使用該屬性的函數調用者不使用其返回值,則會發出警告。這對于不檢查結果要么是安全問題,要么總是錯誤的函數很有用,比如realloc。
#define __must_check __attribute__((__warn_unused_result__))
noinline 禁止內聯函數
noinline
是一個 GCC 特定的屬性,用于禁止編譯器將函數內聯。內聯函數是一種優化技術,編譯器會將函數的代碼直接插入到調用該函數的地方,以減少函數調用的開銷。然而,在某些情況下,開發人員可能希望禁止內聯,以確保函數的行為和性能符合預期。
attribute((noreturn)) 標記函數不會返回到調用者
- 一些標準庫函數,例如 abort 和 exit ,無法返回。GCC 會自動知道這一點。一些程序定義了自己的函數,這些函數永遠不會返回。您可以聲明它們為 noreturn 來告訴編譯器這一事實
- 關鍵字 noreturn 告訴編譯器假設 fatal 無法返回。然后它可以進行優化,無需考慮如果 fatal 何時返回會發生什么。這使得代碼略微更優。更重要的是,它有助于避免未初始化變量的虛假警告。
- 關鍵字 noreturn 不會影響當適用時的異常路徑:一個標記為 noreturn 的函數仍然可以通過拋出異常或調用 longjmp 返回調用者
- 一個 noreturn 函數的返回類型不應該是 void
attribute((no_sanitize_address)) 不需要執行內存錯誤檢測
__no_sanitize_address__
是一個編譯器屬性,通常用于禁用地址消毒器(Address Sanitizer, ASan)對特定函數或代碼段的檢查。以下是對其作用和使用場景的詳細解釋:
-
地址消毒器(ASan)簡介:
Address Sanitizer 是一種內存錯誤檢測工具,廣泛用于捕獲諸如緩沖區溢出、堆棧溢出、使用未初始化內存等問題。它通過在編譯時插入額外的檢查代碼來檢測這些錯誤。然而,這些檢查可能會增加運行時開銷,或者在某些低級代碼中導致誤報。 -
__no_sanitize_address__
的作用:
當一個函數或代碼段被標記為__no_sanitize_address__
時,編譯器會跳過對該部分代碼的 ASan 檢查。這意味著:- 該代碼不會受到 ASan 的額外檢查。
- 可以避免 ASan 在某些低級操作(如直接操作內存或硬件寄存器)中產生的誤報。
- 減少運行時開銷,特別是在性能關鍵的代碼中。
總結
__no_sanitize_address__
是一個有用的工具,用于在特定場景下禁用 ASan 檢查。它在需要直接操作內存、優化性能或避免誤報時非常有用。然而,使用時需要謹慎,因為禁用 ASan 檢查可能會掩蓋潛在的內存錯誤。
attribute((no_stack_protector))
__no_stack_protector__
是一個 GCC 特定的屬性,用于禁用棧保護功能。棧保護是一種安全機制,用于防止棧溢出攻擊,例如緩沖區溢出。它通過在函數調用時插入額外的檢查代碼來檢測棧的完整性。- 當一個函數被標記為
__no_stack_protector__
時,編譯器將不會為該函數插入棧保護代碼。這可能會導致安全風險,因為攻擊者可以利用棧溢出漏洞來執行惡意代碼。
__no_profile 編譯器不應使用與配置文件相關的工具對函數進行操作
- 在函數聲明上使用 no_profile_instrument_function 屬性來表示編譯器不應使用與配置文件相關的工具對函數進行操作,例如通過 -fprofile-generate / -fprofile-instr-generate / -fcs-profile-generate / -fprofile-arcs 標志來實現。
notrace 禁止跟蹤函數調用
__attribute__((__no_instrument_function__))
attribute((pure)) 標記為純函數
這段代碼定義了一個名為 __pure
的宏,它使用了 GCC 提供的 __attribute__((__pure__))
屬性,并附帶了一個鏈接,指向 GCC 官方文檔中關于純函數(pure function)的說明。以下是對其的詳細解釋:
__pure
的定義
__pure
是一個宏,用于簡化對 GCC 的 __attribute__((__pure__))
屬性的使用。通過定義宏,開發者可以在代碼中更方便地標記函數為“純函數”,同時提高代碼的可讀性和可移植性。
什么是純函數?
純函數(pure function)是指滿足以下條件的函數:
- 無副作用:函數不會修改全局狀態或影響外部環境(如全局變量、文件、I/O 等)。
- 確定性:函數的返回值僅依賴于其輸入參數,對于相同的輸入參數,函數始終返回相同的結果。
純函數的這些特性使得它們在編譯時更容易被優化。
__pure
的作用
通過為函數添加 __pure
屬性,開發者可以顯式告訴編譯器該函數是一個純函數。編譯器會利用這一信息進行以下優化:
- 消除冗余調用:
- 如果函數在同一作用域內被多次調用且輸入參數相同,編譯器可以只調用一次并緩存結果。
- 重新排序指令:
- 由于純函數沒有副作用,編譯器可以安全地調整其調用順序,以優化性能。
- 內聯優化:
- 編譯器可能會更傾向于將純函數內聯,從而減少函數調用的開銷。
使用場景
-
數學計算:
- 例如,計算平方根、絕對值等函數,這些函數的返回值僅依賴于輸入參數。
__pure int square(int x) {return x * x; }
-
數據轉換:
- 將輸入數據轉換為另一種形式,而不修改全局狀態。
__pure int to_uppercase(char c) {return (c >= 'a' && c <= 'z') ? (c - 'a' + 'A') : c; }
-
查找操作:
- 在只讀數據結構中查找值的函數也可以標記為純函數。
注意事項
-
與
__attribute__((const))
的區別:__pure
函數可以讀取全局變量的值,但不能修改它們。__attribute__((const))
是更嚴格的屬性,要求函數既不能修改全局狀態,也不能讀取全局變量。
-
正確使用:
- 如果一個函數被錯誤地標記為
__pure
,但實際上具有副作用(如修改全局變量或執行 I/O 操作),可能會導致未定義行為或錯誤的優化結果。
- 如果一個函數被錯誤地標記為
-
編譯器支持:
__pure
是 GCC 和 Clang 支持的擴展屬性,因此在其他編譯器中可能不可用。
總結
__pure
是一個用于標記純函數的宏,通過顯式聲明函數的純屬性,幫助編譯器進行更高效的優化。它適用于無副作用且返回值僅依賴于輸入參數的函數。在使用時需要確保函數符合純函數的定義,否則可能導致錯誤的優化行為。附帶的鏈接提供了 GCC 官方文檔的參考,便于開發者了解更多細節。
__printf 像 printf
一樣檢查格式字符串
- format 屬性用于告訴編譯器某個函數的參數應該像 printf 函數那樣進行格式化檢查。
#define __printf(a, b) __attribute__((__format__(printf, a, b)))
__printf(4, 0)
- 4 表示格式字符串是函數的第 4 個參數(從 1 開始計數,包括 this 指針或隱式參數)。
- 0 表示該函數沒有可變參數列表(即沒有類似 … 的參數)。如果存在可變參數列表,則需要指定其起始位置
__cleanup 在變量的作用域結束時自動調用指定的清理函數
__cleanup
是一個 GCC 提供的屬性(attribute),用于在變量的作用域結束時自動調用指定的清理函數。它是一種基于作用域的資源管理機制,能夠有效減少手動管理資源釋放的復雜性,避免資源泄漏。
工作原理
-
定義清理函數:
- 清理函數是一個普通的 C 函數,用于釋放資源或執行其他清理操作。
- 該函數的參數通常是一個指向需要清理的變量的指針。
-
聲明變量時使用
__cleanup
:- 在聲明變量時,通過
__cleanup
屬性指定清理函數。 - 當變量超出其作用域時,編譯器會自動調用指定的清理函數。
- 在聲明變量時,通過
-
作用域結束時自動調用:
- 當變量的作用域結束(如函數返回或代碼塊結束)時,清理函數會被自動調用,無需顯式調用。
聲明
__fortify_function
用于啟用額外的安全檢查,以防止常見的編程錯誤,如緩沖區溢出
__LEAF
告訴編譯器,該函數是一個葉函數(leaf function),即它不會調用其他函數
- 葉函數的屬性允許編譯器進行進一步的優化,例如減少函數調用的開銷和棧空間的使用。
__error__
屬性告訴編譯器,如果該函數被調用,則會觸發編譯錯誤,并輸出指定的錯誤消息 msg
# define __errordecl(name, msg) \extern void name (void) __attribute__((__error__ (msg)))
__wur 表示函數的返回值不應被忽略
__wur
是__attribute__ ((__warn_unused_result__))
的縮寫,表示函數的返回值不應被忽略。這有助于防止內存泄漏,因為調用者必須處理返回的指針
__THROW && __NTH 聲明函數不會拋出任何異常
__nonnull 用于指示函數的某些參數不能為空指針
- 這有助于編譯器進行靜態分析,檢測潛在的錯誤,并在編譯時發出警告或錯誤信息,從而提高代碼的安全性和可靠性。
- 語法為
__attribute__((nonnull (arg_index1, arg_index2, ...)))
,其中arg_index
是參數的索引,從 1 開始。
extern int __lxstat (int __ver, const char *__filename,struct stat *__stat_buf) __THROW __nonnull ((2, 3));
__nonnull ((3))
:這表示__fxstat
函數的第2和3個參數(即__stat_buf
)不能為空指針。如果在調用__fxstat
函數時傳遞了空指針作為第三個參數,編譯器會在編譯時發出警告或錯誤信息。
__errordecl 用于聲明編譯時錯誤信息
# define __errordecl(name, msg) \extern void name (void) __attribute__((__error__ (msg)))
__REDIRECT 將一個函數重定向到另一個函數
- 將openalias重定向到open函數
extern int __REDIRECT (__open_alias, (const char *__path, int __oflag, ...),open) __nonnull ((1));
關鍵字
__restrict 用于指示指針或引用所指向的內存區域在當前作用域內不會被其他指針或引用所別名
- 在 C99 標準中,
restrict
是標準關鍵字。在某些編譯器實現中,可能使用__restrict
作為擴展。 - 通過使用
__restrict
,開發人員可以告訴編譯器,在當前作用域內,通過該指針訪問的內存區域不會與其他指針訪問的內存區域重疊。這使得編譯器可以進行更激進的優化,例如更好地利用寄存器和減少內存訪問,從而提高程序的性能。
__auto_type 自動推導變量的類型
__auto_type
是 GCC(GNU Compiler Collection)編譯器提供的一個擴展關鍵字,用于自動推導變量的類型。它類似于 C++ 中的 auto
關鍵字,但專門用于 C 語言中。__auto_type
的主要作用是讓編譯器根據初始化表達式的類型自動推導變量的類型,從而簡化代碼書寫并提高可讀性。
工作原理
-
類型推導:
- 使用
__auto_type
聲明的變量,其類型由初始化表達式的類型決定。 - 編譯器在編譯時會自動推導出變量的具體類型。
- 使用
-
作用范圍:
__auto_type
只能用于局部變量的聲明,不能用于全局變量或函數參數。
-
編譯器支持:
__auto_type
是 GCC 的擴展功能,可能不被其他編譯器(如 MSVC)支持。
-
簡化代碼:
- 減少了顯式類型聲明的冗余,尤其是在類型復雜的情況下。
-
增強可讀性:
- 代碼更簡潔,開發者可以專注于邏輯而非類型聲明。
-
類型安全:
- 在宏中使用時,
__auto_type
可以確保變量的類型與初始化表達式一致,避免類型錯誤。
- 在宏中使用時,
-
靈活性:
- 適用于各種類型,包括基本類型、指針、結構體等。
限制
-
編譯器依賴:
__auto_type
是 GCC 的擴展功能,可能不被其他編譯器支持。
-
作用范圍有限:
- 只能用于局部變量,不能用于全局變量或函數參數。
-
可讀性問題:
- 對于不熟悉
__auto_type
的開發者,可能會增加理解代碼的難度。
- 對于不熟悉
使用場景
-
簡化類型聲明:
- 在類型復雜或不易確定的情況下,使用
__auto_type
可以減少顯式類型聲明的負擔。
- 在類型復雜或不易確定的情況下,使用
-
宏定義:
- 在宏中使用
__auto_type
可以增強類型安全性,避免類型錯誤。
- 在宏中使用
-
泛型操作:
- 通過
__auto_type
實現類似泛型的功能,例如交換變量、比較大小等。
- 通過
總結
__auto_type
是 GCC 提供的一個強大工具,用于在 C 語言中實現類型推導。它可以顯著簡化代碼書寫,提高代碼的可讀性和類型安全性,特別適合在宏定義和泛型操作中使用。然而,由于其是編譯器擴展功能,在使用時需要注意兼容性問題。
全局變量
stderr
標準錯誤輸出流
stderr
是標準錯誤輸出流,通常用于輸出錯誤和警告信息。在大多數操作系統中,stderr
是一個全局的文件流,默認情況下輸出到控制臺或終端。stderr
流的處理由操作系統和 C 標準庫管理。通常,stderr
流是無緩沖的,這意味著每次寫入操作都會立即輸出到終端或控制臺- 多線程環境中的
stderr
處理在多線程環境中,如果多個線程同時向stderr
流寫入數據,可能會導致輸出混亂。
內置函數
__builtin_clz __builtin_clzll 計算一個無符號整數的前導零的數量
-
__builtin_clz 用于計算一個無符號整數(unsigned int)的前導零的數量。前導零是指從最高有效位(最左邊的位)開始,連續的零的數量
-
__builtin_clzll 用于計算一個無符號長長整數(unsigned long long)的前導零的數量。它的工作原理與 __builtin_clz 類似,只是它處理的是更大的數據類型。
__builtin_constant_p 檢查一個表達式是否是編譯時常量
-
GCC 編譯器提供的一個內建函數,用于檢查一個表達式是否是編譯時常量。它在編譯期間進行評估,并返回一個布爾值,指示給定的表達式是否為常量.這個函數通常用于條件編譯或優化代碼,以便在編譯時確定某些條件是否成立,從而避免在運行時進行不必要的計算。
-
您可以使用內置函數 __builtin_constant_p 來判斷表達式 exp 在編譯時是否已知為常量,因此 GCC 可以對涉及該值的表達式執行常量折疊。該函數的參數是要測試的表達式。表達式不會被評估,副作用會被丟棄。如果參數是已知為編譯時常量的整數 1,如果不是已知為編譯時常量則返回 0。任何具有副作用的表達式都會使函數返回 0。返回 0 并不表示表達式不是常量,而只是表示 GCC 無法在活動優化選項集的約束內證明它是常量。
-
通常您會在內存資源至關重要的嵌入式應用程序中使用此函數。如果您有一些復雜的計算,可能希望它在涉及常量時進行折疊,但不涉及常量時調用函數。
__builtin_expect 用于分支預測
- GCC 編譯器提供的一個內建函數,用于分支預測。它允許開發人員向編譯器提供有關條件表達式的預期結果的信息,從而幫助編譯器進行更好的優化。__builtin_expect 函數通常用于性能敏感的代碼中,以提高分支預測的準確性,從而減少 CPU 的分支預測失敗率,提高程序的執行效率。
- 這個函數的語法如下:
long __builtin_expect (long exp, long c);
- exp 參數是要預測的表達式,c 參數是預期的結果。這個函數返回 exp 的值,但編譯器會根據 c 的值進行優化,以提高分支預測的準確性。
- 這個函數通常用于性能敏感的代碼中,以提高分支預測的準確性,從而減少 CPU 的分支預測失敗率,提高程序的執行效率。它可以用于條件語句、循環和其他需要分支預測的地方。
__builtin_return_address 獲取當前函數的返回地址
- GCC 編譯器提供的一個內建函數,用于獲取當前函數的返回地址。它通常用于調試和錯誤檢查,以便在運行時獲取函數調用的返回地址。這對于跟蹤函數調用棧和調試程序非常有用。
void * __builtin_return_address (unsigned int level);
- level 參數指定要獲取的返回地址的級別。level 為 0 時,返回當前函數的返回地址;level 為 1 時,返回調用當前函數的函數的返回地址;以此類推。這個函數通常用于調試和錯誤檢查,以便在運行時獲取函數調用的返回地址。這對于跟蹤函數調用棧和調試程序非常有用。
- 這個函數的返回值是一個指針,指向當前函數的返回地址。這個地址可以用于調試和錯誤檢查,以便在運行時獲取函數調用的返回地址。這對于跟蹤函數調用棧和調試程序非常有用。
__builtin_unreachable 標記不可達代碼
- GCC 編譯器提供的一個內建函數,用于標記不可達代碼。它告訴編譯器在執行到該位置時,程序將不會繼續執行下去。這可以幫助編譯器進行更好的優化,因為它可以假設在這個位置之后的代碼永遠不會被執行。使用這個函數可以提高代碼的性能,并且在調試時可以幫助開發人員識別潛在的錯誤或不一致之處。
- 這個函數通常用于在編譯器優化時提供提示,告訴編譯器某些代碼永遠不會被執行。這可以幫助編譯器進行更好的優化,并且在調試時可以幫助開發人員識別潛在的錯誤或不一致之處。
__builtin_object_size
用于在編譯時確定指針所指向對象的大小
__builtin_object_size
是 GCC 編譯器提供的一個內置函數,用于在編譯時確定指針所指向對象的大小。
- 該函數接受兩個參數:
- 第一個參數是指針
ptr
。 - 第二個參數是一個常量整數,用于指定計算對象大小的方式。值
0
表示返回對象的最大已知大小
- 第一個參數是指針
__builtin_prefetch 用于在程序運行時向處理器發出預取指令
__builtin_prefetch
是 GCC 編譯器提供的一個內置函數,用于在程序運行時向處理器發出預取指令。它允許開發人員在訪問數據之前,提前將數據加載到 CPU 的緩存中,從而提高程序的性能。- 這個函數通常用于性能敏感的代碼中,以提高數據訪問的速度。它可以用于循環、數組訪問和其他需要頻繁訪問數據的地方。使用
__builtin_prefetch
可以減少 CPU 的緩存未命中率,從而提高程序的性能。 - 預取指令的目的是將指定的內存地址加載到緩存中,以減少后續訪問該地址時的延遲。這種優化對于性能關鍵的代碼非常有用,尤其是在處理大量數據或需要頻繁訪問內存的場景中
- 需要注意的是,__builtin_prefetch 是一種低級優化工具,只有在性能瓶頸明確且經過分析驗證時才應使用。不當的預取可能會導致性能下降,例如浪費緩存空間或增加內存帶寬壓力。此外,預取的效果依賴于硬件架構和緩存行為,因此在不同平臺上的表現可能有所不同。
__builtin_prefetch 的基本語法如下:
__builtin_prefetch(const void *addr, int rw, int locality);
- addr: 要預取的內存地址。通常是一個指針,指向即將被訪問的數據。
- rw: 指定預取的類型。0 表示只讀預取,1 表示寫預取(即數據將被修改)。
- locality: 指定數據的局部性,范圍為 0 到 3。值越高,表示數據在緩存中保留的時間越長:
0: 數據很快會被訪問一次,不需要長時間保留。
3: 數據會被頻繁訪問,需要盡可能長時間保留在緩存中。
__va_arg_pack 用于處理可變參數函數
__va_arg_pack_len 用于獲取可變參數的數量
函數
exit
- 錯誤碼參考include/linux/errno.h
perror
接受一個字符串參數,并將該字符串作為前綴輸出到標準錯誤流
perror(filename);
perror
函數接受一個字符串參數,并將該字符串作為前綴輸出到標準錯誤流(stderr
),后面跟隨一個冒號和一個空格,然后是與當前 errno
值對應的錯誤消息。
filename
是傳遞給perror
的字符串參數,通常表示發生錯誤的文件的名稱。
-
錯誤信息輸出:
- 當文件操作失敗時,例如打開文件、讀取文件或寫入文件失敗,
errno
會被設置為相應的錯誤代碼。 perror(filename)
會輸出類似于以下格式的錯誤信息:filename: No such file or directory
- 其中,
filename
是傳遞給perror
的字符串,No such file or directory
是與errno
值對應的錯誤消息。
- 當文件操作失敗時,例如打開文件、讀取文件或寫入文件失敗,
fprintf
將格式化的輸出寫入指定的文件流
- 它的功能類似于
printf
,但fprintf
允許你指定輸出的目標文件流,而不僅僅是標準輸出(stdout)。
函數原型
int fprintf(FILE *stream, const char *format, ...);
stream
:目標文件流,指定輸出的目標。例如,stderr
表示標準錯誤輸出流。format
:格式字符串,指定輸出的格式。...
:可變參數,根據格式字符串指定的格式,提供相應的值。
返回值
- 成功時,返回寫入的字符數。
- 失敗時,返回負值。
源碼
- 定義了一個增強的
fprintf
函數版本,用于在編譯時進行額外的安全檢查。這段代碼利用了編譯器的特性來提高函數的安全性和可靠性。
# ifdef __va_arg_pack //__va_arg_pack 是一個編譯器特性,通常用于處理可變參數函數。
// __fortify_function 是一個編譯器特性,用于啟用額外的安全檢查,以防止常見的編程錯誤,如緩沖區溢出
__fortify_function int
fprintf (FILE *__restrict __stream, const char *__restrict __fmt, ...)
{return __fprintf_chk (__stream, __USE_FORTIFY_LEVEL - 1, __fmt,__va_arg_pack ());
}
#endif
fstat 獲取文件描述符 fd
對應文件的狀態信息
fstat
函數返回 0 表示成功,返回 -1 表示失敗并設置errno
。
isalnum 檢查字符是否為字母或數字
-
函數聲明:
int isalnum(int c);
isalnum
函數接受一個整數參數c
,表示要檢查的字符。- 返回值為非零值(通常為 1)表示字符是字母或數字,返回 0 表示字符不是字母或數字。
-
用途:
isalnum
函數用于判斷字符是否屬于字母(包括大寫和小寫字母)或數字(0-9)。- 這是一個常用的字符分類函數,在處理字符串和字符數據時非常有用。
isatty 檢查文件描述符是否指向一個終端設備
參數
fd
:一個整數,表示文件描述符。常見的文件描述符包括:0
:標準輸入(stdin)1
:標準輸出(stdout)2
:標準錯誤輸出(stderr)
返回值
- 如果文件描述符指向一個終端設備,
isatty
返回非零值(通常為 1)。 - 如果文件描述符不指向一個終端設備,
isatty
返回 0。
getopt_core.h
optarg
是一個指向字符的指針,用于存儲當前選項的參數值optind
是一個整數,表示下一個要掃描的 ARGV 元素的索引opterr
是一個整數,用于控制getopt
函數是否打印未識別選項的錯誤消息optopt
是一個整數,用于存儲未識別的選項字符
getopt_long 處理長選項(即以雙破折號 --
開頭的選項)和短選項(即以單破折號 -
開頭的選項)
-
函數聲明:
int getopt_long(int argc, char *const argv[], const char *optstring,const struct option *longopts, int *longindex);
getopt_long
函數接受五個參數:argc
:命令行參數的數量。argv
:命令行參數的數組。optstring
:一個字符串,包含短選項的字符。如果一個選項需要參數,其后應跟一個冒號:
。longopts
:一個指向struct option
數組的指針,定義了長選項。longindex
:一個指向整數的指針,用于存儲當前匹配的長選項在longopts
數組中的索引。
-
返回值 :
- 當
getopt_long
無法匹配到命令行選項時,它會返回?
。 - 如果遇到未知選項或選項缺少必需的參數,
getopt_long
也會返回?
。
-
struct option
結構體:struct option {const char *name;int has_arg;int *flag;int val; };
struct option
結構體用于定義長選項:name
:長選項的名稱。has_arg
:指示選項是否需要參數。取值可以是no_argument
、required_argument
或optional_argument
。flag
:如果為NULL
,則返回val
作為選項的值;否則,將val
存儲在flag
指向的變量中,并返回 0。val
:選項的值或標志。
-
使用示例:
int main(int argc, char *argv[]) {int opt;struct option longopts[] = {{"help", no_argument, NULL, 'h'},{"version", no_argument, NULL, 'v'},{"output", required_argument, NULL, 'o'},{0, 0, 0, 0}};while ((opt = getopt_long(argc, argv, "hvo:", longopts, NULL)) != -1) {switch (opt) {case 'h':printf("Usage: ...\n");break;case 'v':printf("Version: ...\n");break;case 'o':printf("Output: %s\n", optarg);break;default:fprintf(stderr, "Unknown option\n");exit(EXIT_FAILURE);}}return 0; }
getenv 獲取環境變量
getenv
是一個標準庫函數,用于獲取環境變量的值。getenv
函數接受一個字符串參數,表示環境變量的名稱,并返回一個指向該環境變量值的指針。如果環境變量不存在,則返回NULL
。
echo $KCONFIG_SEED # 顯示環境變量的值
宏
dummy 虛擬變量
dummy
是一個虛擬參數,用于確保宏展開時參數列表的長度一致。在 __count_args 宏中,dummy 被用作第一個參數,以確保即使沒有傳遞任何參數,參數列表的長度也至少為 1。
/** 計算可變參數宏的參數數。目前只需要* 它用于 1、2 或 3 個參數。*/
#define __arg6(a1, a2, a3, a4, a5, a6, ...) a6
#define __count_args(...) __arg6(dummy, ##__VA_ARGS__, 4, 3, 2, 1, 0)
##__VA_ARGS__
用于處理可變參數宏
##__VA_ARGS__
是一個預處理器技巧,用于處理可變參數宏。VA_ARGS 是一個特殊的宏參數,表示傳遞給宏的所有可變參數。## 是預處理器的連接運算符,用于將兩個標識符連接在一起。
/** 計算可變參數宏的參數數。目前只需要* 它用于 1、2 或 3 個參數。*/
#define __arg6(a1, a2, a3, a4, a5, a6, ...) a6
#define __count_args(...) __arg6(dummy, ##__VA_ARGS__, 4, 3, 2, 1, 0)
_Static_assert 編譯時斷言
_Static_assert(EVT_COUNT < 256, "Can only support 256 event types with 8 bits");
語法
…
...
是一個特殊的語法,用于表示可變參數列表。它允許函數接受不定數量的參數,通常用于實現可變參數函數(variadic functions)。可變參數函數可以接受任意數量和類型的參數,這在處理不確定數量的輸入時非常有用。- gcc 擴展語法
u32 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = MPIDR_INVALID };
[0 ... NR_CPUS-1]
是一個范圍表達式,表示從 0 到NR_CPUS-1
的所有索引。- … 表示范圍內的所有元素都將被初始化為
MPIDR_INVALID
。
Switch (i) {case 0 ... 10:printf("i is between 0 and 10\n");break;case 11 ... 20:printf("i is between 11 and 20\n");break;default:printf("i is greater than 20\n");break;
}
_Generic 用于實現類型安全的多態函數
_Generic
簡介
_Generic
是 C11 標準引入的一種關鍵字,用于實現類型泛型(type-generic programming)。它允許開發者根據表達式的類型選擇不同的代碼路徑,從而實現類似于函數重載的功能。_Generic
提供了一種在編譯時根據類型分發邏輯的機制,而無需運行時判斷。
使用場景
-
類型安全的多態函數:
- 在 C 中,函數重載并不被直接支持。通過
_Generic
,可以根據參數的類型選擇不同的實現,從而實現類似于 C++ 中函數重載的功能。 - 例如:為不同類型的輸入調用不同的處理函數。
- 在 C 中,函數重載并不被直接支持。通過
-
簡化代碼邏輯:
- 在需要對不同類型執行不同操作的場景中,
_Generic
可以避免冗長的if-else
或switch
語句。
- 在需要對不同類型執行不同操作的場景中,
-
提高代碼可讀性和可維護性:
- 通過
_Generic
,可以將類型分發邏輯集中到一個地方,減少重復代碼。
- 通過
-
內核開發和底層優化:
- 在 Linux 內核或其他底層代碼中,
_Generic
常用于為不同類型的同步原語(如自旋鎖、互斥鎖等)提供統一的接口。
- 在 Linux 內核或其他底層代碼中,
工作原理
_Generic
的基本語法如下:
_Generic(expression, type1: result1, type2: result2, ..., default: default_result)
expression
:需要判斷類型的表達式。type1, type2, ...
:列出可能的類型。result1, result2, ...
:對應類型的返回值或代碼路徑。default
(可選):當expression
的類型不匹配任何列出的類型時,選擇的默認結果。
實現細節
-
編譯時類型檢查:
_Generic
在編譯時解析表達式的類型,并選擇匹配的分支。這種機制避免了運行時的類型判斷,提高了性能。
-
類型匹配規則:
_Generic
使用嚴格的類型匹配規則。例如,int
和unsigned int
是不同的類型,必須分別列出。- 如果沒有匹配的類型且未提供
default
分支,編譯器會報錯。
-
與宏結合:
_Generic
通常與宏結合使用,以實現更靈活的接口。例如,可以為不同類型的輸入調用不同的函數。
-
嵌套使用:
_Generic
可以嵌套使用,以處理更復雜的類型分發邏輯。
優缺點
優點:
- 類型安全:
_Generic
在編譯時進行類型檢查,避免了運行時錯誤。
- 性能高效:
- 類型分發邏輯在編譯時完成,不會增加運行時開銷。
- 代碼簡潔:
- 避免了冗長的
if-else
或switch
語句,提高了代碼的可讀性。
- 避免了冗長的
缺點:
- 類型匹配限制:
_Generic
需要顯式列出所有可能的類型,無法處理動態類型。
- 復雜性:
- 對于復雜的類型分發邏輯,代碼可能變得難以維護。
- 兼容性:
_Generic
是 C11 標準的一部分,不支持 C11 的編譯器無法使用。
總結
_Generic
是 C11 提供的一種強大工具,用于實現類型安全的多態函數和類型分發邏輯。它在編譯時根據表達式的類型選擇代碼路徑,既提高了性能,又增強了代碼的可讀性和安全性。盡管存在一些限制,但在需要處理多種類型的場景中,_Generic
是一種非常實用的解決方案,尤其是在底層開發和性能優化中。