日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ARM 之十一__weak 和 __attribute__((weak)) 关键字的使用

發(fā)布時間:2024/10/14 编程问答 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ARM 之十一__weak 和 __attribute__((weak)) 关键字的使用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

??今天在使用 Keil (主要是 armcc 編譯器)編譯代碼(華大的 MCU 驅(qū)動庫hc32f46x_interrupts.h / c)的時候遇到了有 __weak 關(guān)鍵字的函數(shù)不起作用的問題,甚是奇怪。之前對于 __weak 關(guān)鍵字一直是一個簡單的認(rèn)知:編譯器自動使用沒有 __weak 的同名函數(shù)(如果有的話)替換有 __weak 關(guān)鍵字的同名函數(shù),__weak 函數(shù)可以沒有定義,且編譯器不會報(bào)錯! 至于這個參數(shù)詳細(xì)的使用細(xì)節(jié)一直是一知半解,今天借此機(jī)會,以 GCC 作為對比,來學(xué)習(xí)一下 ARM 中的 __weak 關(guān)鍵字的具體使用!

來源

??使用過 GCC 以及有 linux 編程經(jīng)驗(yàn)的人,對于這個關(guān)鍵字應(yīng)該不陌生。GNU 的編譯器(gcc)擴(kuò)展了一個關(guān)鍵字 __attribute__,通過該關(guān)鍵字,用戶可以在聲明時指定特殊的屬性,使用時該關(guān)鍵字后跟雙括號內(nèi)的屬性,例如:__attribute__((屬性名字))。屬性名字都是定義好的,Weak 屬性就是其中之一:__attribute__((weak))。在 linux 源碼中,該關(guān)鍵字非常常見:

??GCC 不多介紹,重點(diǎn)關(guān)注 ARM。在 ARM 編譯器(armcc)中,支持和 GCC 相同的關(guān)鍵字 __attribute__,使用方式也基本相同,如下:

__attribute__((attribute1, attribute2, ...)) // 例如:void * Function_Attributes_malloc_0(int b) __attribute__((malloc)); __attribute__((__attribute1__, __attribute2__, ...)) // 例如:static int b __attribute__((__unused__));
  • 當(dāng)函數(shù)屬性發(fā)生沖突時,編譯器將使用更安全或更強(qiáng)的一個
  • ??除此之外,ARM 編譯器(armcc)還擴(kuò)展了一個關(guān)鍵字 __weak,例如:__weak void f(void); 或者 __weak int i;。ARM 的匯編器(armasm)以另一種方式 [WEAK] 支持該特性。

    注意:
    ??在許多源碼中,經(jīng)常通過宏定義的形式來定義關(guān)鍵字,例如 上面linux 中的 __weak 就是 宏定義的 __attribute__((weak))

    強(qiáng)/弱符號

    ??在 GCC 中,被 __attribute__((weak)) 修飾的符號,我們稱之為 弱符號(Weak Symbol)。例如:弱函數(shù)、弱變量;沒有 __attribute__((weak)) 修飾的符號被稱為強(qiáng)符號。在 ARM 中,沒有弱符號和強(qiáng)符號這種叫法,只有個弱引用(Weak References)非弱引用(non-weak reference )弱定義(Weak definitions)非弱定義(non-weak definition) 的介紹章節(jié)。

  • 編譯器和匯編器都可以輸出弱符號。
  • 非弱引用

    ??非弱引用就是我們平常使用的對于非弱函數(shù)或者弱變量的引用。如果鏈接器無法在到目前為止已加載內(nèi)容中解析對正常非弱符號的引用問題,則 它會嘗試通過在庫中找到符號 來解決此問題:

    • 如果找不到此類引用,則鏈接器將報(bào)告錯誤。
    • 如果解析了這樣的引用,則從入口點(diǎn)可以通過至少一個非弱引用來訪問的節(jié)區(qū)被標(biāo)記為已使用。這樣可以確保鏈接器不會將該節(jié)作為未使用的節(jié)刪除。 每個非弱引用都必須通過一個定義來解決。 如果有多個定義,則鏈接器將報(bào)告錯誤。

    弱引用

    ??引用弱聲明的函數(shù)或者變量的引用即為弱引用。 鏈接器不會從庫中加載對象來解析弱引用。僅當(dāng)由于其他原因在鏡像中包含了定義時,它才能解析弱引用。弱引用不會導(dǎo)致鏈接器將包含定義的節(jié)區(qū)標(biāo)記為已使用,因此鏈接器可能會將其標(biāo)記為未使用而刪除。

    __weak

    __weak 關(guān)鍵字可以應(yīng)用于函數(shù)和變量的聲明以及函數(shù)定義。

    聲明

    ??__weak 可以用于函數(shù)聲明或者變量的聲明。對于聲明,此存儲類指定一個 extern 對象聲明,即使該對象不存在,對于該聲明的引用也不會導(dǎo)致鏈接器對未解析的引用(找不到定義的引用)當(dāng)做錯誤來處理。
    ??如果在當(dāng)前編譯單元中可以找到 __weak 聲明定義,則會用找到的定義替換 __weak 引用;對于找不到定義 __weak 的聲明(函數(shù)或變量,如上圖的 FuncB),編譯器做如下處理:

    • 引用被解析為分支連接指令 BL。等效于將被引用的分支為 NOP
    • 直接將引用替換為 NOP 指令

    注意:必須是在當(dāng)前編譯單元,不再當(dāng)前編譯單元的沒有意義(例如 ExtFuncA 在 main.c 中只有__weak 聲明,但是沒有定義)。具體看下圖的測試代碼:

    注意:用 __weak 聲明然后不使用 __weak 定義的函數(shù)的行為相當(dāng)于非弱函數(shù)。 這與 _attribute__((weak)) 關(guān)鍵字不同!

    定義

    ??用 __weak 定義的函數(shù)弱輸出其符號。弱定義的函數(shù)的行為類似于正常定義的函數(shù),除非將同名的非弱定義的函數(shù)鏈接到同一鏡像中。 如果在同一鏡像中同時存在非弱定義函數(shù)和弱定義函數(shù),則對該函數(shù)的所有調(diào)用都會解析為調(diào)用非弱函數(shù),否則直接使用弱定義的函數(shù)(與上面的若聲明不同)。
    ??如果可以使用多個弱定義,則除非使用鏈接器選項(xiàng) --muldefweak,否則鏈接器會生成一條錯誤消息。在這種情況下,鏈接器隨機(jī)選擇一個供所有調(diào)用來使用。使用方式如下:

    /* a.h !!!注意所在文件不同!!! */ void FuncA(void); void FuncB(void);/* a.c !!!注意所在文件不同!!! */ void FuncA(void) {FuncB(); /* 這里將替換為 main.c 中的 FuncB */ }__weak void FuncB(void) /* 弱定義 */ {}/* main.c !!!注意所在文件不同!!! */ void FuncB(void) {}int main (void) {FuncB(); }

    注意,函數(shù)的聲明一定不能添加 __weak 關(guān)鍵字。具體如下圖:

    注意:用 __weak 聲明然后不使用 __weak 定義的函數(shù)的行為相當(dāng)于非弱函數(shù)。 這與 _attribute__((weak)) 關(guān)鍵字不同!

    限制

  • 函數(shù)或變量不能在同一編譯中同時弱和非弱地使用。
  • void f(void);void g() {f(); /* 非弱函數(shù)引用 */ }__weak void f(void);void h() {f(); /* 弱函數(shù)引用 */ }
  • 不能在定義函數(shù)或變量的同一編譯中使用弱函數(shù)或弱變量,如下將導(dǎo)致編譯錯誤(正確的使用方式參考上面的使用示例):
  • /* a.c 如下同一文件中的定義及使用將報(bào)錯 */ __weak void f(void);void h() {f(); }void f() {}
  • 弱函數(shù)不能是內(nèi)聯(lián)函數(shù)
  • __attribute__((weak))

    ??__attribute__關(guān)鍵字使您可以指定變量或結(jié)構(gòu)字段,函數(shù)和類型的特殊屬性(與具體屬性)。該關(guān)鍵字的作用與 __weak 的作用基本是一樣的,在使用時有些不同,此外在某些情況下,編譯的處理也有些區(qū)別。

    聲明

    ??這個參數(shù)是 GUN 編譯器的一個擴(kuò)展,ARM 編譯器也支持該關(guān)鍵字。__attribute__((weak)) 可以聲明弱變量,并且其聲明方式與 __weak 相比更加靈活。除了 __weak 的聲明方式,我們還可以用 extern int Variable_Attributes_weak_1 __attribute__((weak));
    ??_attribute__((weak)) 可以聲明弱函數(shù),其聲明方式與 __weak 相比更加靈活。除了 __weak 的聲明方式,我們還可以用 extern int Function_Attributes_weak_0 (int b) __attribute__((weak));。

    ??任何包含了 __attribute__((weak)); 聲明的文件的中的同名函數(shù)定義,都將被當(dāng)做弱函數(shù)。如下圖:

    開篇提出的問題就是因?yàn)樯蠄D所示的這種情況!

    ??注意:用 __attribute__((weak)) 聲明然后不使用 __attribute__((weak)) 進(jìn)行定義的函數(shù)的行為就像是弱函數(shù)。 這與 __weak 關(guān)鍵字的用法不同。

    在 GNU 模式中需要 extern 限定符。在非 gnu 模式下,編譯器假設(shè)如果變量不是 extern,那么它將像對待其他非弱變量一樣對待。

    定義

    ??用 __attribute__((weak)) 定義的函數(shù)弱輸出其符號(與 __weak 相同)。其使用方式有以下兩種:

    __attribute__((weak)) void FuncA(void) {printf("Weak FuncA!\r\n"); } /* 或者 */ void __attribute__((weak)) FuncA(void) {printf("Weak FuncA!\r\n"); }

    ??注意:用 __attribute__((weak)) 聲明然后不使用 __attribute__((weak)) 進(jìn)行定義的函數(shù)的行為就像是弱函數(shù)。 這與 __weak 關(guān)鍵字的用法不同。除此之外,沒有啥不同,這里不再多說!

    區(qū)別

  • 如上介紹,__weak 和 __attribute__((weak)) 在聲明和定義的時候,其所處的位置有不同。
  • __weak 僅在函數(shù)定義中使用時才會生成弱函數(shù)。而在任何情況下(聲明和定義) __attribute__((weak)) 都會生成弱函數(shù),無論是用于函數(shù)定義還是用于函數(shù)聲明中!
  • 參考

  • https://community.arm.com/developer/tools-software/tools/f/keil-forum/34584/run-error-when-use-__weak-to-define-function
  • https://blog.csdn.net/rensheng__rumeng/article/details/78634804
  • http://blog.sina.com.cn/s/blog_62d3426b0100g7n6.html
  • http://www.keil.com/support/man/docs/armcc/armcc_chr1359124970859.htm
  • http://infocenter.arm.com/help/topic/com.arm.doc.dui0472j/DUI0472J_armcc_user_guide.pdf
  • https://github.com/ARM-software/CMSIS_5/issues/141
  • 總結(jié)

    以上是生活随笔為你收集整理的ARM 之十一__weak 和 __attribute__((weak)) 关键字的使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。