如果你是 C++ 程序员,应该接触过 C++11 里的 decltype 操作符,它的作用是自动推导表达式的数据类型,以解决泛型编程中有些类型由模板参数决定而难以(甚至不可能)表示的问题。其实这个特性在 C 语言中也早有类似的实现,GNU C 标准中的一个扩展特性 typeof (PS: 不是 typedef)作用与 decltype 类似,我们来看看这个关键字该怎么用。

先来看一个最简单的例子:

// demo 01
int var = ;
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 = ;
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[] = {, , , };
for (int i = ; i < ; i++)
printf("var[%d]:\t%d\n", i, var[i]);

这次 typeof 解析出来的类型跟上一个一样,区别是 var 是一个包含四个元素的数组,相当于 int var[4] = {...}; 输出如下:

这次来个有点水平的:

// demo 04
typeof(typeof(const char *)[]) pchar = {"hello", "world", "good", "night"};
for (int i = ; i < ; 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), ) pchar = {"hello", "world", "good", "night"};
for (int i = ; i < ; i++)
printf("pchar[%d]:\t%s\n", i, pchar[i]);
return ;
}

这里用到了宏函数,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) 忽略掉。

原文博客:http://blog.csdn.net/zhanshen2015/article/details/51495273#

GNU中的关键字typeof的更多相关文章

  1. linux内核宏container_of前期准备之gcc扩展关键字typeof

    typeof基本介绍 typeof(x) 这是它的使用方法,x可以是数据类型或者表达式.它的作用时期和sizeof类似,就是它是在编译器从高级语言(如C语言)翻译成汇编语言时起作用,这个很重要,稍后会 ...

  2. js中this关键字测试集锦

    参考:阮一峰<javascript的this用法>及<JS中this关键字详解> this是Javascript语言的一个关键字它代表函数运行时,自动生成的一个内部对象,只能在 ...

  3. 【C#学习笔记之一】C#中的关键字

    C#中的关键字 关键字是对编译器具有特殊意义的预定义保留标识符.它们不能在程序中用作标识符,除非它们有一个 @ 前缀.例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字. 下面是列出的 ...

  4. 如何给wp(Windows phone)中搜索关键字加亮?

    问题来源 最近在群里看到群友讨论在wp中有个搜索功能,要求搜索关键字在搜索结果内容中加亮(即加颜色),由于wp中没有自带这样的控件,于是大家各抒自见,有人说用第三方控件,有人说用richtextbox ...

  5. c#多线程中Lock()关键字的用法小结

    本篇文章主要是对c#多线程中Lock()关键字的用法进行了详细的总结介绍,需要的朋友可以过来参考下,希望对大家有所帮助     本文介绍C# lock关键字,C#提供了一个关键字lock,它可以把一段 ...

  6. JS中的关键字和保留字

    JavaScript中不能作为变量名的关键字和保留字总结: 1.js中的关键字: break case catch continue default delete do else finally fo ...

  7. Java中的关键字 transient

    先解释下Java中的对象序列化 在讨论transient之前,有必要先搞清楚Java中序列化的含义: Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息 ...

  8. 【转载】C/C++中extern关键字详解

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...

  9. 【转】java中volatile关键字的含义

    java中volatile关键字的含义   在java线程并发处理中,有一个关键字volatile的使用目前存在很大的混淆,以为使用这个关键字,在进行多线程并发处理的时候就可以万事大吉. Java语言 ...

随机推荐

  1. 解析和操作XML文件

    Dom4j工具 使用步骤: 1)导入dom4j的核心包. dom4j-1.6.1.jar 2)编写Dom4j读取xml文件代码 1,Domj4读取xml文件 ,准备工作:读取整个文档并获取根节点 // ...

  2. Asp.NET MVC+WebAPI跨域调用

    使用jQuery调用WebApi有时会遇到跨域的问题,今天介绍一种可以简单解决跨域问题的方法. 当我们跨域请求WebAPI的时候会提示以下信息: XMLHttpRequest cannot load ...

  3. 记DotNetBar换肤

    界面: comboBoxEx  选择皮肤 buttonX 测试指定皮肤 styleManager 后台代码: 初始化 : this.EnableGlass = false; 设置窗体效果 不设置 依然 ...

  4. C 碎片十一 扫雷源码

    // C语言版本扫雷 #include <stdio.h> #include <stdlib.h> /* 1(0,0) 1(0,1) 0(0,2) 1(0,3) 1(0,4) ...

  5. angular 学习笔记(1) 使用angular完整写法

    视图部分: <div ng-app="myapp"> <div ng-controller="myctrl"> <p>{{n ...

  6. Regexp:常用的几个正则表达式

    1.isEmail /** * * @desc 判断是否为邮箱地址 * @param {String} str * @return {Boolean} */ function isEmail(str) ...

  7. jQuery事件绑定函数:on()与bind()的差别

    jQuery从1.7+版本开始,提供了on()和off()进行事件处理函数的绑定和取消.on()和bind()这两个方法有相同的地方也有不同的地方. bind(type,[data],fn); on( ...

  8. Java interface和abstract小记

    一.abstract 用abstract修饰的类叫做抽象类,用abstract修饰的方法叫抽象方法. 含有抽象方法的类必须被声明为抽象类,抽象类必须被继承,抽象方法必须被重写. 抽象类不能被实例化. ...

  9. [Asp.Net] web api 部署注意事项

    在将web api项目部署到IIS上的时候 要将应用程序池设置成.net framework 4.0版本

  10. C基础的练习集及测试答案(提高题)

    提高题:1.编写程序,随机生成一个1~10内的数,让对方猜3次.如果3次内能猜中则输出“恭喜你”:若3次内猜不中则输出正确答案.C语言中提供生成随机数的函数rand()用法:①所需头文件:#inclu ...