在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. linux学习 建立静态库,动态库,写简单的makefile

    建立静态库 建立四个文件 bin(可运行文件),lib(库),include(头文件),src(放源文件) 这里的起的库明为add 在src文件里运行 1)gcc -c add.c //编译add.c ...

  2. 从零开始学习UNITY3D(GUI篇 群组视图控件)

    控件组可以看成一个大的容器,控件组里面的控件,相对位置已该控件组为基准,而不再已屏幕左上角为基准. 下面来看一下代码实例及其效果截图: public class GUI2 : MonoBehaviou ...

  3. .NET平台和C#语言

    .NET平台的作用C#语言通过.NET平台来编写 部署 运行.NET应用程序. 为什么学习.NET语言1. C#语言是为.NET平台而生的.2. C#语言是完全面向对象的语言,所以一般情况下我们用C# ...

  4. Python 读写文件操作

    python进行文件读写的函数是open或file file_handler = open(filename,,mode) Table mode 模式 描述 r 以读方式打开文件,可读取文件信息. w ...

  5. Oracle分析函数之开窗子句-即WINDOWING子句

    Oracle的分析函数,对我们进行统计有很大的帮助,可以避免一些子查询等操作,在统计中,我们对开窗函数的接触较少,下面主要介绍下开窗函数的使用; http://www.itpub.net/thread ...

  6. hexo博客部署到github无法上传的问题

    博客生成之后,按照网上别人的教程,讲项目部署到github上,修改_config.yaml中的deploy部分如下所示: deploy: type: git repository: https://g ...

  7. 50句高级SQL语句

    一个题目涉及到的50个Sql语句 --(下面表的结构以给出,自己在数据库中建立表.并且添加相应的数据,数据要全面些. 其中Student表中,SId为学生的ID) ------------------ ...

  8. 基于MDK的ARM-GCC开发环境建立及新唐M0的HID类设备的C++开发

    一,下载安装测试arm-none-eabi-gcc编译工具链 1,查看arm-none-eabi-gcc编译工具版本        打开网页:https://sourcery.mentor.com/G ...

  9. Android多线程任务优化1:探讨AsyncTask的缺陷

     AsyncTask还有别的缺陷,在生成listview的时候,如果adapter里面的count动态改变的话,不能使用AsyncTask,只能使用Thread+Handler,否则会出现如下错误 j ...

  10. HDU 5737 Differencia(归并树)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=5737 [题目大意] 给出两个序列a和b,要求实现两个操作: 1. 将a序列的一个区间中的所有数改成 ...