C预处理器

来源:互联网 发布:v盾网络验证好破解吗 编辑:程序博客网 时间:2024/05/23 14:52
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__    当前源文件的名称,用字符串常量表示 
  __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__  定义为一个长整数常量 

__FUNCTION__  获取当前所在范围内的函数名。新的C99特性添加的,所以VC6中不支持该宏,而且不同的IDE环境,该宏的定义可能存在不同


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\n", (a)<(b) ) 
  TEST(0,0xFFFF)=>printf("0<0xFFFF=%d\n", (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" 

注:VC6中不支持该特性。


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 

可以改变__LINE__与__FILE__的内容,其中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源程序中当前行号,文件名为源文件的名字。宏#line主要用于调试及其它特殊应用。

例:

#line   100  /*   初始化行计数器*   /   
main   (   )   /*   行号100   */   
{   /*   行号101   */   
  printf()("%d\n"_LINE_);   /*   行号102   */ 

}


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

  _Pragma("字符串序列") 

详情请参考百科:http://baike.baidu.com/view/1451188.htm

例: 
   _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  导致结果也不一样了
0 0
原创粉丝点击