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)而设计的,它的作用是把文件 ...
随机推荐
- 使用PPA在Ubuntu上安装php5.4~5.6,7
使用PPA在Ubuntu上安装php5.4~5.6,7 sudo apt-get install software-properties-common sudo add-apt-repository ...
- Linux 下模拟Http 的get or post请求(curl和wget两种方法)
一.get请求: 1.使用curl命令: curl "http://www.baidu.com" 如果这里的URL指向的是一个文件或者一幅图都可以直接下载到本地curl -i & ...
- 转载大神的检测网站重定向的python脚本
#!/usr/bin/env python #coding=utf8 import sys import requests def check_for_redirects(url): try: r = ...
- SQL2005删除复制数据库的发布与订阅的方法(转载)
SQL2005删除复制数据库的发布与订阅的方法 --在测试环境中恢复从正式数据库服务器 上备份下来的bak文件后,正式环境里数据库复制的发布.订阅也被带进来了,结果恢复的数据库无法更改表结构,直接删除 ...
- 不使用配置文件动态注册HttpModule
在asp.net 4.0中,提供了一种不通过修改配置文件注册Module的方法.从.net3.5开始,新提供的PreApplicationStartMethodAttribute特性可以应用在程序集上 ...
- c语言中的fgets函数
fgets()函数用于从文件流中读取一行或指定个数的字符,其原型为: char * fgets(char * string, int size, FILE * stream); 参数说明:string ...
- 你知道require是什么吗?
引题 用过node的同学应该都知道require是用来加载模块的,那你是否存在如下的疑问呢? 1. require(path)是如何依据path找到对应module呢? 2. 为何在模块定义中,一定要 ...
- AngularJS--学习笔记(一)
AngularJS官方网站提供了一个用于学习的示例项目:PhoneCat.这是一个Web应用,用户可以浏览一些Android手机,了解它们的详细信息,并进行搜索和排序操作. 对于PhoneCat项目的 ...
- Protocol Buffers介绍
基本概念 Protocol Buffers(以下简称PB)是一种独立于语言.独立于开发平台.可扩展的序列化数据结构框架,它常常被用在通信.数据序列化保存等方面. PB是一种敏捷.高效.自动化的用于对数 ...
- UIImageView 动画 / UIImage 方向
UIImage 方向 UIImage imageOrientation是相对当前屏幕的横竖屏来判断方向 如果本身是横屏, 照片也是横屏的话, 方向是正方向 BOOL b1 = (originalIma ...