www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > C語言與CPP編程
[導(dǎo)讀]從C11開始,標(biāo)準(zhǔn)引入了一個新概念“屬性(attribute)”,本文將簡單介紹一下目前在C標(biāo)準(zhǔn)中已經(jīng)添加的各個屬性以及常用屬性的具體應(yīng)用。?一?屬性(Attribute)的前世今生其實(shí)C早在[pre03]甚至更早的時候就已經(jīng)有了屬性的需求。彼時,當(dāng)程序員需要和編譯器溝通,為某些...


從C 11開始,標(biāo)準(zhǔn)引入了一個新概念“屬性(attribute)”,本文將簡單介紹一下目前在C 標(biāo)準(zhǔn)中已經(jīng)添加的各個屬性以及常用屬性的具體應(yīng)用。?
一? 屬性(Attribute)的前世今生
其實(shí)C 早在[pre03]甚至更早的時候就已經(jīng)有了屬性的需求。彼時,當(dāng)程序員需要和編譯器溝通,為某些實(shí)體添加一些額外的信息的時候,為了避免“發(fā)明”一個新的關(guān)鍵詞乃至于引起一些語法更改的麻煩,同時又必須讓這些擴(kuò)展內(nèi)容不至于“污染”標(biāo)準(zhǔn)的命名空間,所以標(biāo)準(zhǔn)保留了一個特殊的用戶命名空間——“雙下劃線關(guān)鍵詞”,以方便各大編譯器廠商能夠根據(jù)需要添加相應(yīng)的語言擴(kuò)展。根據(jù)這個標(biāo)準(zhǔn),各大編譯器廠商都做出了自己的擴(kuò)展實(shí)現(xiàn),目前在業(yè)界廣泛使用的屬性空間有GNU和IBM的 __attribute__(()),微軟的 __declspec(),甚至C#還引入了獨(dú)特的單括號系統(tǒng)(single bracket system)來完成相應(yīng)的工作。
隨著編譯器和語言標(biāo)準(zhǔn)的發(fā)展,尤其是C 多年來也開始逐漸借鑒其他語言中的獨(dú)特擴(kuò)展,屬性相關(guān)的擴(kuò)展也越來越龐大。但是Attribute的語法強(qiáng)烈依賴于各大編譯器的具體實(shí)現(xiàn),彼此之間并不兼容,甚至部分關(guān)鍵屬性導(dǎo)致了語言的分裂,最終都會讓使用者的無所適從。所以在C 11標(biāo)準(zhǔn)中,特意提出了C 語言內(nèi)置的屬性概念。提案大約是在2007年前后形成,2008年9月15日的提案版本n2761被正式接納為C 11標(biāo)準(zhǔn)中的Attribute擴(kuò)展部分(此處歷史略悠久,很可能有不準(zhǔn)確的部分,歡迎各位指正)。
二? 屬性的語法定義
正如我們在上一節(jié)討論的,屬性的關(guān)鍵要求就是避免對標(biāo)準(zhǔn)用戶命名空間的污染,同時對于未來可能引入的更多屬性,我們需要有一個方式可以避免新加的“屬性關(guān)鍵字”破壞當(dāng)前已有的C 語法。所以新標(biāo)準(zhǔn)采用了“雙方括號”的語法方式引入了屬性說明,比如[[noreturn]]就是一個標(biāo)準(zhǔn)的C 屬性定義。而未來新屬性的添加都被控制在雙方括號范圍之內(nèi),不會進(jìn)入標(biāo)準(zhǔn)的命名空間。
按照C 語言標(biāo)準(zhǔn),下列語言實(shí)體可以被屬性所定義/并從中獲益:
  • 函數(shù)

  • 變量

  • 函數(shù)或者變量的名稱

  • 類型

  • 程序塊

  • Translation Unit (這個不知道用中文咋說)

  • 程序控制聲明


根據(jù)C 的標(biāo)準(zhǔn)提案,屬性可以出現(xiàn)在程序中的幾乎所有的位置。當(dāng)然屬性出現(xiàn)的位置和其修飾的對象是有一定關(guān)聯(lián)的,屬性僅在合適的位置才能產(chǎn)生效果。比如[[noreturn]必須出現(xiàn)在函數(shù)定義的位置才會產(chǎn)生效果,如果出現(xiàn)在某個變量的聲明處則無效。根據(jù)C 17的標(biāo)準(zhǔn),未實(shí)現(xiàn)的或者無效的屬性均應(yīng)該被編譯器忽略且不產(chǎn)生任何錯誤報告(在C 17標(biāo)準(zhǔn)之前的編譯器則參考編譯器的具體實(shí)現(xiàn)會有不同的行為)。
由于屬性可以出現(xiàn)在幾乎所有的位置,那么它是如何關(guān)聯(lián)到具體的作用對象呢?下面我引用了語言標(biāo)準(zhǔn)提案中的一個例子幫助大家理解屬性是如何作用于語言的各個部分。
[[attr1]] class C [[ attr2 ]] { } [[ attr3 ]] c [[ attr4 ]], d [[ attr5 ]];
  • attr1 作用于class C的實(shí)體定義c和d

  • attr2 作用于class C的定義?

  • attr3 作用于類型C?

  • attr4 作用于實(shí)體c?

  • attr5 作用于實(shí)體d??


以上只是一個基本的例子,具體到實(shí)際的編程中,還有有太多的可能,如有具體情況可以參考C 語言標(biāo)準(zhǔn)或者編譯器的相關(guān)文檔。
三? 主流C 編譯器對于屬性的支持情況
目前的主流編譯器對于C 11的支持已經(jīng)相對很完善了,所以對于屬性的基本語法,大部分的編譯器都已經(jīng)能夠接納。不過對于在不同標(biāo)準(zhǔn)中引入的各個具體屬性支持則參差不齊,對于相關(guān)屬性能否發(fā)揮應(yīng)有的作用更需要具體問題具體分析。當(dāng)然,在標(biāo)準(zhǔn)中(C 17)也明確了,對于不支持或者錯誤設(shè)定的屬性,編譯器也能夠忽略不會報錯。
下圖是目前主流編譯器對于n2761屬性提案的支持情況:



對于未知或不支持的屬性忽略報錯的主流編譯器支持情況:



四? 目前C 標(biāo)準(zhǔn)中引入的標(biāo)準(zhǔn)屬性
C 11引入標(biāo)準(zhǔn):
  • [[noreturn]]

  • [[carries_dependency]]


C 14引入標(biāo)準(zhǔn):
  • [[deprecated]] 和 [[deprecated("reason")]]


C 17引入標(biāo)準(zhǔn):
  • [[fallthrough]]

  • [[nodiscard]] 和 [[nodiscard("reason")]] (C 20)

  • [[maybe_unused]]


C 20引入標(biāo)準(zhǔn):
  • [[likely]] 和 [[unlikely]]

  • [[no_unique_address]]


接下來我將嘗試對已經(jīng)引入標(biāo)準(zhǔn)的屬性進(jìn)行進(jìn)一步的說明,同時對于已經(jīng)明確得到編譯器支持的屬性,我也會嘗試用例子進(jìn)行進(jìn)一步的探索,希望拋磚引玉能夠幫大家更好的使用C 屬性這個“新的老朋友”。
1? [[noreturn]]
從字面意義上來看,noreturn是非常容易理解的,這個屬性的含義就是標(biāo)明某個函數(shù)一定不會返回。
請看下面的例子程序:
// 正確,函數(shù)將永遠(yuǎn)不會返回。[[noreturn]] void func1(){?throw?"error";?}
// 錯誤,如果用false進(jìn)行調(diào)用,函數(shù)是會返回的,這時候會導(dǎo)致未定義行為。[[noreturn]] void func2(bool b){?if?(b)?throw?"error";?}
int main(){ try {?func1()??;?} catch(char const *e) {?std::cout?<"Got?something:?"?<"??\n";?}
// 此處編譯會有警告信息。 func2(false);}
這個屬性最容易被誤解的地方是返回值為void的函數(shù)不代表著不會返回,它只是沒有返回值而已。所以在例子中的第一個函數(shù)func1才是正確的無返回函數(shù)的一個例子;而func2在參數(shù)值為false的情況下,它還是一個會返回的函數(shù)。所以,在編譯的時候,編譯器會針對func2報告如下錯誤:
noreturn.cpp: In function 'void func2(bool)':noreturn.cpp:11:1: warning: 'noreturn' function does return 11 | } | ^
而實(shí)際運(yùn)行的時候,func2到底會有什么樣的表現(xiàn)屬于典型的“未定義行為”,程序可能崩潰也可能什么都不發(fā)生,所以一定要避免這種情況在我們的代碼中出現(xiàn)。(我在gcc11編譯器環(huán)境下嘗試過幾次,情況是什么都不發(fā)生,但是無法保證這是確定的行為。)
另外,[[noreturn]]只要函數(shù)最終沒有返回都是可以的,比如用exit()調(diào)用直接將程序干掉的程序也是可以被編譯器接受的行為(只是暫時沒想到為啥要這么干)。
2? [[carries_dependency]]
這個屬性的作用是允許我們將dependency跨越函數(shù)進(jìn)行傳遞,用于避免在弱一致性模型平臺上產(chǎn)生不必要的內(nèi)存柵欄導(dǎo)致代碼效率降低。
一般來說,這個屬性是搭配 std::memory_order_consume 來使用的,支持這個屬性的編譯器可以根據(jù)屬性的指示生成更合適的代碼幫助程序在線程之間傳遞數(shù)據(jù)。在典型的情況下,如果在 memory_order_consume 的情況下讀取一個值,編譯器為了保證合適的內(nèi)存讀取順序,可能需要額外的內(nèi)存柵欄協(xié)調(diào)程序行為順序,但是如果加上了[[carries_dependency]]的屬性,則編譯器可以保證函數(shù)體也被擴(kuò)展包含了同樣的dependency,從而不再需要這個額外的內(nèi)存柵欄。同樣的事情對于函數(shù)的返回值也是一致的。
參考如下例子代碼:
std::atomic<int *> p;std::atomic<int?*>?q;
void func1(int *val){?std::cout?<std::endl;?}
void func2(int * [[carries_dependency]] val){ q.store(val, std::memory_order_release);std::cout?<std::endl;?}
void thread_job(){ int *ptr1 = (int *)p.load(std::memory_order_consume); // 1 std::cout << *ptr1 << std::endl; // 2 func1(ptr1); // 3 func2(ptr1); // 4}
  • 程序在1的位置因?yàn)閜tr1明確的使用了memory_order_consume的內(nèi)存策略,所以對于ptr1的訪問一定會被編譯器排到這一行之后。


  • 因?yàn)?的原因,所以這一行在編譯的時候勢必會排列在1后面。


  • func1并沒有帶任何屬性,而他訪問了ptr1,那么編譯器為了保證內(nèi)存訪問策略被尊重所以必須在func1調(diào)用之間構(gòu)建一個內(nèi)存柵欄。如果這個線程被大量的調(diào)用,這個額外的內(nèi)存柵欄將導(dǎo)致性能損失。


  • 在func2中,我們使用了[[carries_dependency]]屬性,那么同樣的訪問ptr1,編譯器就知道程序已經(jīng)處理好了相關(guān)的內(nèi)存訪問限制。這個也正如我們再func2中對val訪問所做的限制是一樣的。那么在func2之前,編譯器就無需再插入額外的內(nèi)存柵欄,提高了效率。


3? [[deprecated]] 和 [[deprecated("reason")]]
這個屬性是在C 14的標(biāo)準(zhǔn)中被引入的。被這個屬性加持的名稱或者實(shí)體在編譯期間會輸出對應(yīng)的警告,告訴使用者該名稱或者實(shí)體將在未來被拋棄。如果指定了具體的"reason",則這個具體的原因也會被包含在警告信息中。?
參考如下例子程序:
[[deprecated]]void old_hello() {}
[[deprecated("Use new_greeting() instead. ")]]void old_greeting() {}
int main(){ old_hello(); old_greeting(); return 0;}
支持對應(yīng)屬性的編譯器上,這個例子程序是可以通過編譯并正確運(yùn)行的,但是編譯的過程中,編譯器會對屬性標(biāo)志的函數(shù)進(jìn)行追蹤,并且打印出相應(yīng)的信息(如果定義了的話)。在我的環(huán)境中,編譯程序給出了我如下的提示信息:

deprecated.cpp: In function 'int main()':deprecated.cpp:9:14: warning: 'void old_hello()' is deprecated [-Wdeprecated-declarations] 9 | old_hello(); | ~~~~~~~~~^~deprecated.cpp:2:6: note: declared here 2 | void old_hello() {} | ^~~~~~~~~deprecated.cpp:10:17: warning: 'void old_greeting()' is deprecated: Use new_greeting() instead. [-Wdeprecated-declarations] 10 | old_greeting(); | ~~~~~~~~~~~~^~deprecated.cpp:5:6: note: declared here 5 | void old_greeting() {} | ^~~~~~~~~~~~
[[deprecated]]屬性支持廣泛的名字和實(shí)體,除了函數(shù),它還可以修飾:

  • 類,結(jié)構(gòu)體

  • 靜態(tài)數(shù)據(jù)成員,非靜態(tài)數(shù)據(jù)成員

  • 聯(lián)合體,枚舉,枚舉項(xiàng)

  • 變量,別名,命名空間

  • 模板特化


4? [[fallthrough]]
這個屬性只可以用于switch語句中,通常在case處理完畢之后需要按照程序設(shè)定的邏輯退出switch塊,通常是添加break語句;或者在某些時候,程序又需要直接進(jìn)入下一個case的判斷中。而現(xiàn)代編譯器通常會檢測程序邏輯,在前一個case處理完畢不添加break的情況下發(fā)出一個警告信息,讓作者確定是否是他的真實(shí)意圖。但是,在case處理部分添加了[[fallthrough]]屬性之后,編譯器就知道這是程序邏輯有意為之,而不再給出提示信息。
5? [[nodiscard]] 和 [[nodiscard("reason")]]
這兩個屬性和前面的[[deprecated]]類似,但是他們是在不同的C 標(biāo)準(zhǔn)中被引入的,[[nodiscard]]是在C 17標(biāo)準(zhǔn)中引入,而[[nodiscard("reason")]]是在C 20標(biāo)準(zhǔn)中引入。
這個屬性的含義是明確的告訴編譯器,用此屬性修飾的函數(shù),其返回值(必須是按值返回)不應(yīng)該被丟棄,如果在實(shí)際調(diào)用中舍棄了返回變量,則編譯器會發(fā)出警示信息。如果此屬性修飾的是枚舉或者類,則在對應(yīng)函數(shù)返回該類型的時候也不應(yīng)該丟棄結(jié)果。
參考下面的例子程序:
struct [[nodiscard("IMPORTANT THING")]] important {};important i = important();important get_important() { return i; }important
本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉