linux tricks 之 typeof用法.
typeof是gcc的扩展功能,比较简单,是用来取得参数类型,具体可参考gcc官网的解释.
https://gcc.gnu.org/onlinedocs/gcc/Typeof.html
由于Linux代码采用Gcc编译器编译,所以它可以采用Gcc对C语言的扩展特性,以实现高效的代码。其中运用非常广泛的扩展就是复合语句。Gcc把包含在圆括号和大括号双层括号内的复合语句看作是一个表达式,它可以出现在任何允许表达式的地方,而复合语句中可以声明局部变量,以及循环条件判断等复杂处理。而表达式的最后一条语句必须是一个表达式,它的计算结果作为返回值。
int a = ({typeof(a) _a = ; ++_a;});
上例中符合表达式中声明了局部变量_a,而返回值为++_a的结果,所以a的值为1。基于这种扩展,内核可以通过在复合语句中定义局部变量而表面自加自减运算符的副作用问题。内核中的min_t和max_t宏就是这样实现的。
include/linux/kernel.h
#define min_t(type, x, y) ({ \
type __min1 = (x); \
type __min2 = (y); \
__min1 < __min2 ? __min1: __min2; }) #define max_t(type, x, y) ({ \
type __max1 = (x); \
type __max2 = (y); \
__max1 > __max2 ? __max1: __max2; })
尽管多数时候通过使用这类宏,可以避免参数的副作用,但是这会增加内存的开销和执行效率,所以如果能够保证参数不存在副作用,那么使用通常的如下定义即可:
#define min(a, b) ((a) > (b)? (b) : (a))
#define max(a, b) ((a) > (b)? (a) : (b))
以上的min_t和max_t宏适需要提供数据类型,typeof的出现使这一步也可被省略。
include/linux/kernel.h
#define min(x, y) ({ \
typeof(x) _min1 = (x); \
typeof(y) _min2 = (y); \
(void) (&_min1 == &_min2); \
_min1 < _min2 ? _min1 : _min2; }) #define max(x, y) ({ \
typeof(x) _max1 = (x); \
typeof(y) _max2 = (y); \
(void) (&_max1 == &_max2); \
_max1 > _max2 ? _max1 : _max2; })
观察min和max的实现,它们通过typeof获取x和y的类型,然后定义局部变量以消除参数副作用。
思考问题:注意到中间的比较运算,
(void) (&_min1 == &_min2);
(void) (&_max1 == &_max2);
这两行代码是用来做类型检查的,如果x和y的类型不同,那么编译器将提示如下警告信息,这对检查代码很有帮助。
xxx.c:: warning: comparison of distinct pointer types lacks a cast
那么,类型检查是如何生效的呢?在stackoverflow上找到解释如下:
This provides for type checking, equality between pointers shall be between compatible types and gcc will provide a warning for cases where this is not so.
We can see that equality between pointers requires that the pointers be of compatible types from the draft C99 standard section 6.5.9 Equality operators which says:
One of the following shall hold:
and includes:
both operands are pointers to qualified or unqualified versions of compatible types;
and we can find what a compatible type is from section 6.2.7 Compatible type and composite type which says:
Two types have compatible type if their types are the same
This discussion on osnews also covers this and it was inspired by the the GCC hacks in the Linux kernel article which has the same code sample. The answer says:
has to do with typechecking.
Making a simple program:
int x = 10;
long y = 20;
long r = min(x, y);Gives the following warning: warning: comparison of distinct pointer types lacks a cast
stackoverflow对此问题的讨论已经很清楚,简单的来说,c99标准约定,对于比较运算符 == 和 != 来说,如果比较的对象是指针,那么指针所指的对象类型必须一致,否则抛出上面的warning信息.
参考资料:http://stackoverflow.com/questions/5595593/min-macro-in-kernel-h
linux tricks 之 typeof用法.的更多相关文章
- Linux中find常见用法
Linux中find常见用法示例 ·find path -option [ -print ] [ -exec -ok command ] {} \; find命令的参数 ...
- Linux关于watch的用法
Linux关于watch的用法 2011-07-20 0个评论 收藏 我要投稿 watch 是一个非常实用的命令,基本所有的 Linux 发行版都带有这个小工具,如同名字一 ...
- [转]Linux中find常见用法示例
Linux中find常见用法示例[转]·find path -option [ -print ] [ -exec -ok command ] {} \;find命令的参 ...
- Linux下find命令用法详解
Linux下find命令用法详解 学神VIP烟火 学神IT教育:XueGod-IT 最负责任的线上直播教育平台 本文作者为VIP学员 烟火 第一部分:根据文件名查找 1.在当前目录 ...
- linux中make的用法
一.linux中make的用法 目的: 基本掌握了make 的用法,能在Linux系统上编程.环境: Linux系统准备: 准备三个文件:file1.c, file ...
- Linux中“!"的神奇用法
前言 实际上,不起眼的“!”在linux中有着很多让你惊叹的妙用.本文就来细数那些“!”的神奇用法. 执行上一条命令 例如,在执行完上面一条命令后,可以使用下面的方式再次执行上一条命令: $ wher ...
- Linux中sed的用法实践
Linux中sed的用法实践 参考资料:https://www.cnblogs.com/emanlee/archive/2013/09/07/3307642.html http://www.fn139 ...
- Linux中Sed的用法
Linux中Sed的用法 sed是一个很好的文件处理工具,本身是一个管道命令,主要是以行为单位进行处理,可以将数据行进行替换.删除.新增.选取等特定工作,下面先了解一下sed的用法sed命令行格式为: ...
- Linux tar命令高级用法——备份数据
Linux tar命令高级用法——备份数据 2015-12-31 Linux学习 Linux上有功能强大的tar命令,tar最初是为了制作磁带备份(tape archive)而设计的,它的作用是把文件 ...
随机推荐
- route工具
route工具 route工具主要用来查看或修改内核路由表. 1.查看内核路由表 route [-nee] 参数说明: -n:不要使用协议或主机名称,直接使用 IP 或 port number:-ee ...
- web图片使用
1. jpg.png.gif 适用场景 jpg 色彩丰富.大的图片例如 写实的图像,商品图片,人像,实物素材的广告banner等 png 色彩较少,有透明,或 具备较大亮度差异及强烈对比的图像,例如 ...
- Mongodb c#增删改查
写在前面 最近项目需要,就研究了下mongodb,也是为了快速上手,就自己弄了一个简单的例子,这里记录一下. Mongodb 传统的关系数据库一般由数据库(database).表(table).记录( ...
- php 开启curl,重启php-fpm服务
1,找到php.ini配置 find / -name 'php.ini' /usr/local/php/etc/php.ini 找到extension=php_curl.dll 把前面的分号去掉即可. ...
- mysql更新一个表里的字段等于另一个表某字段的值
update a left join c on a.id = c.id set a.body = c.c1 where a.id=c.id; update zcat as z left join zc ...
- 最长不下降子序列(LIS)
最长上升子序列.最长不下降子序列,解法差不多,就一点等于不等于的差别,我这里说最长不下降子序列的. 有两种解法. 一种是DP,很容易想到,就这样: REP(i,n) { f[i]=; FOR(j,,i ...
- ReactJS入门学习一
ReactJS入门学习一 阅读目录 React是什么? React如何制作组件? 理解组件属性props 理解页面中如何渲染数据的 理解从服务器端获取数据及理解state的 回到顶部 React是什么 ...
- 转>>在同一个sql语句中如何写不同条件的count数量
今天在做Portal中的Dashboard展现的时候,需要对多个统计字段做展现,根据我现在的掌握水平,我只能在sql调用构建器中实现一种sql语 句返回的resultSet做展现.没有办法,只能从数据 ...
- Centos优化Hadoop
导读 Hadoop是一个能够让用户轻松架构和使用的分布式计算平台,用户可以轻松地在Hadoop上开发和运行处理海量数据的应用程序,本节讲安装并且优化centos 6.7 系统下的Supper Hado ...
- 将服务器上的myql数据库导入本地数据库
文章是从我的个人博客上粘贴过来的, 大家也可以访问 www.iwangzheng.com 首先登录到服务器上,进入文件夹,我们这边的是m-cms $mysqldump -uroot mos > ...