C措施的源代码中可包罗各类编译指令,这些指令称为预处理惩罚呼吁。固然它们实际上不是C语言的一部门,但却扩展了C措施设计的情况。本节将先容如何应用预处理惩罚措施和注释简化措施开拓进程,并提高措施的可读性。
4.7.1C语言的预处理惩罚措施
ANSI尺度界说的C语言预处理惩罚措施包罗下列呼吁:
#define
#error
#include
#if
#else
#elif
#endif
#ifdef
#ifndef
#undef
#line
#pragma
很是明明,所有预处理惩罚呼吁均以标记#开头,下面别离加以先容。
4.7.2#define
呼吁#define界说了一个标识符及一个串。在源措施中每次碰着该标识符时,均以界说的串代换它。ANSI尺度将标识符界说为宏名,将替换进程称为宏替换。呼吁的一般形式为:
#define identifier string
留意,该语句没有分号。在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行竣事。
譬喻,如但愿TURE取值1,FALSE取值0,可说明两个宏#define
#define TURE 1
#define FALSE 0
这使得在源措施中每次碰着TURE或FALSE就用0或1取代。
譬喻,在屏幕上打印“012”:
printf("%d%d%d",FALSE,TRUE,TRUE+1);
宏名界说后,即可成为其它宏名界说中的一部门。譬喻,下面代码界说了ONE、TWO及THREE的值。
#define ONE 1
#define TWO ONE+ONE
#define THREE ONE+TWO
分明宏替换仅仅是以串取代标识符这点很重要。因此,假如但愿界说一个尺度错误信息,可编写如下代码:
#defineE_MS"standard error on input\n"
printf(E_MS);
编译措施碰着标识符E_MS时,就用“standard error on input\n”替换。对付编译措施,printf()语句实际是如下形式:
printf("standard error on input\n;")
假如在串中含有标识符,则不举办替换。譬喻:
#define XYZ this is a test
.
.
.
printf("XYZ");
该段不打印"this is a test"而打印"XYZ"。
假如串长于一行,可以在该行末端用一反斜杠续行,譬喻:
#defineLONG_STRING"this is a very long\
string that is used as an example"
C语言措施普遍利用大写字母界说标识符。这种约定可使人读措施时很快发明那边有宏替换。最好是将所有的#define放到文件的开始处或独立的文件中(用#include会见),而不是将它们分手到整个措施中。
宏代换的最一般用途是界说常量的名字和措施中的“游戏数”。譬喻,某一措施界说了一个数组,而它的几个子措施要会见该数组,不该直接以常量定命组巨细,最好是用名字界说之(需改变数组巨细时)。
#define MAX_SIZE100
float balance [MAX_SIZE;]#define呼吁的另一个有用特性是,宏名可以取参量。每次碰着宏名时,与之相连的形参均由措施中的实参取代。譬喻:
当编译该措施时,由MIN(a,b)界说的表达式被替换,x和y用作操纵数,即printf()语句被代换后取如下形式:
printf("the minimum is:%,d"(x<y)?x:y);
用宏代调换代实在的函数的一大长处是宏替换增加了代码的速度,因为不存在函数挪用的开销。但增加快度也有价钱:由于反复编码而增加了措施长度。
4.7.3#error
处理惩罚器呼吁#error强迫编译措施遏制编译,主要用于措施调试。
4.7.4#include
呼吁#include使编译措施将另一源文件嵌入带有#include的源文件,被读入的源文件必需用双引号或尖括号括起来。譬喻:
#include"stdio.h"
#include<stdio.h>
这两行代码均利用C编译措施读入并编译用于处理惩罚磁盘文件库的子措施。
将文件嵌入#include呼吁中的文件内是可行的,这种方法称为嵌套的嵌入文件,嵌套条理依赖于详细实现。
假如显式路径名为文件标识符的一部门,则仅在哪些子目次中搜索被嵌入文件。不然,假如文件名用双引号括起来,则首先检索当前事情目次。假如未发明文件,则在呼吁行中说明的所有目次中搜索。假如仍未发明文件,则搜索实现时界说的尺度目次。
假如没有显式路径名且文件名被尖括号括起来,则首先在编译呼吁行中的目次内检索。
假如文件没找到,则检索尺度目次,不检索当前事情目次。
4.7.5条件编译呼吁
有几个呼吁可对措施源代码的各部门有选择地举办编译,该进程称为条件编译。贸易软件公司遍及应用条件编译来提供和维护某一措施的很多顾主版本。
1.#if、#else,#elif及#endif
#if的一般寄义是假如#if后头的常量表达式为true,则编译它与#endif之间的代码,不然跳过这些代码。呼吁#endif标识一个#if块的竣事,拜见例4-13。
#ifconstant-expression
statement sequence
#endif
#p#分页标题#e#
由于MAX大于99,以上措施在屏幕上显示一串动静。该例说明白一个重点:跟在#if后头的表达式在编译时求值,因此它必需仅含常量及已界说过的标识符,不行利用变量。表达式不许含有操纵符sizeof。
#else呼吁的成果有点象C语言中的else;#else成立另一选择(在#if失败的环境下)。因而上面的例子可扩充,拜见例4-14。
在此例中,因为M A X小于9 9,所以,不编译#if块,而是编译# else块,因此,屏幕上显
示"compiled for small array"这一动静。
留意,# else 既是# if 块又是#else 块头。这是因为任何#if 仅有一个#endif。
#elif呼吁意义与ELSE IF 沟通,它形成一个if else-if路线状语句,可举办多种编译选择。
#elif 后跟一个常量表达式。假如表达式为t r u e,则编译其后的代码块,差池其它#elif表达式进
行测试。不然,顺序测试下一块。
#if expression
statement sequence
#elif expression1
statement sequence
#elif expression2
statement sequence
#elif expression3
statement sequence
#elif expression4
#elif expression3N
statement sequence
#endif
譬喻:下面措施操作ACTI Ve_ COUNTRY界说钱币标记。
#define US 0
#define ENGLAND1
#define FRANCE 2
# define ACTIVE_COUNTRY US
#if ACTIVE_COUNTRY = = US
char currency[ ]="dollar; "
#elif ACTIVE_COUNTRY= =ENGLAND
char currency[ ]="pound; "
#else
char currency[ ]="franc;"
#endif
#if与#elif呼吁大概一直嵌套到实现划定的权限,个中#endif、#else或#elif与最近#if或#elif关联。譬喻,下面措施是完全有效的。
#if MAX>100
#if SERIAL_VERSION
int port=198;
#elif
int port=200;
#elif
#else
char out_buffer[100];
#endif
2. # ifdef 和# ifndef
条件编译的另一种要领是用#ifdef与#ifndef呼吁,它们别离暗示“假如有界说”及“假如无界说”。
# ifdef的一般形式是:
# ifdef macroname
statement sequence
#endif
假如宏名在前面# def i n e语句中已界说过,则该语句后的代码块被编译。
#ifndef的一般形式是:
#ifndef macroname
statement sequence
#endif
假如宏名在#define 语句中无界说,则编译该代码块。
#ifdel 与#ifndef可以用于#else 语句中,但#elif 不可。拜见4 -1 5。
上述代码打印“ Hi Ted ”及“ RALPH not defined”。假如T E D没有界说,则显示“ H i a n y o n e”,后头是“ RALPH not defined”。
可以像嵌套#if 那样将#ifdef 与#ifndef 嵌套至任意深度。
4.7.6 #undef
呼吁#undef 打消其后谁人前面已界说过有宏名界说。一般形式为:
#undef macroname
譬喻:
# define LEN 100
#difine WIDTH 100
char array[LEN][WIDTH];
# undef LEN
# undef WIDTH
/ *at this point both LEN and WIDTH are undefined * /
直到碰着#undef 语句之前, L E N与W I D T H均有界说。
# undef 的主要目标是将宏名范围在仅需要它们的代码段中。
4.7.7 #line
呼吁# line改变_LINE_ 与_ F I L E _的内容,它们是在编译措施中预先界说的标识符。
呼吁的根基形式如下:
# line number["filename"]个中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源措施中当前行号,文件名为源文件的名字。呼吁# line主要用于调试及其它非凡应用。
譬喻,下面说明行计数从1 0 0开始;printf( ) 语句显示数1 0 2,因为它是语句#line 100后的第3行。
#line 100 /* 初始化行计数器* /
main ( ) /* 行号100 */
{ /* 行号101 */
p r i n t f ( " % d \ n " ,_ line _ ) ; /* 行号102 */
}
4.7.8 #pragma
呼吁#pragma 为实现时界说的呼吁,它答允向编译措施传送各类指令。譬喻,编译措施大概有一种选择,它支持对措施执行的跟踪。可用# p r a g m a语句指定一个跟踪选择。
4.7.9 预界说的宏名
A N S I尺度说明白五个预界说的宏名。它们是:
_ line _
_ F I L E _
_ D A T E _
_ T I M E _
_ S T D C _
假如编译不是尺度的,则大概仅支持以上宏名中的几个,或基础不支持。记着编译措施也许还提供其它预界说的宏名。
_ line _及_ F I L E _宏指令在有关# line的部门中已接头,这里接头其余的宏名。
_ D AT E _宏指令含有形式为月/日/年的串,暗示源文件被翻译到代码时的日期。
源代码翻译到方针代码的时间作为串包括在_ T I M E _中。串形式为时:分:秒。
假如实现是尺度的,则宏_ S T D C _含有十进制常量1。假如它含有任何其它数,则实现长短尺度的。
留意:宏名的书写由标识符与双方各二条下划线组成。
4.7.10 注释
在C语言中,所有的注释由字符/ *开始,以* /竣事。在星号及斜杠之间不答允有空格。编译措施忽略注释开始符到注释竣事符间的任何文本。譬喻,下面措施在屏幕上只打印
“h e l l o”。
main ()
{
p r i n t f ( "hello" ) ;
/*printf ("This is a sample to print hell;o"*/)
}
注释可呈此刻措施的任何位置,但它不能呈此刻要害字或标识符中间。
即,注释x=10+ /*add the numbers */ 5;是有效的,但swi/* this will not work */tch(c){…
是不正确的,因为C的要害字不能含有注释。凡是也不但愿表达式中间呈现注释,因为这会使
意义含混不清。
注释不行嵌套,即一个注释内不行含有另一个注释。譬喻,下面代码段在编译时堕落:
/*this is an outer comment
x = y / a ;
/*this is an inner comment -and causes an error */
* /
当需要表明措施的行为时,注释应简明简要。除了最简朴和最直观的函数外,都应有注释,在函数开始处说明其成果,如何挪用以及返回那里。