連結器、載入器與目的檔格式

弱符號(a weak symbol)

通常若兩個目的檔都用同一個名字定義了全域函式或變數,連結器會抱怨:'multiple definition'。而若一個名字被使用卻在任何地方都沒被定義,連結器也會抱怨:'undefined reference'。弱符號放寬了這兩條規則。它是一個實質上說「只有在沒有更強者存在時才用我」的定義,或一個說「我想要這個,但它不在也沒關係」的參照。它是一種有禮貌、可選的符號。

這有兩面。弱「定義」是一個被標為 weak 的全域符號:若同名的強(一般)定義也存在,強者勝出,弱者被默默忽略——不會有 'multiple definition' 錯誤。這讓函式庫能提供一個預設實作,使用者只要定義自己的版本就能覆蓋它。弱「參照」是對被標為 weak 之符號的使用:若在連結或載入時找不到定義,該參照就單純地留為位址零(空指標)而非錯誤,於是你可以在執行期測試 'if (the_symbol)' 來看它是否被提供。在 C 中,你用編譯器屬性建立這些,例如在 GCC 與 Clang 上於宣告或定義上寫 __attribute__((weak))。

它作為可覆蓋預設值與可選功能背後的機制而重要:執行期可為某個掛勾函式提供弱預設並讓程式取代它,而程式可弱參照一個只存在於較新版函式庫的符號,使它在較舊版上仍能連結與執行。經典陷阱是「沉默」:因為弱定義被覆蓋時不出警告,兩個各自提供同名弱符號的函式庫可能產生取決於連結順序的結果,這很難除錯。而解析為 null 的弱參照,若你沒先檢查它非 null 就呼叫,會當掉。

// 在函式庫裡:使用者可覆蓋的弱預設。 __attribute__((weak)) void on_startup(void) { /* 什麼都不做 */ } // 弱參照:使用前可安全測試。 extern void optional_hook(void) __attribute__((weak)); if (optional_hook) optional_hook(); // 若未解析則跳過

弱定義會無誤地讓位給任何強定義;弱參照若缺失則解析為 null,所以你用檢查來保護該呼叫。

呼叫一個未解析的弱參照(空指標)會當掉,不是什麼都不做——務必先測試它。而且因為覆蓋是默默發生的,跨函式庫依賴弱符號會使行為取決於連結順序。

又稱
weak bindingSTB_WEAK弱繫結