天天看點

typeof關鍵字在核心中的運用

ANSI C定義了sizeof關鍵字,用來擷取一個變量和資料類型在記憶體中所占的存儲位元組數。GNU 擴充了一個關鍵字,typeof用來擷取一個變量或表達式的類型。

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

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

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

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

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

青銅級别:

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

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

白銀級别:

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

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

黃金級别:

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

通過typeof直接擷取宏的參數類型,這樣我們就不必再單獨将參數的類型傳給宏了。到這裡他已經基本可以在江湖中有自己一席之地了,但離号令天下還有一步之遙。

磚石級别:

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

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

waring:comparison of distinct pointer types lacks a cast      
  • 讓我們分析一下他是如何實作判斷他的兩個參數的資料類型是否一緻。

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

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

解剖核心第一宏container_of

首先讓我們來膜拜一下天下第一宏的風采:

#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) );
})      

它的主要作用就是:根據結構體某一成員的位址,擷取這個結構體的首位址。根據宏定義我們可以看到,這個宏有三個參數,他們分别是:

  • type:結構體類型
  • member :結構體内的成員
  • ptr: 結構體内成員member的位址

也就是說,隻要我們知道了一個結構體的類型,結構體内某一成員的位址,就可以直接獲得這個結構體的首位址。這個宏傳回的就是這個結構體的首位址。

container_of宏實作分析

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

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

這個宏有兩個參數,一個是結構體類型TYPE,一個是結構體的成員MEMBER。它使用的技巧就是:将0強制轉換為一個指向TYPE類型的結構體常量指針。因為常量指針為0,即可以看做結構體首位址為0,是以結構體每個成員變量的位址即為該成員相對于結構體首位址的偏移。最後通過強制類型轉換size_t,将成員位址值轉換為整數,取得其在整個結構體中的偏移。

  • 再來看container_of宏
consttypeof(((type*)0)->member)_mptr=(ptr);      
(type*)((char*)_mptr-offsetof(type,member));      

小思