编译预处理是指在C++编译系统对C++源程序进行正式编译之前,由编译预处理程序对源程序中的预处理命令进行处理的过程。常用的预处理命令有:#define、#include、#ifdef、#ifndef、#else及#endif等,C++程序中引入预处理命令的主要目的是为了改进程序设计环境、提高编程效率。预处理命令不是C++本身组成成分,所以不能直接对其进行编译,而只能“抢”在源程序编译之前作一些“特殊处理”,将预处理命令“翻译”成C++程序能识别的语句。
预处理命令书写要求:
1.为了与C++语句相区分,命令必须“#”打头,#是命令动词的组成成分。
2.命令动词区分大小写。
3.预处理命令不是C++语句,书写结束后不能加分号。
4.一般地,预处理命令单独占一行,可以放在程序的开头、中间或末尾等任何位置。
宏
宏的定义:#define 宏名[(形参列表)][字符串]
源程序开始编译前,将所以引用的宏名“替换”成对应的字符串,然后再编译源程序
举例:
#define array_size 100
#define NOT !
#define AND &&
#define OR ||
#define S(r) 3.14*(r)*(r)
注:1.宏名一般用大写字母表示
2.宏分为带参宏和无参宏。带参宏中,当形参为多个时,其间用逗号“,”隔开,每个形参即为一个标识符(一般为变量)。
3.字符串即为替换宏名的一串字符串(替换后不一定解析为字符类型),包括空格、反斜杠、引用
号等。若为带参宏,一般还应含有“形参”。书写时无需双引号定界。
4.如果在字符串中含有运算符,需要注意替换后的运算优先级,为了达到“预期运算顺序”,通常在字符串的相应位置加括号。如:
#define Pl 2+1.14
#define Pl2(2+1.14)
int r(3);
float s;
s=Pl*r*r; //替换后s=2+1.14*3*3
s=Pl*r*r; //替换后s=(2+1.14)*3*3
5.若“字符串”省略,仅定义宏名。
6.宏的定义可以嵌套,即一个宏定义的字符串中出现另一个宏名,引用是层层替换。如:
#define A 10
#define B A*10
cout<<B+10; //输出110
7.宏定义中,是用字符串对宏名作“机械”的置换或参数替换,不作合法性检查。如:
#define Pl 3.l4
float s=Pl*3*3; //出错
#define DIV(a,b) a/b
cout<<DIV(3.0,0); //出错
8.宏一旦定义,便可以使用,而且宏引用的每处在预处理时都替换成相同的字符串,除非重新定义,永不改变,所以宏名又称作“符号常量”。
9.如果在程序中多处使用同一“字符串”,为了简化程序源代码一般将其定义成宏。
10.宏不是变量,宏定义不分配内存空间。
宏的使用
宏的使用,就是在程序中引用宏。
如果宏名出现在字符串常量中,将不视为宏引用,即不对其进行宏替换。
#define Pl 3.14
cout<<"Pl="<<Pl<<endl; //输出Pl=3.14
如果宏名作为标识符部分成份出现,将不视为宏引用。如:
#define Pl 3.14
float Pl2=3.14159;
cout<<Pl2<<endl; //输出3.14159 而不是3.142
若引用的宏为带参宏,则实参与形参的个数应相等。
宏引用根据宏的定义位置是有作用域的。作用域从定义点开始到本程序单元或命令行“#undef宏名”结束。
在程序中出现宏,称为“宏引用”或“宏调用”;在源程序编译前,将程序中的宏名替换成对应的字
符串,称为“宏展开”或“宏替换”
#include<iostream>
using namespace std;
#define max2(x,y) (x)>(y)?(x):(y)
#define max3(x,y,z) max2(max2(x,y),z)
int main()
{
int a, b, c, max;
cout << "a="; cin >> a;
cout << "b="; cin >> b;
cout << "c="; cin >> c;
max = max3(a, b, c);
cout << "MAX=" << max << endl;
return 0;
}
#include<iostream>
using namespace std;
#define INPUT cin>>a>>b>>c
#define OUTPUT cout<<a<<b<<c<<endl
int main()
{
int a, b, c;
INPUT;
OUTPUT;
return 0;
}
宏与函数的区别
宏允许嵌套定义;而函数不能。
函数中的形参与实参要求有数据类型;而带参宏的形参没有数据类型,只是一个符号而已,实
参从理论上讲可以为语法范围内的任何一种数据类型。
函数调用是先求出实参的值,然后再传递给形参;而带参宏引用只是进行“简单”的实参替换形参。
函数调用是在程序运行时进行,并且为形参分配内存单元;而宏展开是在源程序编译前进行,不为其分配内存单元,不进行值的传递,也不存在返回值。
是宏展开会增加源程序代码;而函数调用保持源程序代码不变。
宏替换不会占用程序运行时间,仅延长编译时间;而函数调用要占用程序运行时间,用于分配内存单元、值传递过程消耗等。
文件包含
文件包含是指在一个C++文件中包含另一个C++文件的全部内容。一般地,用C++写程序往往不只是一个文件,而为了提高程序的编写效率,更为了引用其他文件中的功能模块,由此引入文件包含
#include<文件名>
#include"文件名"
源程序开始编译前,将指定“文件”中的内容替换对应包含命令,使文件中的全部内容成为C++程序文件中的一部分。
注:1遇被包含文件名用于指定C++程序引用的文件,是一个文本文件,又称“头文件”或“标题文件”
一般地,C++系统头文件不带扩展名,C式系统头文件扩展名为.h。C++中的系统头文件般包括函数原型、宏定义、结构体定义、全局变量定义、类定义和对象定义等.
2.如果被包含文件被修改,凡包含此文件的所有C++程序文件都要全部重新编译.
3.用尖括号引用被包含文件,系统到编译系统指定的文件夹中寻找被包含的文件(标准方式)。“尖括号”方式一般用于包含系统提供的头文件。
4.用双引号引用被包含文件,系统先到当前文件夹中寻找被包含的文件,若找不到,到编译系统指定的文件夹中寻找。“双引号”方式一般用于包含用户自定义的头文件。一般地,双引号之间可以指定包含头文件的路径,用于指定包含头文件的位置。
5.一个#include命令只能指定一个被包含文件,如果要包含多个文件,要用多个#include命令。
6.在一个被包含文件中可以包含另一个被包含文件,即文件包含可以嵌套。
7.#include命令通常位于C++程序文件开头。
条件编译
一般情况下,源程序中的所有代码行都参加编译。而条件编译是指源程序中某段代码通过条件来决定是否参加编译。在大型应用程序中,可能会出现某些功能不需要的情况,这时就可以利用条件编译来选取需要的功能进行编译,以便生成不同的应用程序,供不同用户使用。此外,条件编译还可以方便程序的逐段调试,简化程序调试工作。
格式1:
#if 表达式
程序段A
[#else
程序段B]
#endif
判断表达式的值,若为真(非0)则编译程序段A;否则编译程序段B。
#include<iostream>
using namespace std;
#define N 5
#define FLAG true
int main()
{
int a[N], m, * p = a, i = 1;
while (p < a + N)
{
cout << "请输入第" << i++ << "个整数:";
cin >> *(p++);
}
p = a;
#if FLAG
m = *p;
while (++p < a + N)
if (m < *p)
m = *p;
cout << "MAX=" << m << endl;
#else
m = *p;
while (++p < a + N)
if (m > *p)
m = *p;
cout << "MIN=" << m << endl;
#endif
return 0;
}
#include<iostream>
using namespace std;
#define LETTER false
int main()
{
char* str = "C++ Language Program", c;
int i = 0;
while ((c = *(str + i)) != '\0')
{
i++;
#if LETTER
if (c >= 'a' && c <= 'z')
c -= 32;
#else
if (c >= 'A' && c <= 'Z')
c += 32;
#endif
cout << c;
}
cout << endl;
return 0;
}
格式2:
#ifdef 标识符
程序A
[#else
程序B]
#endif
若标识符是已定义过的宏名,则编译程序A,否则编译程序B
格式3:
#ifndef 标识符
程序A
[#else
程序B]
#endif
若标识符不是已定义过的宏名,则编译A,否则编译程序B
格式3和格式2功能恰恰相反