当前位置: 首页 > C, C/C++ > 正文

程序诞生的第一步–预处理

                                                            程序诞生的第一步——预处理

      程序在编译前要进行预处理,这一步被为预处理阶段,主要是进行一些文本性的操作,比如宏的替换,插入一些由#include指令所包含的一些文件的内容,以及处理一些条件编译的东西。下面介绍预处理的主要部分宏及其相关知识和一些标准中的命令。

      首先标准中确定的一些预定义符号:

      __FILE__              进行编译的源文件名

      __LINE__              文件当前的行号

      __DATE__            文件被编译的日期

      __TIME__             文件被编译的时间

      __STDC__            如果编译遵循ANSI C其值就为1

#define 的用法

       #define    NAME       EXP

       在预处理阶段编译器就会把文件中所有的NAME全部替换成EXP(可以是字符,也可以是表达式)只要使用#define指令就可以替换任意的文本到文件中。NAME和EXP都是自己可以定义的。

      如果一行写不下,可以在这一行的最后加上字符 \ 下一行接着写,编译器会认为这是一个东西。

       例如:

                   #define PRINT     printf(“this file is %d:” \

                                                            “x=%d,y=%d,z=%d\

                                                             __FILE__,__LINE__,\

                                                             x,y,z)

                    像这样的生命都是合法的,其打印值也是我们所期望的打印值。

      

宏:

     其实#define指令并不能与宏相等,这个指令包含一个规定就是宏这种用法,

如果一个宏带参数那么参数列表应当紧挨着宏名,并且后边宏的参数都应当带上括号,防止由于运算符号优先级带来的一系列问题。

     举个例子:

              #define

              #define   SUM(x)   x*x

              int main()

              {

                    int a=5;

                    int b;

                    b=SUM(a+1);

                    printf(“%d\n”,b);

                    return 0;

               }

    此时打印出来的东西到底是什么?有人可能会回答36,其实博主在学习C语言的过程中也回答过36这个答案,但其实正确的答案是11.请考虑下这个宏到底是如何替换的。其实是a+1*a+1,按照C语言的运算符优先级乘法运算高于加法运算(无括号情况下)这个值就是11,这和我们的想法相差甚远,这就是为什么在声明带参数的宏时一定要注意带括号的问题。

  #define的替换:

  在使用宏进行替换操作时要注意其替换过程:

   1.在调用宏时,首先对参数进行检查,看看是否包含了任何宏定义过的符号,如果是他们首先被替换。

   2.替换文本随后被插入到程序中宏明的地方,此处是完全的替换。

   3.最后再对文本进行扫描,看看它是否包含了任何由#define定义的符号,如果有重复上面的过程。

    另外:宏也可以应用与字符串

宏与函数:

       首先说明几个其他相关知识点:

       宏其实有时候有副作用,不要在宏中出现X++这种语句,它会给程序带来不可预知的恶劣影响。

      宏命名的约定:虽然宏明可以自定义,但是请用大写命名吧这样方便自己也方便日后维护程序的人,我们要有专业精神。

     现在来说宏与函数的区别于优劣性:

     宏:

          宏每次都要插入到程序中,有可能导致程序的代码量会变得非常的大,但是宏的执行速度非常快(毕竟不再运行阶段处理宏),宏在其上下文环境中应当尽可能的加上括号防止因为运算符优先级产生问题,对与宏的参数求值,具有副作用的宏会带来不良影响,宏与类型无关只要对参数的操作合法,它可以是任意类型。宏不能传递类型名,不能递归。

     函数:

           函数每次调用都要执行相同的代码段,再返回值是会有额外的开销,参数的副作用没有影响,参数的值对于类型有严格的要求,总体运行较慢(毕竟是在运行阶段调用)。

    #undef  移除一个已经声明过得宏。

    #error text of error   message   定义一个表示错误地宏 

    条件编译:

        #define   NAME    1

        #if    NAME

                EXP

        #endif

    如果已经定义了某个宏比如NAME,那么编译器会自动的对其进行运算,如果为真则编译EXP后结束,如果为假那么就跳过EXP。

这种判断是可以嵌套的还有#elif ,这其实很像if-else-if语句,其实在意思上都是差不多的。

   #if NAME

          EXP 

   #elif  NAME1

           EXP2

   #elif  NAME2

            EXP3

   #else

           EXP4

   #endif

 都是在前一个判断为假的时候才编译后一个语句的。

 对于条件编还有最后一个问题:

 就是是否被定义的问题,要知道在头文件中不能对一个宏进行反复的声明,但是有时候却会出现这样的问题;

     解决方法;

       #ifndef  NAME

       #define   NAME

     这两句就是如果没有定义过NAME,那么就定义一个NAME,这样可以有效解决反复声明同一个宏的问题。

函数库文件与本地文件的问题;

  一般默认#include是系统的函数库,#include”NAME.h”是用户本地的文件,根据编译器的不同可以去设置其查找范围,其实没有功能上的区别就算你用尖括号包含了本地的文件,编译器在库中找不到时会自动扫描当前的目录来找到它,有强迫症的可以去自行百度看看怎么改这个东东。


版权声明:本文为博主原创文章,未经博主允许不得转载。

]]>

本文固定链接: http://zmrlinux.com/2015/04/03/%e7%a8%8b%e5%ba%8f%e8%af%9e%e7%94%9f%e7%9a%84%e7%ac%ac%e4%b8%80%e6%ad%a5-%e9%a2%84%e5%a4%84%e7%90%86/ | Kernel & Me

该日志由 root 于2015年04月03日发表在 C, C/C++ 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: 程序诞生的第一步–预处理 | Kernel & Me