C++中的预编译

C++提供的编译预处理功能主要有以下三种:
(一) 宏定义
(二) 文件包含
(三) 条件编译
预编译又称为预处理 , 是做些代码文本的替换工作。处理 # 开头的指令 , 比如拷贝 #include 包含的文件代码, #define 宏定义的替换 , 条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。
预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。下面是部分预处理指令:

指令用途

        指令             用途
         #           空指令,无任何效果
         #include    包含一个源代码文件
         #define     定义宏
         #undef      取消已定义的宏
         #if         如果给定条件为真,则编译下面代码
         #ifdef      如果宏已经定义,则编译下面代码
         #ifndef     如果宏没有定义,则编译下面代码
         #elif       如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
         #endif      结束一个#if……#else条件编译块
         #error      停止编译并显示错误信息

1.宏
C++ 宏定义将一个标识符定义为一个字符串,源程序中的该标识符均以指定的字符串来代替。因此预处理命令后通常不加分号。这并不是说所有的预处理命令后都不能有分号出现。由于宏定义只是用宏名对一个字符串进行简单的替换,因此如果在宏定义命令后加了分号,将会连同分号一起进行置换

#define预处理指令是用来定义宏的。该指令最简单的格式是:首先神明一个标识符,然后给出这个标识符代表的代码。在后面的源代码中,就用这些代码来替代该标识符。这种宏把程序中要用到的一些全局值提取出来,赋给一些记忆标识符。

#define      MIN(A, B)     ((A) <= (B)? (A):(B))

用#define定义函数注意点:
将函数中的参数扩起来

#define ADD(x, y) x + y
#define MUL(x, y) x * y

int main ( int argc, char *argv[] )
{
   int a= 2, b = 3;
   printf ("\nProgram %d\n\n", MUL(ADD(a, b), 5) );  //被转换成2 + 3 * 5 注意 没有括号 故结果为17
   return 0;
} 

用#define来定义函数的优缺点:
优点:可完成函数调用的功能,又能减少系统开销,提高运行效率。因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。
缺点:所占用的目标代码空间相对较大
牺牲空间来换取时间

#define具有替代的作用,可以当做是全局变量的使用,在C++中,把一个变量设置成const后,值也是不会再改变,那么#define和const有什么区别的呢?

const常量与define宏定义的区别

(1) 编译器处理方式不同。define宏是在预处理阶段展开,生命周期止于编译期。
只是一个常数、一个命令中的参数,没有实际的存在。#define常量存在于程序的代码段。const常量是编译运行阶段使用,const常量存在于程序的数据段.2)类型和安全检查不同。define宏没有类型,不做任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。
(3) 存储方式不同。define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
const常量会在内存中分配(可以是堆中也可以是栈中)

2.文件包含
#include预处理指令的作用是在指令处展开被包含的文件。包含可以是多重的,也就是说一个被包含的文件中还可以包含其他文件。

在程序中包含头文件有两种格式:
    #include <my.h>
    #include "my.h"
第一种方法是用尖括号把头文件括起来。这种格式告诉预处理程序在编译器自带的或外部库的头文件中搜索被包含的头文件。第二种方法是用双引号把头文件括起来。这种格式告诉预处理程序在当前被编译的应用程序的源代码文件中搜索被包含的头文件,如果找不到,再搜索编译器自带的头文件。
采用两种不同包含格式的理由在于,编译器是安装在公共子目录下的,而被编译的应用程序是在它们自己的私有子目录下的。一个应用程序既包含编译器提供的公共头文件,也包含自定义的私有头文件。采用两种不同的包含格式使得编译器能够在很多头文件中区别出一组公共的头文件
//总之, #include <my.h>是从标准库路径中开始搜索文件, #include "my.h"是编译器从用户的工作路径开始搜索文件

3.条件编译指令
条件编译指令将决定那些代码被编译,而哪些是不被编译的。可以根据表达式的值或者某个特定的宏是否被定义来确定编译条件。
1.#if指令
#if指令检测跟在制造另关键字后的常量表达式。如果表达式为真,则编译后面的代码,直到出现#else、#elif或#endif为止;否则就不编译。
2.#endif指令
#endif用于终止#if预处理指令。

#define DEBUG 0    //定义了一个宏
main()
   {
        #if DEBUG    //判断DEBUG的值,虽然已经定义,但是值是0
             printf("Debugging\n");
        #endif   //DEBUG 为0,endif成立
             printf("Running\n");
    }   
//由于程序定义DEBUG宏代表0,所以#if条件为假,不编译后面的代码直到#endif,所以程序直接输出Running。如果去掉#define语句,效果是一样的。

3.#ifdef和#ifndef

 #define DEBUG  //定义了一个宏
 main()
     {
          #ifdef DEBUG   //判断DEBUG是否定义,#ifdef是定义了
              printf("yes\n");
          #endif
          #ifndef DEBUG   //  判断DEBUG是否没定义
               printf("no\n");
          #endif
        }
       // #if defined等价于#ifdef; #if !defined等价于#ifndef

4.#else指令
#else指令用于某个#if指令之后,当前面的#if指令的条件不为真时,就编译#else后面的代码。#endif指令将中指上面的条件块。

#define DEBUG
main()
    {
       #ifdef DEBUG //是否定义DEBUG
            printf("Debugging\n");
       #else
             printf("Not debugging\n");
       #endif
             printf("Running\n");
       }
    //#else,#elif和endif是一样的

5.#elif指令
#elif预处理指令综合了#else和#if指令的作用。

  #define TWO
        main()
        {
            #ifdef ONE
                printf("1\n");
            #elif defined TWO
                printf("2\n");
            #else
                printf("3\n");
            #endif
        }
       // 程序很好理解,最后输出结果是2。

6.其他一些标准指令
#error指令将使编译器显示一条错误信息,然后停止编译。
#line指令可以改变编译器用来指出警告和错误信息的文件号和行号。
#pragma指令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息

发布了46 篇原创文章 · 获赞 13 · 访问量 3万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章