在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. ServiceStack 入门(二)

    本文介绍创建一个简单的基于ServiceStack的项目. 1. 新建一个solution 2.创建完成后有4个project. ServiceModel是定义Request DTO 和 Reques ...

  2. NET,ASP.NET,C#,WinFrom之间的联系与区别

    1:C#是编程语言(静态,强类型).类似中文.德文.英文这样. 2:.NET是一个平台(可承载多个编程语言,比如C# C++.net J# VB.Net),       但是都是运行在.net Fra ...

  3. AngularJS ng-class用法

    mark from https://my.oschina.net/gejiawen0913/blog/188547 ng-class是AngularJS预设的一个指令,用于动态自定义dom元素的css ...

  4. JavaSE复习日记 : 接口

    /* * 接口 * 引用数据类型: * 类,接口,数组; * * 接口是一种引用数据类型,可以看作是一个特殊的类,它存在的目的是为了解决没有多重继承引起的功能弱的问题而设计的,一个类只能有一个父类,但 ...

  5. (4)事件处理——(3)代码执行的顺序(Timing of code execution)

    In Chapter 1, Getting Started, we noted that $(document).ready()was jQuery's primary way to perform ...

  6. JQ兼容性问题

    checkbox操作 1:设置为选中状态   $(this).prop("checked", true); 2:判断是否选中     $(this).is(":check ...

  7. Android常用工具类封装---SharedPreferencesUtil

    SharedPreferences常用于保存一些简单的数据,如记录用户操作的配置等,使用简单. public class SharedPreferencesUtil {              // ...

  8. C#学习日志 day 4 ------ 类相关---this指针以及相关关键字

    c#中的类和java中的类没什么太大区别.但是c#有些特有的关键字以及属性使得c#具有一些特性. 首先就是this关键字,this在c++和java中都有,可以表示当前对象,以及变量所属对象等.例如 ...

  9. Use API to retrieve data from internet

    Reference: Working with APIs Many big companies and organizations provide API for us to retrieve dat ...

  10. HTML5速查表

    HTML5速查表 标签 描述 版本 属性 <!--...--> 定义注释 4 / 5 none <!DOCTYPE> 定义文档类型 4 / 5 none <a> 定 ...