typeof關(guān)鍵字

ANSI C定義了sizeof關(guān)鍵字,用來獲取一個變量和數(shù)據(jù)類型在內(nèi)存中所占的存儲字節(jié)數(shù)。GNU 擴展了一個關(guān)鍵字,typeof用來獲取一個變量或表達式的類型。

int i;
typeof(i) j = 20;
typeof(int *) a;
int f();
typeof(f()) k;

在上面的代碼中,因為變量i的類型為int,所以typeof(i)就等于int,typeof(i) j = 20就相當(dāng)于int j = 20;typeof(int *) a;就相當(dāng)于int * a;函數(shù)也是有類型的,函數(shù)的類型就是其返回值類型,所以typeof(f()) k;就相當(dāng)于int k;。

typeof( typeof(int *)[5] ) a; //相當(dāng)于int * a[5];
typeof( int x[5]) y; //相當(dāng)于int y[5];

完美MAX(a,b)宏的誕生

在之前黑鳥的文章中,有提到過如何定義一個適用不同類型且無副作用的MAX宏。今天就帶大家復(fù)習(xí)一下:

青銅級別:

#define MAX(x,y) ( (x) > (y)?(x):(y) )

一般我們在教科書上看到的簡單的MAX宏定義如上。但他有兩個致命缺陷,一是兩個參數(shù)必須類型一致,二是參數(shù)不能帶副作用,即自增或自減。原因大家自己思考,當(dāng)然也可以查看我之前的文章。

白銀級別:

#define MAX(type,x,y) ({ \
    type _x = x; \
    type _y = y; \
    _x > _y ? _x : _y; \
})

該方法通過添加一個type參數(shù)解決了不同類型變量比較的問題;通過定義臨時變量_x和_y成功解決了參數(shù)的副作用問題。但他還不是最好的。

黃金級別:

#define MAX(x,y) ({ \
    typeof(x) _x = x; \
    typeof(y) _y = y; \
    _x > _y ? _x : _y; \
})

通過typeof直接獲取宏的參數(shù)類型,這樣我們就不必再單獨將參數(shù)的類型傳給宏了。到這里他已經(jīng)基本可以在江湖中有自己一席之地了,但離號令天下還有一步之遙。

磚石級別:

#define MAX(x,y) ({\
    typeof(x) _x = x; \
    typeof(y) _y = y; \
    (void) ( &_x == &_y ); \
    _x>_y ? _x : _y;\
})

該宏定義之所以稱之為鉆石級別,是因為他多了一句(void) ( &_x == &_y ); 該語句看起來貌似一句廢話,但其實用的很巧妙。它主要是用來檢測宏的兩個參數(shù)的數(shù)據(jù)類型是否相同。如果不相同,編譯器會給一個警告信息提醒開發(fā)人員。

waring:comparison of distinct pointer types lacks a cast
  • 讓我們分析一下他是如何實現(xiàn)判斷他的兩個參數(shù)的數(shù)據(jù)類型是否一致。

從字面意思來看,他用來判斷兩個變量的地址是否相等。可能有人會說,兩個變量的地址怎么可能相等呢?但妙就妙在這個地方!當(dāng)該語句還未執(zhí)行到判斷兩個變量的地址是否相等的時候,編譯器首先要檢查兩個變量的數(shù)據(jù)類型是否相同。如果兩個變量的數(shù)據(jù)類型不相同的話,那么編譯器會有警告信息。當(dāng)然我們也就可以從中獲益。而如果兩個變量的數(shù)據(jù)類型相等的話,那么該語句在整個宏中也不起任何作用,同樣我們也沒任何損失,笑問這種無本生意為何不做呢?

所謂是驢子是馬拉出來溜溜!上面我們講了這么多,但sizeof關(guān)鍵字到底有什么作用呢?下面通過解剖內(nèi)核宏container_of來為大家展示sizeof關(guān)鍵字的強大作用和內(nèi)核設(shè)計者的巨大腦洞。

解剖內(nèi)核第一宏container_of

首先讓我們來膜拜一下天下第一宏的風(fēng)采:

#define offsetof( TYPE, MEMBER ) ( (size_t)&((TYPE *)0)->MEMBER )
#define container_of(ptr, type,member) ({ \
    const typeof( ((type *)0)->member ) _mptr = (ptr); \
    (type  *)( (char *)_mptr - offsetof(type, member) );
})

它的主要作用就是:根據(jù)結(jié)構(gòu)體某一成員的地址,獲取這個結(jié)構(gòu)體的首地址。根據(jù)宏定義我們可以看到,這個宏有三個參數(shù),他們分別是:

  • type:結(jié)構(gòu)體類型
  • member :結(jié)構(gòu)體內(nèi)的成員
  • ptr: 結(jié)構(gòu)體內(nèi)成員member的地址

也就是說,只要我們知道了一個結(jié)構(gòu)體的類型,結(jié)構(gòu)體內(nèi)某一成員的地址,就可以直接獲得這個結(jié)構(gòu)體的首地址。這個宏返回的就是這個結(jié)構(gòu)體的首地址。

container_of宏實現(xiàn)分析

作為一名Linux內(nèi)核驅(qū)動開發(fā)者,除了要面對各種手冊、底層寄存器,有時候還要應(yīng)付底層造輪子的事情。為了系統(tǒng)的穩(wěn)定和性能,有時候我們不得不深入底層,死磕某個模塊進行分析和優(yōu)化。底層的工作雖然很有挑戰(zhàn)性,但有時候也是很枯燥的。不像應(yīng)用開發(fā)那樣有意思,所以為了提高對工作的興趣,大家表面上雖然不說自己牛叉,但內(nèi)心深處一定要建立起自己的職位優(yōu)越感。人不可有傲氣,但不能無傲骨。我們可不像開發(fā),知道api接口、讀讀文檔、完成功能就ok了。作為一名底層開發(fā)者要時刻記住:要和寄存器、內(nèi)存、硬件電路等各種底層群眾打成一片。從群眾中來,到群眾中去,急群眾之所急,想群眾之所想。這樣才能構(gòu)建一個穩(wěn)定和諧的嵌入式社會。

  • 首先來看offsetof宏
#define offsetof( TYPE, MEMBER ) ( (size_t)&((TYPE *)0)->MEMBER )

這個宏有兩個參數(shù),一個是結(jié)構(gòu)體類型TYPE,一個是結(jié)構(gòu)體的成員MEMBER。它使用的技巧就是:將0強制轉(zhuǎn)換為一個指向TYPE類型的結(jié)構(gòu)體常量指針。因為常量指針為0,即可以看做結(jié)構(gòu)體首地址為0,所以結(jié)構(gòu)體每個成員變量的地址即為該成員相對于結(jié)構(gòu)體首地址的偏移。最后通過強制類型轉(zhuǎn)換size_t,將成員地址值轉(zhuǎn)換為整數(shù),取得其在整個結(jié)構(gòu)體中的偏移。

  • 再來看container_of宏
 const typeof( ((type *)0)->member ) _mptr = (ptr); 

因為結(jié)構(gòu)體的成員數(shù)據(jù)類型可以是任意的數(shù)據(jù)類型,所以為了讓這個宏兼容各種數(shù)據(jù)類型,我們定義了一個臨時指針變量_mptr,該變量用來存儲結(jié)構(gòu)體成員member的地址ptr。那如何獲取ptr指針類型的呢?就是通過上面的宏語句。

(type*)((char*)_mptr-offsetof(type,member));

最后通過該成員變量地址_mptr減去該成員在結(jié)構(gòu)體中的偏移,就得到了結(jié)構(gòu)體的首地址。當(dāng)然因為返回的是結(jié)構(gòu)體的首地址所以數(shù)據(jù)類型還必須強制轉(zhuǎn)換一下。

小思

好了到這里我們對天下第一宏的分析也就接近尾聲了。那么可以看出任何一個復(fù)雜的東西,我們都可以把它分解。運用所學(xué)的基礎(chǔ)知識一層一層,一點以一點去剖析,先去降維分析,然后再進行綜合。那么這種能力就是你的核心競爭力,也是你超越其他工程師脫穎而出的機會!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ANSI C定義了sizeof關(guān)鍵字,用來獲取一個變量和數(shù)據(jù)類型在內(nèi)存中所占的存儲字節(jié)數(shù)。GNU 擴展了一個關(guān)鍵字...
    Leon_Geo閱讀 1,091評論 2 7
  • ANSI C定義了sizeof關(guān)鍵字,用來獲取一個變量和數(shù)據(jù)類型在內(nèi)存中所占的存儲字節(jié)數(shù)。GNU 擴展了一個關(guān)鍵字...
    Leon_Geo閱讀 907評論 3 5
  • typeof關(guān)鍵字是C語言中的一個新擴展。 typeof的參數(shù)可以是兩種形式:表達式或類型。 下面是使用表達式的的...
    shalapovar閱讀 9,013評論 0 1
  • 注:這是第三遍讀《C語言深度解剖》,想想好像自從大學(xué)開始就沒讀完過幾本書,其中譚浩強的那本《C語言程序設(shè)計(第四版...
    HavenXie閱讀 1,918評論 1 6
  • 內(nèi)心強大比什么都重要,你要照顧好自己;承認(rèn)自己的平凡,但是努力向好的方向發(fā)展;可以平靜面對生活,安然的聽從自己內(nèi)心...
    生活點滴_5224閱讀 173評論 0 1

友情鏈接更多精彩內(nèi)容