zoukankan      html  css  js  c++  java
  • C预处理器

    C预处理器是一种简单的宏处理器。
    预处理器是由特殊的预处理器命令行控制的,它们是以#符号开头的源文件行。

    预处理器的一般操作:从源文件中删除所有的预处理器命令行,并在源文件中执行这些预处理器命令所指定的转换操作

    预处理器代码行的语法与C语言其他部分的语法是完全独立的,但经过预处理所产生的源代码必须在上下文环境中合法

    常见的预处理器命令:
    #define   定义一个预处理器宏  
    #undef     取消一个预处理器宏
    #include   插入另一个源文件的文本
    #if        测试一个常量表达式的值
    #ifdef     测试如果一个宏已被定义
    #ifndef    测试如果一个宏没有被定义
    #else      测试失败后
    #endif     终止条件文本
    #line      提供用于编译器信息的行号
    #elif      与else if相似
    defined    判断一个宏是否被定义,已定义返回1,否则返回0,其与#if和#elif联用
    #          将#后的宏标记转化为字符串
    ##         将两个相邻的宏标记连接成一个整体标记
    #pragma    指定依赖编译器的信息
    #error     用指定的信息产生一个编译时错误

    1.预处理器词法约定
    ·以#开始的行被看成是预处理器命令
    ·命令的名称必须紧随#字符之后(标准C编译器允许#后出现空格,而一些旧式编译器不允许)
    ·如果只有一个#,则被看null指令,当成空行处理
    ·如果命令名称后不包含命令参数,则命令名称后必须是空白字符或注释,例:
      #define PLUS + 或 #define PLUS /* */ +
    ·如果命令名称带参数,则左括号必须紧随命令名称
    ·如果一个宏被展开之后是一些看上去像预处理器命令,这些命令不会被标准C编译器所认识,  例:
      #define GETMATH #include <math.h>
      GETMATCH展开后为 # include < math . h>,会被编译为错误C代码
    ·在命令行末加可以进行续行,例
      #define err(flag,msg) if(flag)
       printf(msg)

    2.宏的定义
    ·语法:
    ·Object-liked defined
      #define 宏名 标记序列
    ·With parameter list defined
      #define 宏名(parameter list) 标记序列

    3.宏扫描
    ·规则:当一个宏被展开之后,对宏调用的扫描就在宏展开的开始位置继续,使宏名称在被展开宏的内部可以被认识,以便进一步的宏替换,例
      #define plus(x,y) add(y,x)
      #define add(x,y) ((x)+(y))
      对plus(plus(a,b),c)进行宏展开,步骤如下:
       plus(plus(a,b),c)
          =>add(c,plus(a,b))
          =>((c)+(plus(a,b)))
          =>((c)+(add(b,a)))
          =>((c)+((b)+(a)))
    ·自身宏嵌套
      ·在标准C中,出现在自身的展开体中的宏并不会被重新展开
      ·旧式的C预处理器在传统中并不会检测这种递归,而是试图继续展开直到系统出错为止

    4.预定义的宏
    ·常用的预定义的宏有:
      __LINE__    当前源程序行的行号,用十进制整数常量表示
      __FILE__    当前源文件的名称,用字符串常量表示

      __FUNCTION__  当前源函数的名称,用字符串常量表示
      __DATE__    编译时的日期,用“MM dd yyyy”形式的字符串常量表示
      __TIME__    编译时的时间,用“hh:mm:ss”形式的字符串常量表示
      __STDC__    当且只当编译器遵循ISO标准时,它的值是十进制常量1
      __STDC__VERSION__  如果编译器遵循C99,则这个宏的值是199901L,其他情况下,该宏没定义
      __STDC__HOSTED__  当前是宿主系统,该宏值为1,当前是独立系统,这个宏值为0
      __STDC__IEC__559__ 如果浮点实现遵循IEC 60599标准,这个宏值为1,否则无定义
      __STDC__IEC__559__COMPLEX__  如果复数运算实现遵循IEC 60559标准,则该宏值为1,否则未定义
      __STDC__ISO10646__  定义为一个长整数常量

    5.取消宏定义和宏的重定义
    ·使用#undef命令,语法:
      #undef 宏名
    ·避免宏的重定义,使用#ifndef或#ifdef命令,例
      #ifndef MYHEADER_H
      #define MYHEADER_H 2000
      #endif

    6.宏展开的优先级错误
    ·宏的操作完全是标记的文本替换,只有在宏展开结束后,才会把宏体解析为声明,表达式或语句,例:
      #define SQUARE(x) x*x
      SQUARE(a)=>a*a  //ok
      SQUARE(a+b)=>a+b*a+b //???
    ·避免这种事件发生,可以使用括号
      #define SQUARE(x) (x)*(x)
      SQUARE(a+b)=>(a+b)*(a+b) //ok

    7.把标记转换为字符串
    ·在标记序列中使用#,例
      #define TEST(a,b) printf( #a "<" #b "=%d ", (a)<(b) )
      TEST(0,0xFFFF)=>printf("0<0xFFFF=%d ", (0)<(0xFFFF))

    8.宏展开中的标记合并
    ·在标中使用##,例
      #define NAME(i) name ## i
      NAME(1)=>name1

    9.宏的可变参数列表
    ·使用省略号来表示宏参数中的可变参数列表,语法:
      #define 宏名(参数列表,...) 标记序列
      #define 宏名(...) 标记序列
    ·在标记序列中使用__VA__ARGS__来对应参数列表中的...,例
    #define MAKE_EM_A_STRING(...) #__VA__ARGS__
    MAKE_EM_A_STRING(a,b,c,d)=>"a,b,c,d"

    10.文件包含
    ·使用#include命令,形式如下:
      #include <字符序列>
      #include "字符序列"
      #include 标记序列
    ·使用<>和""的区别在于编译器查找包含文件的方式
      <>:根据编译器的定义规则和路径进行查找
      "":从源文件的当前路径开始查找,如果不存在,则以<>形式来处理

    11.条件编译
    ·使用#if,#else,#endif命令,形式:
      #if 常量表达式
         ....
      #else
         ...
      #endif
    ·使用#elif命令,形式
      #if 常量表达式1
        ...
      #elif 常量表达式2
         ...
      #elif 常量达式n
         ...
      #else
         ...
      #endif
    ·使用#ifdef和#ifndef命令,形式
      #ifdef 宏名
      表示如果该宏名已被定义,则相当于#if 1
    如果该宏名没有定义,则相当于#if 0
    ·defined操作符
    ·defined操作符可以在#if和#elif表达式中使用,不能用于别处,形式
       defined name
       defined (name)
      例:
       #if defined(VAX) 代替
       #ifdef VAX  

    12.显示的行号
    ·使用#line命令,形式:
    #line n "filename"
    #line n
    表示源代码的下一行来自用户所编写的filename的文件的第n行,n必须是十进制数字

    13.pragma指定
    ·以添加新的预处理器功能或者向编译器提供因实现而异的信息
    ·标准pragma命令
      #pragma [FP_CONTRACT|FENV_ACCESS|CX_LIMITED_RANGE] [ON|OFF|DEFAULT]
    ·_Pragma操作符,形式:
      _Pragma("字符串序列")
    例:
       _Pragma("STDC FENV_ACCESS ON") 等价于
      #pragma STDC FENV_ACCESS ON

    14.错误指令
    ·使用#error命令,形式
    #error 预处理器标记
    ·如果信息中包含有宏,而不想将宏进行展开,可以使用字符串形式
    #define SIZE 1024
    #if (SIZE%256 !=0 )
    #error "SIZE must be a multiple of 256"
    #endif
      
    15.关于一些旧编译器空白字符的预处理
    例:
      #define INC ++ 
      #define TAB internal_table
      #define INCTAB table_of _increments

      #define CONC(x,y) x/**/y
      对于CONC(INC,TAB)进行展开,标准与非标准编译器处理方式:
      标准                          非标准
      CONC(INC,TAB)             CONC(INC,TAB)
      INC TAB                   INCTAB   这里出现了不一样的结果
       ++ internal_table        table_of_increments  导致结果也不一样了

  • 相关阅读:
    web中间件之nginx
    JVM之工具分析
    jprofiler监控tomcat
    如何查看端口被占用
    训练赛第二场C题 zoj 2339 Hyperhuffman
    训练赛第三场A题 zoj 559
    训练赛第二场G题 ZOJ 2343
    训练赛第一场D题
    训练赛第一场A题 (ZOJ 2313)
    HDU 1422 重温世界杯 DP题
  • 原文地址:https://www.cnblogs.com/baiduboy/p/6568969.html
Copyright © 2011-2022 走看看