C語言的“編譯時多態”

typeof 在 kernel 中的使用 —— C 語言的“編譯時多態”

C 語言本身沒有多態的概念,函數沒有重載的概念。然而隨著 C 語言編寫的軟件逐漸龐大,越來越多地需要引入一些其他語言中的特性,來幫助更高效地進行開發,Linux kernel 是一個典型例子。

在動態類型的語言里面,往往有 typeof 這種語法,來獲取變量的數據類型,比如 JavaScript 當中,typeof 以字符串型式返回了這個變量的數據類型,借由這種特性,往往可以根據傳入參數的類型不同,產生不同的行為。

GCC 提供的 typeof,實際上是在預編譯時處理的,最后實際轉化為數據類型被編譯器處理。用法上也和上述語言不太一樣。

基本用法是這樣的:

int a;
typeof(a) b; //這等同于int b;
typeof(&a) c; //這等同于int* c;

那么在內核中這種特性是怎樣使用的呢?

/** Check at compile time that something is of a particular type.* Always evaluates to 1 so you may use it easily in comparisons.*/
#define typecheck(type,x) \
({  type __dummy; \typeof(x) __dummy2; \(void)(&__dummy == &__dummy2); \1; \
})/** Check at compile time that 'function' is a certain type, or is a pointer* to that type (needs to use typedef for the function type.)*/
#define typecheck_fn(type,function) \
({  typeof(type) __tmp = function; \(void)__tmp; \
})

這兩段代碼來自于 include/linux/typecheck.h,用于數據類型檢查。

宏 typecheck 用于檢查 x 是否是 type 類型,如果不是,那么編譯器會拋出一個 warning(warning: comparison of distinct pointer types lacks a cast); 而 typecheck_fn 則用于檢查函數 function 是否是 type 類型,不一致則拋出 warning(warning: initialization from incompatible pointer type)

原理很簡單,對于 typecheck ,只有當 x 的類型與 value 一致,&__dummy == &__dummy2 的比較才不會因為類型不匹配而拋出 warning ,詳情可以參考 C 語言對于指針操作的標準規定。對于 typecheck_fn ,當然也只有 function 的返回值和參數表與 type 描述一致,才不會因為類型不匹配而拋出 warning 。

到這里有人可能會有一個疑問,內核代碼里執行類型檢查會不會降低效率?答案是不會的,因為實際上,這些為類型檢查而聲明的臨時變量,實際上在上下文中都沒有使用,并且還特別地強制類型轉換為 void 防止任何由這些臨時變量產生的結果被使用的情況,因此在編譯器優化時,就將這些無用的代碼刪除了。

然后 kernel 中還定義了使用另一種類型檢查策略的獲取最大最小值的宏。

/** ..and if you can't take the strict* types, you can specify one yourself.** Or not use min/max/clamp at all, of course.*/
#define min_t(type, x, y) ({            \type __min1 = (x);          \type __min2 = (y);          \__min1 < __min2 ? __min1: __min2; })#define max_t(type, x, y) ({            \type __max1 = (x);          \type __max2 = (y);          \__max1 > __max2 ? __max1: __max2; })

這個例子里面不要求 x 和 y 是嚴格等于 type 類型,只要 x 和 y 能夠安全地完成隱式類型轉換為 type 就可以安全通過編譯,否則會拋出 warning。

另外一個非常經典的例子就是交換變量。

/** swap - swap value of @a and @b*/
#define swap(a, b) \do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)

試想如果沒有 typeof,要怎么在 C 語言中實現這種類似 C++ 模板的特性呢?

這里還有一個功能和 typeof 類似的運算符: typeid

typeid 是為 RTTI(運行時類型檢查) 提供的第二個運算符。

  • typeid 運算符允許在運行時確定對象的類型

  • 返回的結果是 const type_info&

  • typeid 運算符在應用于多態類類型的左值時執行運行時檢查,其中對象的實際類型不能由提供的靜態信息確定;

  • typeid 也可以在模板中使用以確定模板參數的類型

  • typeid 是操作符,不是函數,運行時獲知變量類型名稱;

和 typeof 的主要區別有二:

  • typeof(編譯器提供) 是一個編譯時結構,并返回編譯時定義的類型,和 C++11 提供的關鍵字 decltype 類似
  • typeid( C++ 提供) 是一個運行時結構,因此提供了有關該值的運行時類型的信息

typeid 表達式的形式是 typeid(e), 其中 e 可以是任意表達式或者類型的名字。 typeid 操作的結果是一個常量對象的引用,該對象的類型是標準庫類型 type_info 或者 type_info 的公有派生類型。如果表達式是一個引用,則 typeid 返回該引用所引對象的類型。不過當 typeid 作用于數組或函數時,并不會執行向指針的標準類型轉換。也就是說,如果我們對數組 a 執行 typeid(a) ,則所得的結果是數組類型而非指針類型。

當運算對象不屬于類類型或者是一個不包含任何虛函數的類時, typeid 運算符指示的運算對象的靜態類型。而當運算對象是定義了至少一個虛函數的類的左值時, typeid 的結果直到運行時才會求得。

當 typeid 作用于指針時(而非指針所指的對象),返回的結果是該指針的靜態編譯時類型

typeid 是否需要運行時檢查決定了表達式是否會被求值。只有當類型含有虛函數時,編譯器才會對表達式求值。反之,如果類型不含有虛函數,則 typeid 返回表達式的靜態類型;編譯器無須對表達式求值也能知道表達式的靜態類型。

RTTI 栗子:

class Base {friend bool operator==(const Base&, const Base&);
public:// Base 的接口成員
protected:virtual bool equal(const Base&) const;// Base 的數據成員和其他用于實現的成員
};class Derived : public Base {
public:// Derived 的其他接口成員
protected:bool equal(const Base&) const;// Derived 的數據成員和其他用于實現的成員
};// 類型敏感的相等運算符
// 接下來介紹我們是如何定義整體的相等運算符的:
bool operator==(const Base &lhs, const Base &rhs) {// 如果 typeid 不相同,返回 false;否則虛調用 equalreturn typeid(lhs) == typeid(rhs) && lhs.equal(rhs);
}

在這個運算符中,如果運算對象的類型不同則返回 false。否則,如果運算對象的類型相同,則運算符將其工作委托給虛函數 equal 。當運算對象是 Base 的對象時,調用 Base::equal ;當運算對象是 Derived 的對象時,調用 Derived::equal 。

虛 equal 函數

繼承體系中的每個類必須定義自己的 equal 函數。派生類的所有函數要做的第一件事都是相同的,那就是將實參的類型轉換為派生類類型:

bool Derived::equal(const Base &rhs) const {// 我們清楚這兩個類型是相等的,所以轉換過程不會拋出異常auto r = dynamic_cast<const Derived&>(rhs);// 執行比較兩個 Derived 對象的操作并返回結果
}

上面的類型轉換永遠不會失敗,因為畢竟我們只有在驗證了運算對象的類型相同之后才會調用該函數。然而這樣的類型轉換必不可少,執行了類型轉換后,當前的函數才能訪問右側運算對象的派生類成員

type_info 類

type_info 的操作

  • t1 == t2 如果 type_info 對象 t1 和 t2 表示同一種類型,返回 true, 否則返回 false

  • t1 != t2 與上一條相反

  • t.name() 返回一個 C 風格的字符串,表示類型名字的可打印形式。類型的名字生成方式因系統而異

  • t1.before(t2) 返回一個 boo 值,表示 t1 是否位于 t2 之前。 before 所采用的順序關系是依賴于編譯器的。

一般 type_info 是作為一個基類出現,所以應該提供一個公有的虛析構函數。當編譯器希望提供額外的類型信息時,通常在 type_info 的派生類中完成。

type_info 類沒有默認構造函數,而且他的拷貝和移動構造函數以及賦值運算符都被定義成刪除的。因此我們無法定義或拷貝 type_info 類型的對象,也不能為 type_info 類型的對象賦值。創建 type_info 對象的唯一途徑是使用 typeid 運算符

栗子:

int arr[10];
Derived d;
Base *p = &d;std::cout << typeid(42).id() << ", "<< typeid(arr).name() << ", "<< typeid(Sales_data).name() << ", "<< typeid(std::string).name() << ", "<< typeid(p).name() << ", "<< typeid(* p).name() << std::endl;

在作者的計算機上運行該程序,結果如下

i, A10_i, 10Sales_data, Ss, P4Base, 7Derived

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/448059.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/448059.shtml
英文地址,請注明出處:http://en.pswp.cn/news/448059.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

看臉色知體內各積毒 有效清潔內臟妙方

觀察下五臟六腑是否中毒。 淤血、痰濕、寒氣這些不能及時排出體外&#xff0c;危害健康和精氣神的物質&#xff0c;中醫稱之為毒素&#xff0c;在鏡子里你也可以看出它們。識別之后&#xff0c;你更需要有效的內臟清潔妙方! 癥狀一&#xff1a;面色青兩側長痘黃褐斑愁云滿面…

UTC Time

整個地球分為二十四時區&#xff0c;每個時區都有自己的本地時間。在國際無線電通信場合&#xff0c;為了統一起見&#xff0c;使用一個統一的時間&#xff0c;稱為通用協調時(UTC, Universal Time Coordinated)。UTC與格林尼治平均時(GMT, Greenwich Mean Time)一樣&#xff0…

解決:Unknown custom element: <myData> - did you register the component correctly? For recursive compon

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1. 引用一個組件報錯&#xff1a; Unknown custom element: <myData> - did you register the component correctly?For recursi…

無處不在的container_of

無處不在的container_of linux 內核中定義了一個非常精煉的雙向循環鏈表及它的相關操作。如下所示&#xff1a; struct list_head {struct list_head* next, * prev; };ubuntu 12.04 中這個結構定義在 /usr/src/linux-headers-3.2.0-24-generic/include/linux/types.h 中&…

程序員學習能力提升三要素

摘要&#xff1a;IT技術的發展日新月異&#xff0c;新技術層出不窮&#xff0c;具有良好的學習能力&#xff0c;能及時獲取新知識、隨時補充和豐富自己&#xff0c;已成為程序員職業發展的核心競爭力。本文中&#xff0c;作者結合多年的學習經驗總結出了提高程序員學習能力的三…

時間,數字 ,字符串之間的轉換

package com.JUtils.base;import java.sql.Timestamp; import java.text.SimpleDateFormat;/*** 轉換工具類<br>* 若待轉換值為null或者出現異常&#xff0c;則使用默認值**/ public class ConvertUtils {/*** 字符串轉換為int*** param str * 待轉換的字符串* param …

宏定義及相關用法

宏定義及相關用法 歡迎各位補充 目錄 一些成熟軟件中常用的宏定義&#xff1a;使用一些內置宏跟蹤調試&#xff1a;宏定義防止使用時錯誤&#xff1a;宏與函數 帶副作用的宏參數 特殊符號&#xff1a;’#’、’##’ 1、一般用法2、當宏參數是另一個宏的時候 __VA_ARGS__與##…

解決:Cannot read property ‘component‘ of undefined ( 即 vue-router 0.x 轉化為 2.x)

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 vue項目原本是用0.x版本的vue-router&#xff0c;但是去報出&#xff1a;Cannot read property component of undefined 這是因為版本問…

AMD Mantle再添新作,引發下代GPU架構猜想

摘要&#xff1a;今年秋天即將發布的《希德梅爾文明&#xff1a;太空》將全面支持AMD Mantle API&#xff0c;如此強大的功能背后離不開強大的CPU、GPU支持。上周AMD爆出了下一代海盜島R9 300系列&#xff0c;據網友猜測海盜島家族可能用上速度更快的HBM堆棧式內存。 小伙伴們…

不作35歲的程序員?

程序員三部曲--不作35歲的程序員?摩西2000 在中國&#xff0c;程序員不能超過35歲&#xff0c;似乎已經是不爭的事實&#xff0c;軟件開發工作就是青春飯&#xff0c;頂多靠畢業這十年的時間&#xff0c;超過這個年齡&#xff0c;要不成功躍身成為管理者&#xff0c;要不轉…

linux下使用TC模擬弱網絡環境

linux下使用TC模擬弱網絡環境 模擬延遲傳輸簡介 netem 與 tc: netem 是 Linux 2.6 及以上內核版本提供的一個網絡模擬功能模塊。該功能模塊可以用來在性能良好的局域網中,模擬出復雜的互聯網傳輸性能,諸如低帶寬、傳輸延遲、丟包等等情 況。使用 Linux 2.6 (或以上) 版本內核…

CDN 是什么 、CDN 引入

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 CDN 的全稱是 Content Delivery Network&#xff0c;即內容分發網絡。 CDN的基本原理是廣泛采用各種緩存服務器&#xff0c;將這些緩存…

長壽的人會有的8個健康理念

長壽的人會有的8個健康理念。年輕的時候總是在揮霍身體健康&#xff0c;吸煙、喝酒沒有節制&#xff0c;到老了之后身體會出現各種問題。老年人如果想要身體健康、長壽的話&#xff0c;就要從日常生活習慣做起。下面小編就來介紹長壽的人會有的8個健康理念&#xff1a; 1、少…

Ubuntu下selenium+Chrome的安裝使用

Ubuntu下seleniumChrome的安裝使用 安裝 chrome 官網下載安裝包 sudo dpkg -i google-chrome-stable_current_amd64.deb whereis google-chrome 安裝selenium pip3 install selenium 下載chromedriver(火狐使用geckodriver)驅動 http://npm.taobao.org/mirrors/chromed…

shoot for用法

Look, there are people like Ross who need to shoot for the stars, with his museum, and his papers getting published.---《老友記》 而像羅斯這種人則追求卓越&#xff0c;博物館&#xff0c;發表論文。 爭取;為...而努力Were shooting this year for a 50% increase in…

VUE : 雙重 for 循環寫法、table 解析任意 list 、萬能表格組件、解析一維數組、動態生成 table 所有數據

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1.需求&#xff1a; 我想要一個 table 組件能在實際調用時動態生成所有的 tr 、td 。 后端返回的只是一個 list &#xff0c; 前端頁…

安全離職妙招

高招的離職&#xff0c;不但有可能讓前老板幫你說好話&#xff0c;讓前同事成為你的啦啦隊&#xff0c;未來若有好機會&#xff0c;還會想到你&#xff0c;只要你學會克服離職流程中的五個尷尬情境。 情境一、離職怎么提&#xff1f; 口頭請辭&#xff0c;最先告知上司。 有…

字節內推~

大佬們有興趣來字節約飯么&#xff0c;下面是內推鏈接~ 社招內推鏈接&#xff1a;https://job.toutiao.com/s/LwpKWU8 校招內推鏈接&#xff1a;https://job.toutiao.com/s/LwsFw6g

使用編輯工具快速創建實體對象的方法

快速創建java類 (\w)\s(.) /** $2 */\nprivate String $1; search Mode 為 Reqular expression 轉載于:https://www.cnblogs.com/otways/p/11283303.html

超詳細 圖解 : IntelliJ IDEA 逆向生成 JAVA 實體類

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 1.配置數據庫,&#xff0c;這里連接的是mysql。 2.填寫 連接數據庫的信息&#xff0c;填寫完成后可以點擊Test Connection,測試一下是否…