(c/c++学习笔记十一)预处理命令

来源:互联网 发布:阿里云机房 编辑:程序博客网 时间:2024/06/05 14:14

    预处理是指在进行编译的第一遍扫描(语法扫描和语法分析)之前所做的工作。预处理是C语言的一个重要功能,它由预处理器负责完成。当对一个源文件进行编译时,系统将自动引用预处理程序对程序中的预处理部分作处理,处理完毕后自动进入对源程序的编译。C语言提供了多种预处理功能,如宏定义、文件包含、条件编译等。

一.宏

    C语言允许用一个表示法来表示一个字符串,称为“宏”。被定义为“宏”的表示符称为“宏名”,对程序中所有出现的“宏名”,都用宏定义中的字符串去替换,这称为“宏替换”和“宏展开”。

    1.无参数宏

    定义的一般形式为: #define 标示符 字符串

    例:

#define ADD (a+b)                     //定义一个宏  不用加分号 如果有分号则一起替换int main(int argc, char *argv[]){    int a = 5, b = 3, c;    c = 2*ADD;                        //宏展开为 c=2*(a+b);  注意括号不能少    printf("c=%d\n",c);               //输出 c=16      }
    宏定义使用宏明表示一个字符串,在宏展开时又以该字符串取代宏名,这是一种简单的替换,字符串中可以包含任何字符,可以是常数,也可以使表达式,预处理程序对它不作任何检查,如有错误,只能在编译已被宏展开后的程序是发现。


    宏定义必须写在函数之外,其作用域为宏定义命令起到源程序结束,要终止其作用域可以使用#undef命令

    例:

#define PI 3.14int main(int argc, char *argv[]){double pi = PI;printf("PI=%f\n",PI);}#undef PIvoid function(){double pi = PI;         //错误 已经取消宏定义printf("PI=%f\n",PI);}
    宏明在源程序中若用引号括起来,则预处理程序不对其做宏替换


    宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏明。在宏展开时由预处理程序层层替换。

    例:

#define PI 3.14#define N 2*PI*r                   int main(int argc, char *argv[]){double r = 5;double n = N;              //展开为 n=2*3.14*rprintf("n=%f",n);}

    注意#define和typedef的区别,宏是预处理完成的,typedef是在编译时处理的。typedef不是简单的替换,而是对类型说明符重命名。

    例:

#define INTPTR1 int*typedef int* INTPTR2;int main(int argc, char *argv[]){INTPTR1 a, b;    //a是指针变量 b是整型变量INTPTR2 x, y;    //x y都是指针变量int m = 10;a = &m;b = &m;          //错误  b是整型变量x = &m;y = &m;}

二.带参数的宏

    C语言允许带有参数的宏。在宏定义中的参数称为形式参数,在宏调用中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代替形参。

    带参宏定义的一般形式为:  #define 宏名(形参表) 字符串   调用形式为: 宏名(实参表)

    例:

#define ADD(a,b) a+bint main(int argc, char *argv[]){int a = 4, b = 5, c;c = ADD(a,b);         //宏展开为 c=a+bprintf("c=%d\n",c);   //输出 c=9}


    带参宏定义中,宏名和形参表之间不能有空格,如果有空格则会被当做无参数宏,例如把 #define ADD(a,b) a+b 写作 #define ADD (a,b) a+b 在上述宏展开后是 c=(a,b) a+b

    在带参宏定义中,形参不分配内存单元,因此不必作类型定义。而宏调用中的实参有具体的值。要用他们去替换形参因此必须做类型说明。这是与函数中的情况不同的。在函数汇中,形参和实参是两个不同的量,各有自己的作用域,调用时要把实参值赋予形参,进行“值传递”。而带参宏中,只是符合代换,不存在值传递问题。

    在宏定义的形参标识符,而宏调用可以使表达式

    例

#define MUT(x) (x)*(x)int main(int argc, char *argv[]){int a = 4, b;b = MUT(a+2);         //宏展开 (a+2)*(a+2)  注意宏定义中参数的括号,如果没括号,则展开为 a+2*a+2,结果完全不同printf("b=%d\n",b);   //输出 b=36  如果宏定定字符串中没有括号 则结果为10}


带参宏和带参函数很相似,但有本质不同,除了上面谈到的几点之外,把同一表达式用函数处理与用宏处理的结果有可能是不同的

例:(函数调用)

int MUT(int x){return (x)*(x);}int main(int argc, char *argv[]){int i = 1;while(i<=5)printf("%d\n",MUT(i++));}
例(带参宏)

#define MUT(x) ((x)*(x))int main(int argc, char *argv[]){int i = 1;while(i<=5)printf("%d\n",MUT(i++));}
    函数调用中,i作为函数参数传入到函数内,计算完返回 i加1进入下次循环,共执行5次循环  带参宏中,宏展开为(i++)*(i++) 计算完i做两次自加操作,只循环三次


    宏定义也可以来定义多个语句,在宏调用时,把这些语句又代换到源程序内。

    例:

#define DEF int a = 2; int b = 3; int c = a + 2;int main(int argc, char *argv[]){DEF;printf("a=%d b=%d c=%d",a,b,c);  //输出 a=2 b=3 c=40}


三.条件编译

    预处理程序提供了条件编译的功能,可以按不同的条件去编译不同的程序部分,因而产品不同的目标代码文件。这对程序的移植和调试是很有用的,条件编译有三种形式。

    第一种:

    #ifdef 标识符

        程序段1

    #else

        程序段2

    #endif

    它的功能是,如果标识符已经被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。如果没有程序段2,本格式中的#else可以没有,即可以写为:

    #ifdef 标识符

        程序段1

    #elseif


    第二种:

    #ifndef 标识符

        程序段1

    #else

        程序段2

    #endif

    它的功能和第一种正相反,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译

    此功能多用于避免头文件多重包含


    第三种:

    #if 常量表达式

        程序段1

    #else

        程序段2

    #endif

    它的功能是,如果常量表达式的值为真(非0),则对程序段1进行编译,否则对程序段2进行编译。

    



    



0 0