在 C 語言中,若想在程式執行期間取得目前函數的名稱,過去常見的作法是在函數內部硬編碼字串常數,例如 printf("myfunc()")。這不僅枯燥乏味,且極易在複製貼上時產生錯誤。隨著 C99 標準的推出,編譯器提供了標準化的機制來自動完成這項任務,本文將介紹如何使用 __func__、__FUNCTION__ 以及 Microsoft 擴展的 __FUNCDNAME__、__FUNCSIG__ 來優雅地解決此問題。
__func__ 與 __FUNCTION__C99 標準引入了 __func__ 預定義識別碼。它不是巨集,而是編譯器隱式宣告的一個靜態常數字元陣列:
static const char __func__[] = "function-name";
其中 function-name 為該函數的原始名稱(未經修飾)。由於 C++ 標準並未完全納入所有 C99 特性,主流編譯器(MSVC、GCC、Clang、Comeau、C++Builder)多提供 __FUNCTION__ 巨集作為替代或補充,其行為與 __func__ 基本一致。
__FUNCTION__,不支援 __func__;若使用 /EP 或 /P 編譯選項則失效。__func__ 與 __FUNCTION__。__FUNCTION__。__FUNC__。定義一個通用的顯示函數,即可在任意函數中輸出自身名稱,無需手動維護字串:
void show_name(const char* name) {
std::cout << name << std::endl;
}
void myfunc() {
show_name(__FUNCTION__); // 輸出:myfunc
}
void foo() {
show_name(__FUNCTION__); // 輸出:foo
}
因為 __FUNCTION__ 在函數左大括號後立即初始化,故可安全用於參數列表中,不受多載影響。
Visual C++ 提供兩個非標準擴展,對 C++ 開發者極具價值:
__FUNCDNAME__: 回傳函數的修飾名稱,可用於驗證 ABI 相容性、解析連結錯誤、或跨 DLL 呼叫 C++ 連結函數。?myfunc@@YAXXZ__FUNCSIG__: 回傳完整函數簽名,包含回傳型別、參數列表、類別名稱、const/volatile 限定詞等。void __cdecl myfunc(void) 或 void __thiscall S::myfunc(void) const善用 C99 的 __func__ 及編譯器擴展巨集,能大幅減少硬編碼字串的維護負擔,提升程式碼的可讀性與可維護性。在跨平台專案中,建議以條件編譯封裝統一介面,例如:
#if defined(_MSC_VER)
#define FUNC_NAME __FUNCTION__
#elif defined(__GNUC__)
#define FUNC_NAME __func__
#else
#define FUNC_NAME "unknown"
#endif