首頁 > 軟體

C 語言的弱符號與弱參照你瞭解嗎

2022-03-28 13:01:19

C語言中的__attribute__((weak)) 與 attribute ((weakref())

引言:最近在看 linux 中一些驅動程式碼。驅動程式碼中為了實現程式的擴充套件性和相容性用了很多 C 語言中的高階特性。本節就來談一談 C 語言中的弱符號和弱參照的用法。

弱符號

弱符號是指在定義或者宣告一個物件(變數、結構體成員、函數)時,在物件的前面新增 __attribute__((weak)) 標誌所得到的物件符號。如下所示函數即為一個弱物件符號 void test_weak_attr(void),或者稱該函數是弱函數屬性的、虛擬函式。

__attribute__((weak)) void test_weak_attr(void)
// 或者使用如下樣式的定義,兩者等效
void __attribute__((weak)) test_weak_attr(void)
{
    printf("Weak Func!rn");
}

弱符號的作用與範例

弱符號是相對於強符號而言的,在定義或者宣告變數、函數時,未新增 __attribute__((weak)) 標識的就預設為強符號。如下,最普通的函數定義,就是定義了一個強符號 void test_strong_ref(void):

void test_weak_attr(void)
{
    printf("this is a strong funcrn");
}

驅動程式往往需要考慮相容性,因為要兼任很多廠商的不同型號的裝置。若驅動程式中使用強符號定義一些與適配的裝置的特性相關的功能,則下次適配其他裝置時,該強符號函數可能需要被修改,以相容新的裝置。當適配的裝置很多時,頻繁地更改驅動程式碼將破壞驅動的可維護性。

弱符號的出現可以很好地解決該問題。弱符號的物件具有可以被重定義的功能(即可以被過載)。下面通過測試說明弱符號這種可被過載的特性。

在 test_weak_attr.c 程式中定義如下弱函數:

// test_weak_attr.c
#include <stdio.h>

__attribute__((weak)) void test_weak_attr(void)
{
    printf("this is a weak funcrn");
}

在 main.c 中定義如下程式:

// main.c
void test_weak_attr(void)
{
    printf("this is a strong funcrn");
}

void app_main(void)
{
    printf("init donern");
    
    test_weak_attr();
}

編譯執行該 main.c 程式,得到的結果是什麼樣子的呢?

this is a strong func

將 main.c 中的 void test_weak_attr(void) 函數註釋掉,再重新編譯執行程式得到的結果是:

this is a weak func

小結:在使用弱符號函數時,我們可以重新定義一個同名的強符號函數來替代它;若沒有重新定義一個強函數來替換它,就使用弱函數的實現。弱函數就好像是一個可以被替換的“預設函數”。

值得一提的是,舊版本的編譯器還可以使用如下方式的定義(僅宣告無效)將一個物件定義為一個弱物件:

__weak void f(void)
{
//code
}

在 linux 的一些程式碼中,__weak 其實就是通過 __attribute__((weak))的重新命名,兩者等效。

弱參照

弱參照是在宣告一個物件時,通過__attribute__ ((weakref()) 定義一個符號的參照關係。如下所示即定義 test_weakref() 函數弱參照 test_weak_ref() 函數。

static void test_weakref(void) __attribute__ ((weakref("test_weak_ref")));

弱參照是相對於強參照而言的。未通過 __attribute__ ((weakref()) 的符號和實現程式碼之間的關係是強參照。如下即為一個強參照函數。它直接給出了 函數 test_strong_ref(void) 的實現。

static void test_strong_ref(void)
{
    printf("this is a strong refrn");
}

在編譯程式的時候,我們可以直接使用 test_strong_ref(void) 而不必擔心編譯不通過。如果,我沒有時間去實現 test_strong_ref(void) ,還想在程式裡先使用該函數那該如何呢?(是的,就是想白嫖,不想實現,還想先在程式裡使用這個函數)。

這個時候弱參照就派上用場了。可以先將該函數定義為弱參照插入到程式碼中,待後期有時間再慢慢優化程式碼實現這個函數完整的功能。下面結合測試進行說明。

測試程式碼1:

static void test_weakref(void) __attribute__ ((weakref("test_weak_ref")));
void app_main(void)
{
    printf("init donern");
    if (test_weakref) {
        test_weakref();
    } else {
        printf("There is no weakrefrn");
    }
}

測試結果:

There is no weakref

測試程式碼2:

void test_weak_ref(void)
{ 
    printf("this is a weak refn");
}
static void test_weakref(void) __attribute__ ((weakref("test_weak_ref")));
void app_main(void)
{
    printf("init donern");
    if (test_weakref) {
        test_weakref();
    } else {
        printf("There is no weakrefrn");
    }
}

測試結果:

this is a weak ref

小結: 強參照,在未定義該強參照的實現時,編譯會報錯誤:未定義的參照。弱參照允許定義一個未實現(未範例化)的物件,這在編譯的時候會將該物件處理成 NULL,編譯器並不會報錯。通過使用弱參照可以實現後期優化程式碼的功能。而避免改動使用該函數的地方。使用弱函數可以實現類似“勾點(hook)"函數的功能。

實際上,包括C、python、go 程式語言在內的很多語言 都有類似用法,本篇文章敘述的方法同樣適用於這些語言的相關開發。

注意:弱參照僅在靜態編譯中有效,動態連結中可能無效。

總結

弱符號、弱參照都是增強程式的可維護性的方法。弱符號通過可以被重定義的特性,實現可以被替換實現。弱參照通過可以暫時使用一個未定義的函數的功能,實現允許後期再實現該函數具體功能,而不必擔心編譯不通過。

本篇文章就到這裡了,希望能夠給你帶來幫助,也希望您能夠多多關注it145.com的更多內容!    


IT145.com E-mail:sddin#qq.com