预处理指令,宏和运算符
                                               
当编译程序时,它做的第一件事是进行预处理。这一阶段中,甚至可以人为地将编译器视为一个不同的实体——预处理器。该阶段中,编译器读入头文件、决定编译哪些行的源代码并执行文本替换。
预编译阶段的优越性在于它的编译及运行之前执行了某些特定的操作。这些操作并不添加额外的程序执行时间。同时,这些命令也不与程序运行时所发出的任何指令相对应。所以,在使用预处理指令时就需要兼顾实际的运行情况。
预处理的三个基本元素:
指令:在程序编译前执行的特定命令。
预定义宏:在编译前所执行的特定命令。
预处理运算符:#if#define中使用。
预处理指令语法与C++的其它语法有所不同。指令以行末结束,而不需要分号。但你可以用续行符\来摆脱物理行的限制。另外,指令必须从第一行开始。
指令:有一半预处理指令提供了对条件编译的支持,条件编译的作用是用来维护同一程序的不同版本。这些指令包括:
#if  #ifdef  #elif  #ifndef  #else  #end
其余的预处理指令提供了对其它功能的支持,例如包括头文件和宏定义:
#define  #include  #pragma  #error  #line  #undef
详解:
#define [定义符号或宏]
目的:
一:提供了创建符号常量的有效信息途径。
二:使编写人员能写宏指令,这些宏指令看上去像函数调用,但实际上是通过文本替换来实现。
三:简单地定义某些符号作为#ifdef指令的开关。
语法格式:
#define 标识符 替代值
#define 标识符[(参数1,.....,参数n)] 替代值
其中,替代值中出现的形参将在使用时被实参替代. 就象写函数一样.
#define 标识符
◆用法一:符号常量
有些程序用到了一些很重要但又非常难忘或难以输入的数字。最好事先将它们转化成为符号常量。在转化时,通常是以大写形式表示,以便将它们与变量名区分开来。例:
#define E 2.718281828459
这条指令的意思是每当程序中出现E时,预处理器就会将E替换成2.718281828459
注意:
若符号出现在注释、被引用的字符串或一个较长的单词的一部分时,它不会被替换。如English中的E就不会被替换成2.718281828459
字符串常量的用法#defineconst的区别:
const double e = 2.718281828459;
#define E 2.718281828459
两者看似效果相同,但却有一定区别。
如果E是符号常量,它就是真正意义上的常量。在程序时使用E如同直接输入数字。这样一来编写效率就有所提高。作为一个真正意义上的常量,E可以在程序运行前折算成为常量表达
式,从而加快运行速度。例如,在下列代码中,E/2在编译时而非运行时就已经被算出来了:
x = sin(y * E/2);
符号常量的另一特点是,在数组定义时可被用作设定数组大小。这是符号常量可被视为真正意义上的常量的结果。例:
#define ROW 30
#define COL 20
……
……
int a[ROW][COL];//这样是可行的。
……
从另一方面说,const 变量利于编译和调试。因为编译器和调试器可通过名称来识别变量(符号常量的名称已经被实际数值所替换,故难以识别)。同时,对于C++的面向对象编程,const变量可通过定义特定类的作用域来保持面向对象的特性。
◆用法二:宏指令
宏指令看起来像函数调用,但它们是通过文字替换,而不是真正的函数调用来实现的。例如:
#define max(A,B)((A)>(B)?(A):(B))
预处理器在编译前用下列表达式来替换表达式max(i,j):
((i)>(j)?(i):(j))
宏指令使用在类修似于上述的简单环境中。然而,与真正的函数调用相比,宏指令有许多缺陷。例如,不能限定参数的类型。另一缺陷是不能够使用复杂的语句,因为宏指令中的所有
一切都必须压缩于一条语句之内。通过使用inline可以解决这些缺陷。类似宏指令,inline的函数被直接插入源码内,这优化了运行速度,但很可能是以增加了可执行程序的长度为代价的。
在替换模式中对于每个参数使用括号是良好的编程习惯,因为这使得在解算周围的运算符之前强行地换算了参数。不然,如果参数本身包含运算符,就可能导致意外的(甚至是错误的)结果。
◆用法三:控制编译
一个符号可被定义为条件的开关。而这个开关能够控制#ifdefifndef指令的操作。
#ifdef#ifndef指令并不在意一个符号是如何定义的,仅仅在于它有没有被定义。因而可以定义一个符号而不用给出它的替换值;在这种情况下,该符号由一个空串替换。然而,只要#ifdef及其类似的指令应用到它,它就是合法的。
在下例中,符号USE-8086被定义了;这就导致了预处理器编译#ifdef#ifndef之间的代码。
#define USE-8086
……
#ifdef USE-8086
……//会被编译的语句
#endif
#elif [在另一条件下编译]
#elif指令(”else if”)建立了另一个条件来作为一个#if/#endif模块的一部分。#elif指令如同#if指令一样定义了编译条件。该指令定义了一个常量整数表达式:
#elif表达式
如同#if指令,常量整数表达式可包含常量及大多数C++运算符,但是它不包含sizeof运算符、类型转换或enum常量。表达式可包含特别的define(symbol)运算符,如果symbol预先定义过了,就判为1,否则为0
关于条件编译以及完整的语法可以参见#if
#else [在条件为假的情况下编译]
#else指令标注了一个条件编译语句集合的最后一个语句块。该语句块仅在所有的前述#if#elif条件均为假的情况下才编译。从语法上来说,该指令类似于else指令,但是,它用于我条件编译,而不是在运行时的控制程序流。
一个相应的#endif指令终止#else语句块。
……
……//语句块
#else
#endif
完整语法参见#if
#endif [终止条件块]
#endif指令是条件编译语法中所必须的。只要使用了#if指令或类似指令(#ifdef#ifndef),就必须用#endif来标注该条件编译语句块的结束。
#endif指令单独出现在一个语句行中:
#endif
它终止了以#if指令开始的语法。关于条件编译及完整语法的讨论参见#if
#error [停止并汇报错误]
#error指令使编译器能够立即终止编译并返回一条信息。该指令具有以下语法格式:
#error 信息
例如,如果符号ERR已被定义,则下列代码使编译器终止编译并输出一条错误信息:
#ifdef ERR
#error 这里某某出错
#endif
当编译器处理该#error指令时,就会终止编译并且输出当前行号和一条信息,信息内容是:这里某某出错
当违反了一些基本的程序假设环境时,#error指令就会提供一条阻止继续编译程序的途径。特别是当继续编译的程序起不了任何作用时,就可以使用该指令来避免此类的情况发生。最好使用message来提供有用的调试信息。
#if [在条件成立时编译]
尽管#if指令看上去和if语句使用差不多,但该指令作用效果却是完全不同的。#if指令主要有两种用法:条件编译和多行代码的暂时注释。条件编译对于无须复制源码而编译程序的多个版本是很有用的。
一条#if指令标注了一个条件编译快的开始。如果由#if指令所指定的表达式是非零的(),则C++编译器编译随后的语句,直到另外一个相匹配的#elif#else#endif指令出现。
#if表达式
语句块一
[#elif 语句块二]
[#elif 语句块三]
……
……
[#else 语句块N]
#endif
此处的方括号表示可选项;该语法的唯一必要部分是#if#define指令。可以使用任意数量的#elif指令。也可以使用最多一次的#else指令,它必须放在#if#elif指令之后。
C++预处理器检察每一条常量表达式,直到其中的一条表达式为真(非零)。此时,相应的语句块就被编译。每一个语句块都可以包含任意形式的C++代码,包括说明、指令和可执行语句。
常量表达式是由常量和运算符构成的C++表达式,但它不能包含sizeof运算符、强制类型转换或enum常量。表达式中的常量通常是由#define指令或命令行设置所预先定义的符号。
◆用法一:条件编译
条件编译是编译一个程序的不同版本的有效方法。它在许多情况下很有用。你可能在为多个操作平台写一个程序,而且发现不得不为每一个平台重写代码的某一特定部分。你也可能想给程序的正式发布版建立一个调试版本,以便获得调试诊断信息。
这样就产生了一个进退两难的局面。当你要修改程序的各个不同版本时,就不得不做很多类似于添加和删除之类的额外工作。但是如果你想保留程序的各个完整版本时,又会占用大量的磁盘空间。更糟糕的是,每当你想给程序添加一个新特性时,就不得不给程序的每一个版本进行同样的操作。
解决这些问题的方法就是条件编译,这是一种将程序不同版本之间的特定代码部分分离出来的方法。例如对不同的计算机想采用不同的字长系统。首先,定义一系列常量:
#define SHORT 0
#define LONG 1
#define REAL 2
现在,对不同字长的系统进行编译时,只需修改一个语句行,该行定义了系统的COORDSYS值。如下:
#define COORDSYS REAL
其实,有许多编译器都支持命令行或开发环境选项来将一个符号定义为编译命令的一部分,而不需要#define语句。这样的话,你就不用重写代码行。详情参阅你所使用的编译器的文档。
程序的余下部分检察COORDSYS的值来决定哪些部分需要编译:
#if COORDSYS==SHORT
short x,y;
#elif COORDSYS==LONG
long x,y;
#elif COORDSYS==REAL
double x,y;
#endif
要注意#if指令与if语句的区别。#if指令与程序运行的条件无关。
◆用法二:注释行语句
有时候,你可能需要注释掉一些源码行;即暂时性地从源程序中删除这些行,但又使这些代码很容易恢复。达到这一目的的一个明显方法是采用C类型的开始注释符(/*)和结束注释符(/*),将它们置于需要注释掉的代码段的两头。然而,如果其中的某些行已经使用了注释符,该方法就会出错。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。