预处理语句是由一系列和预处理相关的命令符组成的。预处理语句以“#”作为起始标志,其后紧跟预处理命令关键字,之后是空格,空格之后是预处理命令的内容。C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。合理使用预处理功能编写的程序模块有助于阅读、修改、移植和调试,且有助于模块化程序设计。C语言预处理程序的作用是根据源代码中的预处理指令修改源代码。预处理指令是一种命令语句(如#define),用以指示预处理程序如何修改源代码。在对程序进行通常的编译处理之前,编译程序会自动运行预处理程序,对程序进行编译预处理,这部分工作对程序员来说是不可见的。
宏是预处理命令的一种形式,之所以单独介绍它,是因为其使用非常普遍。宏的完整名称是“宏替换”,在ISO/IEC9899国际标准“Programming Langeuage—C”中和ISO/IEC14882国际标准“Programming Langeuage—C++”中,均对宏进行了详细的描述。
1.“#”和“##”
(1)“#”符号的使用
“#”是预处理命令的标志符号。示例如下:
“#”符号还有一个功能,就是将跟在其后的参数转化成一个字符串。例如,
上述程序段代码的执行结果为:adhfkj15。
请读者体会宏定义语句中包含的“#n”的含义。
(2)“##”符号的使用
“##”也是预处理命令的标志符号,但是不允许出现在宏定义语句的开始和末尾。“##”字符串可以将两个独立的字符串连接成一个字符串。例如,
上述程序段代码的执行结果为:
提示
“#”就是特殊语句(预处理语句)的标志符号;“##”是字符连接标识。
2.常见预处理命令
(1)定义变量和取消定义变量
预处理程序段是通过define和undef命令实现定义变量和取消定义变量的。define其实是宏定义命令,应该放在后面的1.13节讲解,但是鉴于它在预处理语句中的重要性,这里先对其进行简单介绍。
取消定义变量(取消宏定义)的命令是undef。其作用是取消该命令前面的程序段中使用define定义的宏变量。在例1-13的Example 1中,使用#define命令定义宏变量PI为3.14159,程序最后使用undef取消宏变量PI的定义。在Example 2中,在main()函数中,先定义宏变量MAX等于200,并将其输出到屏幕上,之后取消前面的定义;重新定义宏变量MAX等于150,重新输出宏变量MAX。Example 2的执行结果如图1-8所示。
例1-13
图1-8 例1-13的程序执行结果
(2)条件预处理语句
一般情况下,程序的每一行源代码都是要编译的。特殊情况下,只有在满足一定条件时才编译某部分内容。条件不同,编译的程序部分不同,即产生不同的功能,即条件编译。常用条件编译的关键字主要有#if、#ifdef、#ifndef、#else、#elif和#endif。这些关键字的常见组合一般如下:
第一种形式:
以上形式的功能是当表达式的值为“真”(非零)时,编译程序段1;否则,编译程序段2。
例1-14
第二种形式:
以上形式的功能是:当所指定的标识符已经被#define定义时,仅编译程序段1;否则编译程序段2。
例1-15
第三种形式:
以上形式的功能和第二种形式相反,即表示当标识符没有被#define语句定义时,仅编译程序段1;否则,仅编译程序段2。
例1-16
(3)包含头文件命令
关键字include是最常见的预处理命令之一。其功能是将被包含的文件中的源代码“放进”源文件中,从而实现代码重用,避免编程者大量的工作。最常见的形式为:
例如,
或
上述语句的意思是将标准库文件stdio.h包含进源文件中。stdio.h文件内主要是声明了一些和标准输入输出相关的变量和函数。
在使用文件包含命令时,主要注意避免的是重复包含同一个头文件。为实现此目的,程序员可以利用条件预处理语句和include语句相结合的方法。例如,
上述代码的功能是:若VERSION等于1,则定义宏变量INCFILE为“vers1.h”;若VERSION等于2,则定义宏变量INCFILE为“vers2.h”;若VERSION等于其他值,则定义宏变量INCFILE为“versN.h”。最后使用包含语句“#include INCFILE”将头文件INCFILE包含进源文件中。
(4)#line预处理命令
“#line”属于C/C++语言的预处理命令。其作用是修改代码行的行号。
在正常情况下,程序源代码的行号是逐次增加的,从开始直到程序结尾。如果程序的源代码总共包含100行,在正常情况下程序的行号应该是1~100,并且是顺序递增的。“#line”预处理命令可以改变程序源代码的行号以及编译源文件的名字。其主要用法有以下两种:
提示
在C/C++的编译系统中,_LINE_和_FILE_是预先定义的两个宏变量,代表了被编译源文件的当前行和当前文件名,#line命令就是改变这两个宏的值。
常见的预定义宏还包括_LINE_、_FILE_、_DATE_、_TIME_和_STDC_。例如,
上段程序代码执行结果为:
(5)特殊命令预处理(“error”“pragam”和“NULL”)
本小节讲述预处理命令“error”“pragam”和“NULL”。
1)预处理命令“error”。error预处理命令可以强制编译程序停止编译,并给出提示信息。如果执行该#error语句,程序不再向下执行,更不用说链接产生可执行文件了。
例1-17(www.xing528.com)
以上程序段的功能说明:如果没有定义宏“__DOS”,#error语句提示“DOS OS is re-quired.”,即程序编译无法成功。如果定义了宏“__DOS”,程序将顺利编译成功。
2)预处理命令“#pragma”。#pragma命令可以指定特殊的指令给编译器。
每个编译程序都可以通过#pragma命令激活或终止该编译程序所支持的一些编译功能。#pragma预处理命令的使用方法如下:
参数paramter是命令的具体参数,具有多种形式。paramter常见的使用形式如下:
•message。
•argsused。
•exit/startup。
•inline。
•once。
•warn。
•code_seg和data_seg。
•resource。
•saveregs。
•hdrstop/hdrfile。
下面分别详细讲解。
①#pragma message。
说明:#pragma message命令适用于提示一些有用信息,于程序编译过程中,在输出编译信息窗口的同时输出这些信息。
例1-18
②#pragma argsused。
说明:#pragma argsused命令仅允许出现在函数定义之间,且仅影响下一个函数,使警告信息被禁止或无效。如果函数的某一个参数在该函数内没有被使用,编译系统可能会有相应的提示信息。如果使用了#pragma argsused预处理命令,这种警告信息就不会出现了。
③#pragma exit和#pragma startup。
说明:#pragma startup命令可以实现设置程序启动之前需要执行的函数;#pragma exit命令可以实现设置程序退出之前需要执行的函数。
例1-19
上述程序代码的执行结果为:
由此可见:程序执行时,先执行了fun_start()函数,之后才执行了main()函数,当程序即将退出时,执行了fun_end()函数。
④#pragma inline。
说明:在Turbo C++中,使用参数inline,而在VisualC++中是使用参数auto_line、in-line_depth和inline_recursion。#pragmainline可以指定一个C/C++函数是否要被内联,并且要指定被内联或者被不内联的函数。被调用的内联函数的代码会直接嵌入调用函数的源代码中。
⑤#pragma once。
说明:使用once参数可以实现仅编译一次该头文件。一般#pragma once放在头文件的最开始。
⑥#pragma warning。
说明:参数warning(有的C版本为warn)可以实现设定提示信息的显示与否以及如何显示。例如,
a)#pragma warning(disable:450734)。
功能:不显示4507和34号警示信息。
b)#pragma warning(once:4385)。
功能:仅显示一次4385号信息。
c)#pragma warning(error:164)。
功能:将164号警告信息作为一个错误信息显示。
d)#pragma warning(push)。
功能:保存所有警告信息的现有警告状态。
e)#pragma warning(pop)。
功能:从栈中弹出最后一个警告信息,在入栈和出栈之间所做的一切改动取消。
⑦#pragma code_seg和#pragma data_seg。
说明:code_seg用于指定函数要存放的代码段。data_seg用于指定数据存放的代码段。
a)完整的code_seg使用方法。其形式如下:
在.obj文件中默认的存放节为.text节,如果code_seg没有带参数使用,函数就存放在.text节中。push(可选参数)将一个记录放到内部编译器的堆栈中,可选参数可以为一个标识符或者节名,pop(可选参数)将一个记录从堆栈顶端弹出,该记录可以为一个标识符或者节名。当使用push指令时,identifier(可选参数)为压入堆栈的记录指派的一个标识符,当该标识符被删除时,与其相关的堆栈中的记录将被弹出堆栈,“segment-name”(可选参数)表示函数存放的节名。例如,
b)完整的data_seg使用方法。
#pragma data_seg预处理命令主要用于数据共享,尤其是在Windows操作系统中,多个进程之间共享数据,可以使用#pragma data_seg预处理命令实现。共享的数据既可以是单个的数据,也可以是数组、向量等。必须注意的是,共享数据在数据段中必须初始化,否则可能引起错误。该预处理命令常应用于DLL动态链接库中。其使用方法如下:
⑧#pragma resource。
说明:将制订的资源文件加入工程中。例如,
⑨#pragma saveregs。
说明:#pragma saveregs预处理命令保证调用huge()函数时不会改变任何寄存器的值。如果在C语言中可能要嵌入汇编语言代码,就可能用到本命令。该命令应该放在某个函数定义之前,并且仅适用于这个函数。
⑩#pragma hdrstop。
说明:#pragma hdrstop预处理命令用于结束预编译头文件列表,表示预编译头文件到此为止,后面的头文件不再进行预编译。通过使用该命令来减少预编译头文件所占用的磁盘空间。
(11)#pragma hdrfile。
说明:#pragma hdrfile预处理命令用来指定保存预编译头文件的文件的名字。在Turbo C++中默认为TCDEF.SYM。如果不使用预编译头文件,本命令无效。例如,
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。