a)基本概念
①以"#"开头的预处理命令。如包括#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,他们称为预处理部分。
②所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法扫描分析)之前所作的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
③C语言提供了很多种预处理功能,如宏定义、文件包含、条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。
④
↓ 源程序
预处理(宏替换)
↓ ↓C
编译 ↓语
↓ .o ↓言
链接 ↓执
↓ ↓行
↓ .out ↓流
执行程序 ↓程
2)宏的概念及无参宏定义方法
a)红的概念
①被定义为“宏”的标示符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这成为“宏代换”或“宏展开”。
②宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参宏和无参宏两种。
③无参宏的宏名之后不带参数。
一般形式为:
#define 标示符 字符串
注意:预处理指令,通常写作函数之前
④宏替换
源程序在编译之前,由预处理程序对我们写的源代码进行处理:会把源代码中所有出现宏名的地方使用宏的字符串去替换。
#define M 10
#define M1 y*y+3*y
void main(){
int a[M+2];
printf("%d\n",M);
int y=3,result=0;
result=3*M1+2*M1-50;
//分析:
//错误理解:
//3*(y*y+3*y)+2*(y*y+3*y)-50
// 54+ 36-50
// =40
//上述结果算出为40,结果是错误的。
//正确理解:只是出现宏名的地方用字符串替换,不会加括号
//3*y*y+3*y+2*y*y+3*y-50
// 27+ 9+ 18+ 9-50
// =13
}
⑤注意点:
a)习惯上宏名用大写字母表示,以便于与便利区别。但也允许小写字母。
b)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,,这只是一种简单的代换,字符串中可以含任何字符,可以是常量,也可以是表达式,预处理程序对它不做任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
c)宏定义不是说明或语句,在行末不必加分号,如加分号则连分号也一起置换。
d)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域可使用#undef命令。
3)有参宏定义和使用方法
a)有参宏的定义方法
①C语言允许 宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:
#define 宏名(形参表)字符串
在字符串中含有各个形参。
②宏的分类:
①无参宏:#define M 10
②有参宏:#define SUM(a) a+10 //SUM(3) 不仅仅要 a+a替换,而且还要把实参3代入到字符串中
③有参宏示例代码:
#include<stdio.h>
#define SUM(a) a+a
void main(){
int result=SUM(3);
printf("%d\n",result);//输出6
}
④有参宏的使用注意事项:
aa)带参宏定义中,形参之间可以出现空格,但是宏名和形参之间不能出现空格。
bb)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用他们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各自有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。
cc)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
dd)在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
ee)宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。
⑤应用:使用有参宏求最大值
代码示例:
#include <stdio.h>
//定义一个有参宏
#define MAX(a,b) a>b?a:b
void main(){
int m=MAX(324,2653);
printf("%d\n",m);//输出2653
}
4)typedef 和 #define 的区别
a)区别:
用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef是在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。
#define DINT int* //宏定义
typedef int * TINT; //重新定义类型
代码示例:
#define DINT int* //宏定义
typedef int * TINT; //重新定义类型
void main(){
int m=2,n=5;
DINT a,b;
a=&m;//正确
b=&n;//错误
DINT c,d;
c=&m;//正确
d=&n;//正确
}
5)条件编译的概念和优点
a、为什么要使用条件编译
①按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。有利于程序的移植和调试。
②条件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。
b、格式为:
#if 常量表达式1
程序段1
#elif 常量表达式2
程序段2
#else
程序段3
#endif
c、功能:如常量表达式1的值为真(非0),则对程序段1进行编译,如常量表达式2的值为真(非0),则对程序段2进行编译,否则对程序段3进行编译。因此可以使程序在不同条件下,完成不同的功能。
6)#ifdef和ifndef 条件编译指令
a、ifdef
①格式为:
#ifdef 标识符
程序段1
#else
程序段2
#endif
②功能:如果标识符已被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。
b、ifndef格式为:
#ifndef 标识符
程序段1
#else
程序段2
#endif
与ifdef的区别是将“ifdef”该成“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与“#ifdef”功能刚好相反。