宏、条件编译、文件包含等预处理命令以及位段的相关概念
所有预处理命令都以“#”开头,每个预处理命令必须单独占一行,语句末尾不适用分号作为结束符。一般将预处理语句放在源程序的首部。预处理语句主要有宏、条件编译和文件包含。
1、宏(只做简单替换——注意计算顺序)
宏是一种预处理命令,根据是否带参数,可以分为无参宏和带参宏。
(1)无参宏
无参宏定义语句的一般格式如下:
#define 标示符 字符串
无参宏是用一个简单的标示符代替一个长的字符串。这个标示符称为宏名,在预编译时将宏名替换为字符串的过程称为宏展开或宏替换。
宏名习惯上用大写字母来表示,以与变量相区别。因为宏定义是用一个宏名代替一个字符串,所以只做简单的替换,不做语法检查。#define命令一般出现在程序中所有函数的外面,宏名的有效范围是从定义命令开始到本文件结束,但可以用#unfef命令终止宏定义的作用域。
(2)带参宏
带参宏定义语句的一般格式如下:
#define 标示符(标示符1,标示符2,...,标示符n) 字符串
其中,括号中的标示符表示实际参数。对带参宏的展开也是用“字符串”代替宏名“标示符”。
例如:下列语句定义了一个求正方形面积的宏:
#define area(a)((a)*(a))
为什么要加上括号呢?这是因为“替换”只是简单的替换操作,当area的实际参数是一个表达式时,不加括号会在编译时出错。如#define area(a)(a*a)当调用area(2+3)时,替换成(2+2*3+3),那么所求的面积是11,而不是25。
带参宏和函数在形式和使用上都很相似。例如下面定义了一个求两个数中较大值的宏:
#define max(a,b) ((a)>(b)?(a):(b))
与下面的函数等价:
int main()
{
return((a)>(b)?(a):(b));
}
带参宏与函数的区别如下:
a、调用函数时,先求出实参表达式的值,然后将其带入到函数定义的形参;而使用带参宏只是进行简单的字符替换,不进行计算。
b、函数调用是在程序运行时处理的,会非配临时的存储单元;而宏扩展则是在编译之前进行的,在展开式不分配内存单元,不进行值的传递处理,也没有返回值的概念。
c、对函数中的实参和形参都要求定义类型,且两者的类型要求一致,如不一致则进行类型转换;而宏不存在类型问题,宏名无类型,它的参数也无类型,只是一个代替符号,展开时带入指定的字符即可。
d、调用函数只得到一个返回值,而使用宏可以得到几个结果。
e、使用宏次数较多时,宏展开后源程序变长;而函数调用不是程序变长。
f、宏替换不占运行时间,只占编译预处理时间,而函数调用则占运行时间。
C语言的预处理命令不是C语句,C语句都是用分号结尾的。
在宏定义#define PI 3.14159中,用宏名PI代替一个字符串。
#define S(a,b) a*b area=S(3,2);第一步被换为area=a*b; ,第二步被换为area=3*2;
2、条件编译
一般情况下,C语言中的所有的行都参加编译过程。但有时处于对程序代码优化的考虑,用户希望对其中的一部分内容只是在满足一定条件时才进行编译并形成目标代码。这种通过条件指定编译的部分代码称为条件编译代码。常见的形式如下:
(1)形式一
#if 常数表达式
程序段1
#else
程序段2
#endif
或者:
#if 常数表达式
程序段1
#endif
(2)形式二
把形式一中的:#if变为#ifdef;该语句的作用是#ifdef后的宏名在此之前已用#define语句定义,就编译程序段1;否则......
常数表达式变为宏名;
(3)形式三
把形式二中的:#ifdef变为#ifndef;#ifndef的功能与#ifdef功能相反。
3、文件包含
所谓文件包含预处理,是指在一个文件中将另一个文件的全部内容包含进来的处理过程,即将另外的文件包含到本文件中。其一般格式为:
#include <包含文件名> //直接到指定的标准包含文件目录去寻找文件
或#include“包含文件名” //现在当前目录寻找,如果找不到再到标准包含文件目录去找
包含文件名又称头文件或编译预处理文件。
文件包含预处理的功能是,在对源文件进行编译之前,用包含文件的内容取代该文件包含预处理语句。
4、位段