美文网首页
C 预处理

C 预处理

作者: adousj | 来源:发表于2016-08-21 13:33 被阅读44次

    C 语言提供了三种预处理功能:宏定义、文件包含、条件编译。这些操作都在预处理阶段完成,因此他们只做替换,不进行计算。

    1. 防止文件重复包含

    #ifndef PROGRAM_H
    #define PROGRAM_H
    
    // .h file, usually declaration
    
    #endif
    

    现在使用新编译器可以如下替换:

    #pragma once
    

    2. 条件编译

    #define TWO
    //#define TWO 0  //此时为假
    int main()
    {
       #ifdef ONE
              printf("1\n");
       #elif defined TWO
              printf("2\n");
       #else
              printf("3\n");
       #endif
    }
    // 输出 2
    

    在编译的时候指定 -DONE-DONE=1 就能够指定编译条件。

    3. 特殊符号

    以下是双下划线

    符号 备注
    __FILE__ 当前程序文件名的字符串
    __LINE__ 当前行号的整数
    __STDC__ 如果编译器遵循ANSI C标准,它就是个非零值
    __DATE__ 当前日期的字符串
    __TIME__ 当前时间的字符串
    #error 将使编译器显示一条错误信息,然后停止编译
    #line 指令改变LINEFILE的内容,它们是在编译程序中预先定义的标识符
    #pragma 没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息

    4. 无参宏定义

    #define PI 3.141592653
    
    1. 宏名一般大写
    • 宏定义允许嵌套
    • 宏定义不存在类型
    • 可以用 #undef PI 终止宏定义的作用域

    无参数宏定义更推荐使用常量类型代替

    const double PI = 3.141592653;
    

    5. 带参宏定义

    #define AREA(a,b) ((a)*(b))
    
    1. 记得要给参数加括号
    #define AREA(a,b) a*b
    
    AREA(2+3, 4) × 5
    // 替换成 2 + 3 × 4 × 5
    // 应该是 ((2×+3)×4) × 5
    

    对于 decltype 而言, () 有特殊含义,表示引用类型,因此当宏定义的结果为左值,并且宏定义外层加有括号,那么 decltype 结果就是引用类型。

    #define INC(a) (++a)
    
    int x = 1;
    // decltype(INC(x))  y;
    // y 是引用类型,必须初始化
    decltype(INC(x))  y = x;
    decltype(INC(x) + 0)  z;
    // z 是值类型
    
    1. 宏名和参数的括号间不能有空格
    2. 宏定义换行
    #define _range(x,y)  []{               \
        std::vector<int> v((y-x));            \
        std::iota(v.begin(), v.end(), x);     \
        return v;                             \
    }()
    
    1. 宏定义展开会使源程序变长

    6. 宏定义中 #

    # 字符串化操作符
    用于把宏定义中参数两端加上 引号""

    #define STR(str) #str
    
    STR(my#name)      // "my#name"
    STR( abc )        // 忽略参数名前后空格,"abc"
    STR(abc     def ) // 中间多个空格只保留一个 "abc def"
    

    一般由任意字符都可以做形参,但以下情况会出错:

    • STR())这样,编译器不会把“)”当成STR()的参数
    • STR(,)同上,编译器不会把“,”当成STR的参数
    • STR(A,B)如果实参过多,则编译器会把多余的参数舍去
    • STR((A,B))会被解读为实参为:(A,B),而不是被解读为两个实参,第一个是(A第二个是B)

    7. 宏定义中##

    ## 是记号粘贴操作符
    普通的宏定义中,预处理器一般把空格解释成分段标志,对于每一段和前面比较,相同的就被替换。但是这样做的结果是,被替换段之间存在一些空格。如果我们不希望出现这些空格,就可以通过添加一些##来替代空格。

    #define A1(name, type)  type name_##type##_type
    #define A2(name, type)  type name ##_##type##_type
    
    A1(a1, int);   // int name_int_type;
    A2(a1, int);   // int a1_int_type;
    

    8. 宏支持可变参数

    使用 VA_ARGS 把参数传递给宏

    #define PRINT(...)  printf(__VA_ARGS__) 
    // or
    // #define PRINT(fmt, args...)  printf(fmt, args)
    
    PRINT("%d %c\n", 10, 'c');
    

    9. 参考

    相关文章

      网友评论

          本文标题:C 预处理

          本文链接:https://www.haomeiwen.com/subject/fazisttx.html