zoukankan      html  css  js  c++  java
  • #define中 #与##的神奇用法linux学习 (转)

    #define中 #与##的神奇用法linux学习 (转)  
    
    #define f(a,b) a##b
    #define d(a) #a
    #define s(a) d(a)
    
    void main( void )
    {
        puts(d(f(a,b)));
        puts(s(f(a,b)));
    }
    
    输出结果:
    f(a,b)
    ab
    
    分析:  ##把两个符号连起来
        #a指把a当成符号,就是把#后面的看成字符串
    
    
    # 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令
        #define to_string( s ) # s
    将会使编译器把以下命令
        cout < < to_string( Hello World! ) < < endl;
    理解为
        cout < < "Hello World!" < < endl;
    使用##连结##前后的内容. 例如, 命令
        #define concatenate( x, y ) x ## y
        ...
        int xy = 10;
        ...
    将会使编译器把
        cout < < concatenate( x, y ) < < endl;
    解释为
        cout < < xy < < endl;
    理所当然,将会在标准输出处显示'10'.
    
     
    
    puts(d(f(a,b)));  ----> 因为d宏中的参数是另外一个宏,且带##,所以作为参数的宏不展开,相当于
                                puts(#f(a,b));----->puts("f(a,b)");
    puts(s(f(a,b))); ----> 因为s宏中的参数是另外一个宏,但不带##,所以作为参数的宏先展开,相当于
                                puts(s(ab));----->puts(d(ab));---->puts(#ab);---->puts("ab");
    
     
    
    #define f(a,b) a##b
    #define d(a) #a --》 以"#"开头的,直接替换,不展开:immediately replaced by the unexpanded actual argument
    #define s(a) d(a) --》 非以"#"开头的,先展开,再替换,也就是一般的情况
    
    所以就两种情况:
    1,不以"#"开头的,先展开参数a,然后是替换代码:puts(s(f(a,b)));-->puts(s(ab))-->puts(d(ab))-->puts("ab")
    2,以"#"开头的,直接替换,不展开:puts(d(f(a,b)))-->puts("f(a,b)")
    
     
    
    #include <stdio.h>
    #define DIRECT_LITERAL(a)  #a
    #define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
    
     
    
    
    int main(void)
    {
        puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
        puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
        return 0;
    }
    这其实从编译角度的展开归约也可以理解啊。
    
    以上代码第一种情况,当预编译器看到DIRECT_LITERAL后查到它是宏定义,定义为#a,此时后面的参数部分就会以#a的形式生成到源文件中。也就是说,预编译后的源文件中,替代第一条语句的就是:
    puts("INDIRECT_LITERAL(a + b)");输出则是INDIRECT_LITERAL(a + b)
    
    而对于第二条语句,当预编译器看到INDIRECT_LITERAL后查到它是宏定义,定义为DIRECT_LITERAL(a),这时先把它作为DIRECT_LITERAL(DIRECT_LITERAL(a + b))的形式暂存起来,你也可以理解为这个状态是语法树的当中一个叶结点。然后再分析后面的DIRECT_LITERAL后面的参数部分,即:DIRECT_LITERAL(a + b),同样,预编译器会将它归约为"a + b"的形式。这样对于里面的DIRECT_LITERAL(a + b)的形式就完全确定下来了,那么这个值就可以充当叶子结点,即它底下不会再有结点。然后再回到刚才那个状态,DIRECT_LITERAL("a + b")最后就是""a + b""。所以这里输出是"a + b"。
    
    值得注意的是#a是将参数a转为字符串形式。所以像DIRECT_LITERAL(a)的展开形式是字符串常量"a"
    那么DIRECT_LITERAL("a")展开就是""a""。
    
    #include <stdio.h>
    #define DIRECT_LITERAL(a)  #a
    #define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
    
    #define DIRECT_CAT(a, b)   a##b
    #define INDIRECT_CAT(a, b)  DIRECT_CAT(a, b)
    
    int main(void)
    {
        puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
        puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
        puts(INDIRECT_LITERAL(DIRECT_CAT(INDIRECT_CAT(a, b), INDIRECT_CAT(c, d))));
        puts(INDIRECT_LITERAL(INDIRECT_CAT(DIRECT_CAT(a, b), DIRECT_CAT(c, d))));
        return 0;
    } 
  • 相关阅读:
    代码整洁之道它山之玉可以重构:身份证号码解析、验证工具(第一天)
    这也是C#代码吗 代码阅读性进阶:测试文档化
    它山之玉可以重构:身份证号码解析、验证工具(第三天)
    它山之玉可以重构:身份证号4(第四天)
    自己动手写压力测试三剑客HttpClient+Async+Parallel
    三层架构,够不够DDD眼中的三层(附C#源代码实现)
    百般武艺为哪般再谈业务域的核心地位
    深入浅出裸测之道单元测试的单元化
    它山之玉可以重构:身份证号码解析、验证工具(第二天)
    重构之重与敏捷之轻身份证号重构回顾
  • 原文地址:https://www.cnblogs.com/timssd/p/4513492.html
Copyright © 2011-2022 走看看