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