详解 GNU C 标准中的 typeof 关键字
如果你是 C++ 程序員,應(yīng)該接觸過 C++11 里的 decltype 操作符,它的作用是自動推導(dǎo)表達(dá)式的數(shù)據(jù)類型,以解決泛型編程中有些類型由模板參數(shù)決定而難以(甚至不可能)表示的問題。其實(shí)這個特性在 C 語言中也早有類似的實(shí)現(xiàn),GNU C 標(biāo)準(zhǔn)中的一個擴(kuò)展特性 typeof (PS: 不是 typedef)作用與 decltype 類似,我們來看看這個關(guān)鍵字該怎么用。
先來看一個最簡單的例子:
// demo 01
int var = 666;
typeof(int *) pvar = &var;
printf("pvar:\t%p\n", pvar);
printf("&var:\t%p\n", &var);
printf("var:\t%d\n", var);
printf("*pvar:\t%d\n", *pvar);
我們先定義了一個 int 型變量 var,然后再定義一個指針型變量指向 var,一般我們就直接 int *xxx = &xx,但是我們?yōu)榱搜菔?typeof 的用法,就不要這么直接了,typeof 是自動推導(dǎo)后面 ( ) 里的數(shù)據(jù)類型,所以 typeof(int *) 直接推導(dǎo)出了 int * 型,用這個類型聲明了 pvar 并將其初始化為 var 的地址,輸出結(jié)果應(yīng)該就顯而易見了,這是在我的機(jī)器上的輸出:
好吧我承認(rèn)上面那個例子是吃力不討好,明明寫個 int * 簡單又明了,非得加個 typeof 搞得這么晦澀,其實(shí) typeof 的功效在于其能夠自動推導(dǎo)表達(dá)式類型,比如我們把剛才的?typeof(int *) 改成 typeof(&var),它也會自動推導(dǎo)出 &var 的類型 —— int * 型,你可以自己試一下,原理是一樣的,這樣的話,當(dāng)遇到一個非常復(fù)雜的表達(dá)式我們很難推斷其類型的時候,typeof 就很有用了。另外有一點(diǎn)要注意:typeof 是 GNU C 標(biāo)準(zhǔn)里特有的擴(kuò)展,標(biāo)準(zhǔn)的 ISO C 并沒有這個關(guān)鍵字,所以在編譯的時候不能加任何 ISO 的 C 標(biāo)準(zhǔn)選項,否則會報錯,比如編譯上面的代碼我加入了一個 -std=c90 的選項,編譯器就會有提示一堆 error:
解決的方法很簡單,把 -std=c90 改成 -std=gnu90 即 GNU 的標(biāo)準(zhǔn)即可。
再來幾個例子,比如
// demo 02
int *pvar = NULL;
typeof(*pvar) var = 999;
printf("var:\t%d\n", var);
這個例子是先定義了一個整型指針變量 pvar,typeof 后面括號里的表達(dá)式為*pvar,pvar 的類型為 int * 型,那 *pvar 當(dāng)然就被解析為 int 型,所以用這個類型聲明的變量 var 也是 int 型,就相當(dāng)于 int var = 999; 輸出結(jié)果如下:
再來:
// demo 03
int *pvar = NULL;
typeof(*pvar) var[4] = {11, 22, 33, 44};
for (int i = 0; i < 4; i++)
?? ?printf("var[%d]:\t%d\n", i, var[i]);
這次 typeof 解析出來的類型跟上一個一樣,區(qū)別是 var 是一個包含四個元素的數(shù)組,相當(dāng)于 int var[4] = {...}; 輸出如下:
這次來個有點(diǎn)水平的:
// demo 04
typeof(typeof(const char *)[4]) pchar = {"hello", "world", "good", "night"};
for (int i = 0; i < 4; i++)
?? ?printf("pchar[%d]:\t%s\n", i, pchar[i]);
這次看起來就比較復(fù)雜了,考驗?zāi)阒羔樄Φ椎臅r候到了,他嵌套了兩層 typeof,我們一層一層的往外剝,先看最里層,typeof 先解析出一個 const char * 類型,有經(jīng)驗的 C 程序員應(yīng)該馬上就能聯(lián)想到字符串了吧,而后面又跟著一個 [4],說明這是一個包含四個字符串的數(shù)組類型,那么這個類型也就被最外層的 typeof 給解析到了,那么最終的 pchar 也就是這個類型了,相當(dāng)于 const char *pchar[4] = {...};?
再來考驗一下你的指針,這次是函數(shù)指針:
// demo 05
int add(int param1, int param2) {
?? ?return param1 + param2;
}
?
int sub(int param1, int param2) {
?? ?return param1 - param2;
}
?
int mul(int param1, int param2) {
?? ?return param1 * param2;
}
?
int main() {
?? ?int (*func[3]) (int, int) = {add, sub, mul};
?? ?typeof(func[0](1, 1)) sum = 100;
?? ?typeof(func[1](1, 1)) dif = 101;
?? ?typeof(func[2](1, 1)) pro = 102;
?
?? ?printf("sum:\t%d\n", sum);
?? ?printf("dif:\t%d\n", dif);
?? ?printf("pro:\t%d\n", pro);
?? ?return 0;
}
這個 demo 中先定義了三個函數(shù),這三個函數(shù)都是返回值為 int 類型,并且接受兩個 int 型的參數(shù),然后在 main 函數(shù)中定義了一個函數(shù)指針數(shù)組 func,并用上面三個函數(shù)名將其初始化,然后我們來看底下的第一個 typeof ?會推導(dǎo)出什么類型,func[0] 就是指 add 這個函數(shù),后面的括號里跟了兩個參數(shù),說白了就是簡單的 add(1, 1) 的調(diào)用,而 add 會返回一個 int 型值,所以最終推導(dǎo)出的類型就是 int 型,其它兩個都是同理,所以 sum、dif、pro 其實(shí)就是三個整型數(shù),相當(dāng)于 int sum = 100; int dif = 101; int pro = 102; 好吧,我承認(rèn)這個 demo 有點(diǎn)坑,而且這個例子舉得不恰當(dāng),輸出結(jié)果就是它們分別的值:
我們再看看它在宏定義中的應(yīng)用:
// demo 06
#define pointer(T) ?typeof(T *)
#define array(T, N) typeof(T[N])
?
int main() {
?? ?array(pointer(char), 4) pchar = {"hello", "world", "good", "night"};
?? ?for (int i = 0; i < 4; i++)
?? ??? ?printf("pchar[%d]:\t%s\n", i, pchar[i]);
?? ?return 0;
}
這里用到了宏函數(shù),pointer(T) 會被替換為 typeof(T *),也就是說 pointer 后面跟某個類型的名字,經(jīng)過預(yù)處理之后就會變成用 typeof 解析相應(yīng)類型的指針類型,而 array 后面跟一個類型名和一個整數(shù),然后 typeof 就會解析為該類型的一個數(shù)組,這樣 main 函數(shù)中的?array(pointer(char), 4),pointer(char) 首先會被解析為 char * 型,然后外層的 array 會再被解析為包含 4 個 char * 元素的數(shù)組類型,所以就相當(dāng)于 char *pchar[4] = {...}; 輸出結(jié)果如下:
好了,啰嗦了這么多,typeof 這個關(guān)鍵字總算是知道用來干什么了吧,感覺好像語法挺晦澀的,而且沒有什么實(shí)際用途,那好吧,我再讓大伙看一看實(shí)際項目中的一個例子:
/*
?* 選自 linux-2.6.7 內(nèi)核源碼
?* filename: linux-2.6.7/include/linux/kernel.h
?*/
#define min(x,y) ({ \
?? ?typeof(x) _x = (x);?? ?\
?? ?typeof(y) _y = (y);?? ?\
?? ?(void) (&_x == &_y);?? ??? ?\
?? ?_x < _y ? _x : _y; })
上面這個例子是選自 linux 2.6.7 內(nèi)核中?include/linux/kernel.h 這個頭文件,宏定義 min 的作用是從兩個相同類型的對象中選取一個最小的,它接受兩個參數(shù) x 和 y,后面的宏替換部分就用 typeof 定義兩個變量 _x 和 _y,并分別賦值為 x y,這里用 typeof 的作用就是可以讓 min 接受任何類型的參數(shù)而不必局限于某一個單一類型,這有點(diǎn)泛型編程的味道了,最后一個語句?_x < _y ? _x : _y; 用了一個條件運(yùn)算符來返回二者之中最小的,中間還有一句?(void) (&_x == &_y); 看起來好像是廢話,其實(shí)這句話是有特殊用意的,因為我們不能保證你在使用 min 的時候傳入的兩個參數(shù)都是相同的類型,這時候就需要做一個檢測,而 C 語言不支持直接 typeof(_x) == typeof(_y) 這樣的操作,所以就取其地址,用指針類型來比較,如果兩個指針的類型不一致,編譯器就會產(chǎn)生警告以達(dá)到檢測的效果,至于前面的 (void),是因為僅表達(dá)式 &_x == &_y 本身是沒有意義的,如果沒有這個 (void) 編譯器同樣會警告:statement with no effect [-Wunused-value],無效的語句,如果不想看到這個警告,那就在前面加個?(void) 忽略掉。
參考資料:
GNU 官方手冊:https://gcc.gnu.org/onlinedocs/gcc/Typeof.html
crifan 的個人網(wǎng)站:http://www.crifan.com/order_min__macro_definition_void_amp__x__amp__y_the_meaning_of/
-
總結(jié)
以上是生活随笔為你收集整理的详解 GNU C 标准中的 typeof 关键字的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2021年广西艺术高考成绩查询,2021
- 下一篇: C89和C99区别