详解 GNU C 标准中的 typeof 关键字。
这是我转载的一篇关于GNC C的typeof的使用,讲解的还算可以,有图有文字。
http://blog.sina.com.cn/s/blog_149e9d2ec0102wxrm.html
转载:http://blog.csdn.net/zhanshen2015/article/details/51495273
如果你是 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]);
这次来个有点水平的:
- // 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这个关键字总算是知道用来干什么了吧,感觉好像语法挺晦涩的,而且没有什么实际用途,那好吧,我再让大伙看一看实际项目中的一个例子:
- #define min(x,y) ({ \
- typeof(x) _x = (x); \
- typeof(y) _y = (y); \
- (void) (&_x == &_y); \
- _x < _y ? _x : _y; })
参考资料:
GNU 官方手册:https://gcc.gnu.org/onlinedocs/gcc/Typeof.html