在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. JavaScript之面向对象学习二(原型属性对象与in操作符)获取对象中所有属性的方法

    1.原型属性对象于in操作符之in单独使用 有两种方式使用in操作符:单独使用和在for-in循环中使用.在单独使用中,代码如下: function Person(){ } Person.protot ...

  2. 部署SharePoint2013解决方案

    Add-SPSolutionInstall-SPSolution -Identity Grain2013.wsp -GACDeployment -CompatibilityLevel {14,15}  ...

  3. T-SQL 查询语句总结

    我们使用一下两张表作为范例: select * from [dbo].[employee] select * from [dbo].[dept] 1.select语句 DISTINCT:去掉记录中的重 ...

  4. PowerBuilder预防数据库死锁相关处理

    实际业务中碰到了PB开发的业务系统造成的数据死锁情况,整理了一些PB关于数据库死锁的一些处理. PB死锁相关 1. 即时的commit和rollback 不同数据库的锁机制各不相同,但对应用程序来说, ...

  5. 关于Oracle将小于1的数字to_char后丢掉0的解决办法

    SQL代码如下: select rtrim(to_char(0.11, 'fm9990.99'), '.') from dual;其中0.11为需要to_char的数字fm去掉字符串前面的空格9990 ...

  6. clip原理

    1.clip的概述: clip是修剪之意 clip有4个属性值:inherit auto rect(20px,40px,60px,0px) !important 其中有作用的仅rect这个属性值,着重 ...

  7. Ubuntu 12.04 LTS下logomaker的安装

    学校嵌入式课程实验,本地装的时候遇到了一系列问题,因为基本不会linux所以到处搜解决方法,中间还走了不少弯路,作个笔记. 1.解压安装倒是没什么问题,运行时提示找不到共享库 logomaker: e ...

  8. C# async await 例子

    private static async void Worker() { Console.Write("main thread id is :{0}",Thread.Current ...

  9. Debian下自动备份文件并上传到远程FTP服务器且删除指定日期前的备份Shell脚本

    说明:  1.备份目录/home/osyunwei下面所有的文件到/home/osyunweibak里面,并且保存为osyunwei20120701.tar.gz的压缩文件格式(2012_07_01是 ...

  10. 基于JDK 8的Dubbo Admin

    在使用Dubbo  Admin的时候,一直报错,无法启动,因为Dubbo Admin使用的各种库相对是比较旧的,在JDK 8下,有些小问题 具体解决过程参考的以下链接 https://github.c ...