设为首页 加入收藏

TOP

关于C里面宏替换的问题(一)
2013-01-13 11:05:23 来源: 作者: 【 】 浏览:1873
Tags:关于 里面 替换 问题

 

  呵呵,这个问题其实应该这么看:

  对于:

  #define cat(a,b) a ## b

  cat(cat(1,2),3)

  很多回复的朋友都有各种各样的结论,有的说是从最外层往里开始替换的,也有的说是从最里层往外替换的,两种说法都能刚好解释一些特例而已,其实两者都不完全对,通过我的实践和对标准的理解得出微软的编译器的处理办法如下:

  cat(cat(1,2),3)

  cat(1,2) ## 3

  cat(1,2)3 //由规则2得到cat(1,2)和3即使是宏也不进行求值

  而对于:

  #define cat(a,b) a##b

  #define xcat(a,b) cat(a,b)

  xcat(xcat(1,2),3)

  事实上微软和大部分编译器都是这样进行替换的:

  xcat(xcat(1,2),3)

  cat(xcat(1,2),3) //由规则2得到xcat(1,2)是一个宏且没有相关的#和##,因此对此宏进行求值展开,注意是完全展开,而不是只展开一层,同时当前宏展开的编译器程序暂停,递归调用了另一个宏替换程序

  xcat(1,2) -> cat(1,2) -> 1 ## 2 ->12 //该宏替换子程序结束返回结果12给上一级宏替换程序

  cat(12,3) //主宏替换程序继续进行,xcat(1,2)没有相连的#,##所以求值展开为12

  123 //最后结果就很明显了

  现在再来看lz这个问题:

  cat(cat(1,2),3)

  cat(a,b)

  f(cat(cat(1,2),3))

  第一个已经解决,第二个展开如下:

  cat(a,b)

  a ## b //由标准规则2知道,即使a,b是宏在这里也不进行求值展开了

  ab //主宏替换程序结束,再次扫描发现ab还是一个宏,且没有相连的#,##因此调用宏展开子程序求其值

  ab -> AB

  AB //再次扫描已经没有宏了,主宏替换程序结束

  第三个宏:

  f(cat(cat(1,2),3)) //经扫描发现有递归宏,从最外层开始替换,遇到需要展开的宏则调用宏展开子程序

  fff cat(cat(1,2),3) //由规则2继续,对cat(cat(1,2),3)调用宏展开子程序

  cat(cat(1,2),3) -> cat(1,2) ## 3 ->cat(1,2)3 //由于cat(1,2)3不是宏,宏替换子程序结束并返回

  fff cat(1,2)3

  再对supermegaboy的另一个问题解释一下:

  #define N 10

  如果想制造字符串化效果"10",直接#define X(N) #N是不行的,根据第一点,结果只会是"N",而不是"10",这时候应该这样定义宏:

  #define N 10

  #define Y(N) X(N)

  #define X(N) #N

  对于他的这部分说明,我们来用我刚刚的方法进行展开即可:

  首先:

  #define N 10

  #define X(N) #N

  宏替换开始:X(N) -> #N -> "N",因为N遇到了#,由标准可知不对它展开

  为什么

  #define N 10

  #define Y(N) X(N)

  #define X(N) #N

  这样行呢?

  依次展开就明白了:Y(N) -> X(N) -> X(10) -> #10 ->"10" //在第二步时N没有#,##因此在对X(N)递归展开时先对N展开了,N展开后再继续对X(N)的展开。

  我写两个宏,有需要看各种宏展开的可以像这样用:

  #define TOSTR_HELPER(a) #a

  #define TOSTR(a) TOSTR_HELPER(a)

  #define … //定义你想要看最后展开结果的递归宏

  void main()

  {

  std::cout《TOSTR(…)《endl;

  }

  这样就可以看TOSTR()中宏的展开情况了,因为它会把里面的宏展开结果转化成字符串并输出。

  思考:编译器其实也是一个程序,单纯从最外层向最里层替换的操作在程序上不好设计,单纯从最里向最外层替换也一样,所以实际的替换过程其实是里外都在进行,我用伪代码写一个可以实现上述方法的替换程序,猜测那些编译器大致是这么处理的:

  TEXT MacroSubstitute(TEXT Macro , TEXT macro[])

  {

  扫描该Macro分离出该Macro的参数TEXT parameter[…](如果有的话);

  if(该Macro不被#和##修饰)

  Macro=为其定义的宏;            //参数还没有展开,只针对宏体

  else

  return Macro;                //如果被修饰则不对它展开直接返回

  for(对该Macro的参数进行遍历 : i=0 -> N)

  if(parameter[i]存在于macro[]中)

  parameter[i]=MacroSubstitute(parameter[i],macro); //对参数进行展开,递归调用宏替换程序

  if(Macro在macro[]中)                //被展开的宏体仍然是宏

  Macro(…)=Macro(parameter[0],parameter …);//用已经展开的参数替换原来的参数形成新的宏

  return MacroSubstitute(Macro,macro);        //最后把这个新宏再按照宏替换的方式展开返回

  }

  最后总结一下吧:

  标准没有规定替换的具体实现,只是提到了一些需要注意的规则,所以替换的具体实现在不同的编译器下是不同的,目前为止,GNU和微软的编译器处理办法就是我上面所说的那样,算是标准的一种实现吧。

  问题是解决了,但是建议这样的宏尽量不要用,现在已经迈入标准C++(www.cppentry.com)的时代了还是用const和inline吧

      

首页 上一页 1 2 3 4 5 6 7 下一页 尾页 1/8/8
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇浅析C语言的基础之细节问题 下一篇在C中如何使函数返回数组

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: