C语言预处理命令

预处理的概念

以"#"号开头的就是预处理命令,在源程序中这些命令都放在函数之外,而且一般都放在源文件的前面,它们称为预处理部分
        所谓预处理是指在进行编译的第一遍扫描(词法扫描和语法分析)之前所作的工作,预处理是C语言的一个重要功能,它由预处理程序负责完成.当对一个源文件进行编译时,系统将自动引用预处理程序对源程序中的预处理部分作处理,处理完毕自动进行对源程序的编译
        C语言提供了多种预处理功能,如:宏定义、文件包含、条件编译等.合理地使用预处理功能编写的程序便于阅读、修改、移植和调试,也有利于模块化程序设计。

宏及条件编译:

宏的概念

被定义为"宏"的表示符称为"宏名"。在编译预处理时,对程序中所出现的"宏名",都用宏定义中的字符串去代换,这称为"宏代换“或”宏展开"
        宏定义是由源程序中的宏定义命令完成的,宏代换是由预处理程序自动完成的.在C语言中,"宏分为有参和无参数两种"
        
        无参宏的宏名后不带参数,其定义的一般形式为:
            #define 标示符 字符串
 
            # 表示这是一条预处理命令,凡是以"#"开头的均为预处理命令
            define 为宏定义命令
            标示符  为所定义的宏名
            字符串  可以是常数、表达式、格式串等

注意事项:

 1.习惯上宏名用大写字母表示,以便于与变量区别,但也允许用小写字母
 
         2.宏定义时用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单地代换,字符串中可以含任何字符,可以是常量,也可以是表达式,预处理程序对它不作任何检查.如有错误,只能在编译已被宏展开后的源程序时发现
         3.宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换
         4.宏定义必须写在函数之外,其作用域为宏定义命令到源程序结束.如要终止其作用域可使用
            #undef命令
         5.宏定义允许嵌套
            #define R 4
            #define PI 3.14
            #define AREA R*PI //嵌套定义
         6.宏名在源程序中若用 "" 括起来,则预处理程序不对其做宏代换
         7.可用宏定义表示数据类型,使书写方便
            #define INT int
            INT a;
            a=10; //相当于用宏给 int 起了一个别名
 
            #define P struct Person
            P{
                int age;
            };
            P p1= {24};

有参宏的定义方法和使用

C语言允许宏带有参数.在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数
        对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参
 
        带参宏定义的一般形式为:
            
            #define 宏名(形参1,形参2...) 字符串
        带参宏调用的一般形式为:
            宏名(实参1,实参2..);
        
        例:
            #define SUM(a) a+a
 
            int result=SUM(3);
            printf("%d \n",result);

有参宏使用的注意事项

1.带参宏定义中,形参之间可以出现空格,但是宏名和形参表之间不能有空格出现
        2.在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义.而宏调用中的实参是具体的值.要用它们去代换形参,因此必须作类型说明.这是与函数中的情况不同的.在函数中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行"值传递"。而在带参宏中,只是符号代换,不存在值传递的问题
        3.在宏定义中的形参是标识符,而宏调用中的实参可以是表达式
        4.在宏定义中,字符串内的形参通常要用括号括起来以避免出错,在上例中的宏定义中(y)*(y)表达式的y都用括号括起来,因此结果是正确的
           例如:#define SUM(x,y) (x)*(y)+(x)+(y)
        5.宏定义也可用来定义多个语句,在宏调用时,把这些语句又代换到源程序内
            例:#define M3(m,n) m=a+2;n=a*2;

应用:

 

定义一个有参宏,求最大值
        #define MAX(a,b) a>b?a:b

同样定义别名时 #define 和 typedef的区别

宏定义只是简单地字符串代换,宏是在预处理完成的,而typedef是在编译时处理的,它不是作简单地代换,而是对类型说明符重新命名.被命名的标识符具有类型定义说明的功能
        
        例:
            #define PIN1 int* 
            typedef int *PIN2;
 
            PIN1 a,b;//这里的a 是 int* 而b是int
            PIN2 a1,b1;// 这里a1,b1 都是int* 
            
            产生差异的原因在于 一个是预处理阶段 一个是编译阶段

条件编译的概念和优点:

  1.为什么要使用条件编译

1)按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件.有利于程序的移植和调试
            2)条件编译当然也可以用条件语句来实现.但是用条件语句将会对整个源程序进行编译,生产的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生产的目标程序较短

 2. #if-#else条件编译指令

格式:
                #if 常量表达式
                    程序段1
                #elif 常量表达式
                    程序段2
                #else 
                    程序段3
                #endif
 
            缺点: 判断的依据是宏定义中的数据,而不是编译器中的

 3 #ifdef用来半段某个宏是否定义,定义为ture

例:
                int a =0;
                #ifdef DEBUG
                    a =10;
                #else
                    a=1000;
                #endif

 4. #ifndef 用来判断某个宏是否定义,没定义为true

  当使用条件编译指令调试BUG,类似于JAVA中的LOG管理

    条件:

      DEBUG1 = 1 显示调试信息

      DEBUG1 = 0 不显示调试信息

 

#define DEBUG=1
 
        #if DEBUG1==1
        #define Log(format,...) printf(format,## __VA_ARGS__);
        #elif DEBUG1==0
        #define Log(format,...)
        #else
        #define Log(format,...)
        #endif
 
        使用
         Log("XXXXX-->",10);
 
        //format 代表形参 ...表示可变参数 也类似于java中的泛型
        //##标示可以有参数也可以没有参数 ,如果没有##就至少有一个参数