u-boot
和kernel
比較普遍地使用了__weak
來定義函式。
在include\linux\compiler-gcc.h
中__weak
是這樣定義的:
#define __weak __attribute__((weak))
gcc
通過__attribute__((weak))
指令定義的函式或變數稱為弱符號(weak symbol),實際上這個指令大部分時候都是用來定義函式,很少用於定義變數。
編譯器預設函式和初始化了的全域性變數為強符號(strong symbol),未初始化的全域性變數為弱符號(weak symbol)。
規則2:如果乙個符號在某個目標檔案中是強符號,在其他檔案中都是弱符號,那麼選擇強符號。
規則3:如果乙個符號在所有目標檔案中都是弱符號,那麼選擇其中占用空間最大的乙個。比如目標檔案a定義全域性變數global為int型,佔4個位元組;目標檔案b定義global為doulbe型,佔8個位元組,那麼目標檔案a和b鏈結後,符號global佔8個位元組(盡量不要使用多個不同型別的弱符號,否則容易導致很難發現的程式錯誤)。
多次定義的全域性函式也屬於此類,因為函式名也是乙個符號變數,只不過它指向了隨後定義的函式體,而且這個變數是唯讀的,不可改變。這也是為什麼函式名可以賦值給乙個變數,但是卻無法給函式名賦值的原因。
首先在平台無關的**中定義乙個__weak
屬性的函式,如u-boot
的board_f.c
的檔案頭部定義了很多led
相關的空函式:
__weak void coloured_led_init(void) {}
__weak void red_led_on(void) {} __weak void red_led_off(void) {} __weak void green_led_on(void) {} __weak void green_led_off(void) {} __weak void yellow_led_on(void) {} __weak void yellow_led_off(void) {} __weak void blue_led_on(void) {} __weak void blue_led_off(void) {}
如果需要這些led
相關的功能,再在相關的檔案中重新定義並實現這些函式即可,例如檔案gpio_led.c
中在巨集config_gpio_led_stubs
開啟時就呼叫__led_set
重新實現了這些函式。
《程式設計師的自我修養》第三章對也對強引用和弱引用做了說明:
弱引用和強引用目前我們所看到的對外部目標檔案的符號引用在目標檔案被最終鏈結成可執行檔案時,它們必須要被正確決議,如果沒有找到該符號的定義,鏈結器就會報符號未定義錯誤,這種被稱為強應用(strong reference)。與之相對應還有一種弱引用(weak reference),在處理弱引用時,如果該符號有定義,則鏈結器將該符號的引用決議;如果該符號未被定義,則鏈結器對於該引用不報錯。鏈結器處理強引用和弱引用的過程幾乎一樣,只是對於未定義的弱引用,鏈結器不認為它是乙個錯誤。一般對於未定義的弱引用,鏈結器預設其為0,或者是乙個特殊的值,以便於程式**能夠識別。在
gcc
中,我們可以通過使用__attribute__((weakref))
擴充套件關鍵字來宣告對乙個外部函式的引用為弱引用。比如下面這段**:
__attribute__ ((weakref)) void foo();
int main()
我們可以將它編譯成乙個可執行檔案,gcc
並不會報鏈結錯誤。但是當我們執行這個可執行檔案時,會發生執行錯誤。因為當main
函式試圖條用foo
函式是,foo
函式的位址為0,於是發生了非法位址訪問的錯誤。乙個改進的例子是:
__attribute__ ((weakref)) void foo();
int main()
這種弱符號和弱引用對於庫來說十分有用,比如庫中定義的弱符號可以被使用者定義的強符號所覆蓋,從而使得程式可以使用自定義版本的庫函式;或者程式可以對某些擴充套件功能模組的引用定義為弱引用,當我們將擴充套件模組與程式連線在一起時,功能模組就可以正常使用;如果我們去掉了某些功能模組,那麼程式也可以正常鏈結,只是缺少了相應的功能,這使得程式的功能更加容易裁剪和組合。專門檢查過
u-boot v2016.09
和linux v3.3-3.8
的**,沒有找到採用弱引用來定義函式的情況,可見也不是很常用的做法。
對於弱符號(weak symbol)和弱引用,其都僅是gnu
工具鏈gcc
對c
語言語法的擴充套件,並不是c
本身的語言特性。
有提到微軟的msvc
就不支援弱符號和弱引用特性。
GCC中的強符號和弱符號及強引用和弱引用
u boot和kernel比較普遍地使用了 weak來定義函式。在include linux compiler gcc.h中 weak是這樣定義的 define weak attribute weak gcc通過 attribute weak 指令定義的函式或變數稱為弱符號 weak symbol ...
強符號和弱符號
在c語言中,函式和初始化的全域性變數 包括顯示初始化為0 是強符號,未初始化的全域性變數是強符號。關於多個強弱符號定義型別不一致的主要有下面三種情況 1.兩個或兩個以上強符號型別不一致 2.有乙個強符號,其他都是弱符號 3.兩個或兩個以上弱符號型別不一致 對於情況一,編譯會報符號重定義錯誤。mapa...
C 強符號和弱符號
1 不允許強符號被多次定義,也即不同的目標檔案中不能有同名的強符號 如果有多個強符號,那麼鏈結器會報符號重複定義錯誤。2 如果乙個符號在某個目標檔案中是強符號,在其他檔案中是弱符號,那麼選擇強符號。3 如果乙個符號在所有的目標檔案中都是弱符號,那麼選擇其中占用空間最大的乙個。在 gcc 中,可以通過...