------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------
预处理指令
1、基本概念
以“#”号开头的预处理命令。如包含命令#include,宏定义命令#define等。在源程序中这些命令都放在函数之外,而且一般都放在源程序文件的前面,它们称为预处理部分。
所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所做的工作。预处理是C语言的一个重要功能,它由预处理程序负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进入对源程序的编译。
C语言提供了许多预处理功能,如宏定义、文件包含、条件编译等。合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。
2、宏的概念及定义方法
被定义为“宏”的标示符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。
无参宏的宏名后不带参数,其定义的一般形式为:
#define 标识符 字符串
其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。
3、无参宏使用注意事项
1)习惯上宏名用大写字母表示,以便于与变量区别。但也允许用小写字母
2)宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不做任何检查。如有错误,智能在编译已被宏展开后的源程序是发现。
3)宏定义不是说明或语句,在行末不必加分号,如果加上分号则连分号也一起置换。
4)宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束。如果要终止其作用域可使用#undef命令。
5)宏名在源程序中若用“”括起来,则预处理程序不对其作宏代换。
6)宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在展开时由预处理程序层层代换。
7)可用宏定义表示数据类型,使书写方便。
8)对“输出格式”作宏定义,可以减少书写麻烦
注意括号问题
/*
宏:
C语言中我们自定义的特殊标示符,习惯大写
宏的定义:
#define 宏名 宏字符串(可以是常量、变量、表达式)
注意:预处理指令,经常写在函数之前
宏不是一个语句,是一个预处理指令,所以不需要加分号结束
3、宏替换
源程序在编译之前,由预处理程序对我们写的源代码进行处理:会把源代码中所有出现 宏名 的地方一律使用 宏的字符串 去替换
*/
#include <stdio.h>
#define M 10
#define M1 y*y+3*y
#define R 4
#define PI 3.14
#define AREA PI*R*R //嵌套定义
#define INT1 int
#define P struct Person
void test(){
printf("M = %d\n",M);
}
//#undef M //此处的作用是,取消宏定义
void test1(){
printf("test1 = %d\n",M);
}
int main(int argc, const char * argv[]) {
int a[M+2]; //int a[12]
printf("%d\n",M); //把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
//正确的
// 3*y*y+3*y+2*y*y+3*y-50;
// 27 +9 + 18 + 9 -50;
// 13
printf("result = %d\n",result);
//宏使用的注意事项
//1、宏是有作用域的 #undef 宏名 可以取消宏定义
test();
test1();
//2、在字符串中出现的宏名不会被替换
//3、宏可以嵌套定义
printf("%.2f\n",AREA);
//4、使用宏其别名
INT1 a1;
a1 = 10;
printf("a1 = %d\n",a1);
P{
int age;
};
P p1 = {23};
return 0;
}
4、有参宏的定义方法
C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。
对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参。
带参宏定义的一般形式为:
#define 宏名(形参表) 字符串
在字符串中包含有各个形参。
带参宏调用的一般形式为:
宏名(实参表)
5、有参宏使用注意事项
1)带参宏定义中,形参之间可以出现空格,但是宏名和形参之间不能有空格出现。
2)在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用他们去代换形参,因此必须作类型说明。这是与函数中的情况不同的。在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而在带参宏中,只是符号代换,不存在值传递的问题。
3)在宏定义中的形参是标识符,而宏调用中的实参可以是表达式。
4)在宏定义中,字符串内的形参通常要用括号括起来以避免出错。
5)宏定义也可以用来定义多个语句,在宏调用时,把这些语句又代换到源程序内。
/*
宏的分类:
无参宏 #define M 10
有参宏 #define SUM(a) a+a
SUM(3) //不仅要 a+a替换,而且还要把 实参3代入到 字符串中
*/
#include <stdio.h>
#define SUM(a) a+a
#define M1(x ,y) (x)*(y)+(x)+(y)
#define M2(a) a+3*y
#define M3(m,n) m = a+2;n=a*2;
int main(int argc, const char * argv[]) {
int result = SUM(3); //6
//有参宏使用
result = M1(4, 5); //29
int y = 2;
//有参宏使用
result = M2(3); //9
//有参宏的使用注意事项
//1、宏的形参之间可以出现空格,但是宏名和形参之间不能出现空格
int a = 3;
//2、有参宏宏的参数最好用括号括起来
result = M1(a+3, a-1);
//(x ,y) x*y+x+y
// 6*2 +6+2
// 20
// a+3*a-1+a+3+a-1
// a+9 -1+a+3+a-1
// 3+9-1+3+3+3-1
// 19
int i,j;
printf("%d\n",result);
printf("i=%d,j=%d\n",i,j);
//3、可以用宏来定义多个语句
M3(i, j);
printf("i=%d,j=%d\n",i,j);
return 0;
}
6、#define和typedef的区别
应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。宏定义只是简单的字符串代换,是在预处理完成的,而typedef视在编译时处理的,它不是作简单的代换,而是对类型说明符重新命名。被命名的标识符具有类型定义说明的功能。
// typedef和#define的区别
#include <stdio.h>
#define INT1 int* //定义一个宏,宏名是INT1
typedef int* TINT; //int起一个别名 TINT
int main(int argc, const char * argv[]) {
int num = 10;
//使用宏定义变量
INT1 a,b; //int* a,b
//a是指针
a = #
//b是普通的变量
b = num;
printf("a = %d,b = %d\n",*a,b);
//使用别名定义变量
TINT a1,b1; //int *a1;int* b1;
a1 = #
b1 = # //a1 和 b1 都是指针
printf("a1 = %d,b1 = %d\n",*a1,*b1);
return 0;
}
7、条件编译—为什么要使用条件编译
1)按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。有利于程序的移植和调试。
2)条件编译当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。
8、#if-#else条件编译指令
第一种形式的格式为:
#if 常量表达式
程序段1
#else
程序段2
#endif
它的功能是,如常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能。
注意:条件编译后面的条件表达式中不能识别变量,它里面只能识别常量和宏定义。
9、#ifdef条件编译指令
第一种形式的格式为:
#ifdef 标识符
程序段1
#else
程序段2
#endif
它的功能是,如果标识符已被#define命令定义过,则对程序段1进行编译;否则对程序段2编译。
如果没有程序段2(它为空),本格式中#else可以没有,即可以写为:
#ifdef 标识符
程序段
#endif
我们常利用这种形式来控制调试信息:
10、#ifndef条件别意指令
第三种形式的格式为:
#ifndef 标识符
程序段1
#else
程序段2
#endif
// 使用条件编译指令调试bug
#include <stdio.h>
#define DEBUG1 1
#if DEBUG1 == 1
//显示调试信息
#define Log(format,...) printf(format,## __VA_ARGS__)
#else
//不显示调试信息
#define Log(format,...)
#endif
void test(){
int a = 10,b=3;
Log("test--->%d,%d\n",a,b);
}
void test1(){
printf("xxxxx\n");
float b = 10.23f;
Log("test1--->%.2f\n",b);
}
int main(int argc, const char * argv[]) {
//DEBUG1 == 1 显示调试信息
//DEBUG1 == 0 不显示调试信息
Log("xxxxxxxxxx-->\n");
test1();
test();
return 0;
}