在Linux内核、嵌入式代码等传统的C代码里,会有一些难以识别的宏定义。我记得在eCos, UBoot, FFmpeg有一些比较BT的宏定义,很难读懂。对于C++程序员来说,最好将这种难读的宏定义转成inline函数或模板函数。本章对这些较难的重定义进行汇总。

1. ; 在宏定义中指定义类型参数
   1: #define FPOS_TO_VAR(fpos, typed, var)                    (var) = (typed)((fpos).__pos)

   2: #define VAR_TO_FPOS(fpos, var)                           (fpos).__pos = (var)

此句在faac的代码里可以见到,其特殊之处是以类型名作为参数,而且是用宏定义一行赋值语句。像如下的调用:

  

   1: uint64_t  ret;

   2: FPOS_TO_VAR(fpos,  uint64_t, ret);

   3:  

   4: uint64_t ret;

   5: ret = (uint64_t) (fpos.__pos);

   6:  

一般可以对宏定义作换行显示,换行后会更清晰易懂一些:

   1: #define FPOS_TO_VAR(fpos, typed, var)     \   

   2:                    (var) = (typed)((fpos).__pos)

2 . 宏定义取结构偏移

这种用法似乎在uCosII中用到,记不清了。举例来说,我们知道一个struct(类型为T)中有一个变量为v,那么由v的地址来得到T的指针呢?

#define S_OFFSET(T,v)      (&((T*)0)->v)
   1: struct ABC

   2: {

   3:     int a;

   4:     char b;

   5:     int c;

   6: };

   7:  

   8: int main()

   9: {

  10:     unsigned int offset = (unsigned int) S_OFFSET(ABC, b);

  11:     return 0;

  12: }

主要是分析一下(&((T*)0)->v)是个什么意思,相当于以下几种代码:

  1. T* p = (T*) 0;  // 把0地址强转为一个T*  
  2. void* offset = &p->v; // 由于变量p的地址为0,所以成员变量v的偏移就是其地址 
T* p = (T*) 0;  // 把0地址强转为一个T*
void* offset = &p->v; // 由于变量p的地址为0,所以成员变量v的偏移就是其地址

得到上述偏移之后怎么用呢?在函数传递的过程中,只需要一个v的指针,就可以反推出其所在的结构的地址。而offset一般不方便指定为一个固定值,因为不同环境下的padding可能不一致,也不方便扩展。

  1. void* p = ...;  
  2. T* t = (T*) (p - offset); 
void* p = ...;
T* t = (T*) (p - offset);
3 . 宏定义中的连接符##

这也是一种比较特殊的用法,在eCos3中经常用到。用于替换一个完整名字(类型名、函数、变量名) 的一部分。以下再种写法等效,请仔细体会:

my_type_t   var1;

my_type_t   var2;

 

   1: struct my_type_t

   2: {

   3:         int a;

   4:         char b;

   5: };

   6:  

   7: #define    MY_TYPE(T,  name)       \

   8:         T  var##name

   9:  

  10: MY_TYPE(my_type_t, 1);

  11: MY_TYPE(my_type_t, 2);

  12:  

  13: int main()

  14: {

  15:         unsigned int offset = (unsigned int) S_OFFSET(my_type_t, b);

  16:         var1.a = 10;

  17: }

4 . 宏定义中的字符串#
   1: define  MYSTR(value)   #value

   2: int main()

   3: {

   4:         char* cc = MYSTR(hello);

   5: }

相当于

char* cc = "hello";

友情链接:C/C++宏的奇巧淫技

有意思的C宏的更多相关文章

  1. UDF——在udf当中添加几个有意思的宏

    很多人的udf都不是自己写的,直接从网上复制粘贴的,编译的时候经常报错.我编写了下面这段示例代码: 我们使用小软件编译: https://www.cnblogs.com/liusuanyatong/p ...

  2. Linux Kernel代码艺术——系统调用宏定义

    我们习惯在SI(Source Insight)中阅读Linux内核,SI会建立符号表数据库,能非常方便地跳转到变量.宏.函数等的定义处.但在处理系统调用的函数时,却会遇到一些麻烦:我们知道系统调用函数 ...

  3. Shader预处理宏、内置状态变量、多版本编译等

    预定义shader预处理宏: Target platform: SHADER_API_OPENGL - desktop OpenGL SHADER_API_D3D9 - Direct3D SHADER ...

  4. ytu 1059: 判别该年份是否闰年(水题,宏定义)

    1059: 判别该年份是否闰年 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 222  Solved: 139[Submit][Status][Web ...

  5. (转)offsetof与container_of宏[总结]

    1.前言 今天在看代码时,遇到offsetof和container_of两个宏,觉得很有意思,功能很强大.offsetof是用来判断结构体中成员的偏移位置,container_of宏用来根据成员的地址 ...

  6. 宏定义中的##操作符和... and _ _VA_ARGS_ _

    1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...

  7. iOS开发笔记--宏定义的黑魔法 - 宏菜鸟起飞手册

    宏定义在C系开发中可以说占有举足轻重的作用.底层框架自不必说,为了编译优化和方便,以及跨平台能力,宏被大量使用,可以说底层开发离开define将寸步难行.而在更高层级进行开发时,我们会将更多的重心放在 ...

  8. _T宏的使用

    来源自百度.   他的作用是让你的程序支持Unicode编码, 因为Windows使用两种字符集ANSI和UNICODE, 前者就是通常使用的单字节方式, 但这种方式处理像中文这样的双字节字符不方便, ...

  9. linux初始化宏__init, __exit

    我们在内核中经常遇到初始化函数是这样定义的:static int __init init_func(); ,与普通函数相比,定义中多了__init.那么,__init是什么意思呢?还有与其匹配的__e ...

随机推荐

  1. 大型票务系统中username和password的安全性问题

    讨论请移步至:http://www.zhiliaotech.com/ideajam/idea/detail/307 相关文章: <今天你买到票了吗?--从铁道部12306.cn站点漫谈电子商务站 ...

  2. 使用my97datepicker控件实现日期范围选择

    注:(2014-12-05内容修改:添加运行效果) 使用my97datepicker 控件,需要对日期的范围进行控制,本人自己写了一个js完成此功能,示例为当前日期到下一周周五之间的日期可选,其他日期 ...

  3. Httpservlet cannot be resolved to a type

    这个问题与上个问题可以说是“错的类似”.解决方案:就是在Tomcat的lib目录下加入servlet-api.jar 即可.

  4. WinForm中快捷键与组合按键的设置方法

    每个窗体都有这样3个事件:KeyDown.KeyPress.KeyUp,KeyDown和KeyPress都是按键按下事件,但KeyDown用的是KeyCode跟键盘各个按键相对应,它对应Keys枚举, ...

  5. shell查看并修复网络连接

    1.  shell监控网卡状态,故障时自动重启网卡 http://blog.slogra.com/post-425.html cat fix_eth0.sh #!/bin/bash check_and ...

  6. [转载]标签a的href和onclick

    转载自:http://gocom.primeton.com/blog21307_27051.htm 我以前在写<A>的href和onclick一直很随意,后来出过几次问题,以后才开始重视这 ...

  7. poj 1321 棋盘问题 递归运算

    棋盘问题 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 19935   Accepted: 9933 Description ...

  8. 转:CI伪静态化

    去掉php框架CI默认url中的index.php 2010-03-17 17:33:07|  分类: php框架ci |字号 订阅   CI默认的rewrite url中是类似这样的,例如你的CI根 ...

  9. 分享一个MD5加密工具类

    来自:http://blog.csdn.net/zranye/article/details/8234480 Es:http://blog.csdn.net/longxibendi/article/d ...

  10. jdk、jre与jvm的区别与联系

    为了学习数据库,重装了系统,之前前一直在用eclipse,现在准备换成myeclipse,这之前当然需要重新设置环境变量,顺手写下有关jdk.jre与jvm的区别与联系以供交流参考. 首先来说一下JD ...