c语言应用系列: 01. 宏在项目中的应用

 http://blog.csdn.net/fly_together/article/details/4552001

C语言中的宏是C编译器提供的预处理 命令的一种,在预处理期间(编译前 )进行求值替换。善用宏可以让代码准确(SPOT原则 )、高效(编译期求值 )、易维护(容易理解 ),配合编译器本身提供的一些机制,可以在不破坏程序完整性的前提下,快捷方便的对程序执行调试(打开/关闭调试信息输出)。但是如果错误应用或者滥用宏,也会导致晦涩的错误、让代码难以维护。本文对C语言中宏的应用做出详细的介绍,并就具体项目应用做分析,最后,提供项目级的宏调试代码文件。

 

 

1. 宏的形式

    分为两种形式。在一个宏定义中,宏的名称紧跟着一个左圆括弧,则称之为函数式宏 ;反之,则为对象式宏

 

    (1) 对象式宏 :由宏名及可选的宏体构成。

               #define name sequence-of-tokens(optional)

      例如:

         #define HTTP_REQUEST_SIZE      0x1024   /* http request packet size limitted */

         #define MAX_CONCURRENT         5000     /* maximum value of server concurrent */

      一般代码中大量存在相关宏的引用,当相关宏值需要更改时,只需要更改宏定义,而不用跟踪整个代码来替换。

 

    (2) 函数式宏 :由宏名、紧接的左圆括弧及可选任意参数、右圆括弧及可选宏体构成。

               #define name( identifier-list(optional) ) sequence-of-tokens(optional)

      例如:

               #define sum( x , y )   ( (x) + (y) )

               #define tcp_socket()  socket(PF_UNIX,SOCK_STREAM,0)

               #define debug_printf(...)  printf(__VA_ARGS__)

 

          需要注意的是,左圆括弧必须紧接着宏名 ,否则,左圆括弧连同右边所有部分,会成为宏体,例如,编写如下代码段:

                /* test01.c */

                #define multiply  ( x, y )  ( (x) * (y) )
                int i = multiply(x,y);

          使用 gcc 按照如下指令编译:

                $ gcc -E macro.c

          可以看到输出为:

                # 1 "macro.c"
                # 1 "<built-in>"
                # 1 "<command line>"
                # 1 "macro.c"


                int i = ( x, y ) ( (x) * (y) )(x,y);

          而不是 我们期望的:

                int i = ( (x) * (y) );

 

2. 宏的重新扫描

     宏调用在扩展(替换)以后,会从替换宏体的开始处重新开始宏处理,以实现深层替换 。宏的所有替换不是发生在宏定义的地方,而是全部发生在宏调用的地方。例如,如下代码段:

                #define plus(x,y)  add(y,x)

         #define add(x,y)   ((x)+(y))

         plus(plus(a,b),c)

   其扩展过程分布描述如下:

         Step                            Result

         1.     original                 plus(plus(a,b),c)

         2.                              add(c, plus(a,b))

         3.                              ((c)+(plus(a,b)))

         4.                              ((c)+(add(b,a)))

         5.      final                   ((c)+(((b)+(a))))

 

   关于重新扫描有很重要的一点:C标准明确规定,宏体内直接或间接 的出现宏(宏名)本身不会再度扩展

 

   我们考虑某平台下存在函数:

         void func(int x);

   并且代码中大量存在func的调用,假设现在另一平台下相同功能的函数:

         void func(int x, int y);

   则可以定义如下宏实现:

         #define CONSTANT_VALUE 0

         #define func(x) func(x,CONSTANT_VALUE)

 

   再考虑另外一种情况:

         #define char unsigned char

   以上宏期望代码中全部的 char 型别用 unsigned char 替换。如果允许再度扩展,则陷入无限循环。

 

   很可惜,以上限制也给我们的编码带来了不利。例如如下代码段是达不到期望值的:

         #define  factorial(x)           ( ( 0 == (x) ) ? 1 : factorial(x-1) )

         int iFac = factorial(100);

     以上代码段我们期望实现编译期100的阶乘求值。很好的设计思路,很可惜,标准不允许。事实上,上行语句的扩展是:

                int iFac =( ( 0 == (100) ) ? 1 : factorial(100 -1) );

   很显然,程序会通过预处理和编译,但是在链接时候报错,除非正好存在函数 factorial(type x)(type是参数型别,要求支持从int到type的正确转换)。

     附带描述一句:C++ 编译器亦不支持以上宏,但是C++中模板(template)可实现以上功能

 

3.  预定义的宏

    C标准预定义了一些宏,日常编码及调试中非常有用。就日常经常用到,描述如下:

         __LINE__           代码当前行

     __FILE__           代码所在文件

     __func__           代码所在函数

     __DATE__           代码编译时日期

     __TIME__           代码编译时时间

 

4. 边界效应

    对于函数式宏而言,其与函数的差别是:函数的实参只求值一次,而函数式宏可能求值多次导致边界效应。考虑

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值