前言
C++主要有三个编译阶段:预处理转译成目的码链接(最后的两个阶段一般才视为真正的“编译”)。

预处理器

预处理器:在编译之前运行的工具,会根据预编译指令对源代码进行“加工”,实际编译的源码是经过预处理器处理过的。
预处理器编译指令是向预处理器发出的命令,总是以符号#开头。根据程序员的指示,决定编译器实际要编译的文本内容。
比如:对于一个宏#define PI 3.1415,在编译之前,预处理器会在代码中找到 PI 并将它替换为 3.1415 。

使用#define定义常量

  • 语法: #define identifer(标识符) value(值)
    栗子:
#define PI 3.1415
#define NICKNAME "f_x"
#define THIS_IS_DOUBLE double
#define THIS_IS_FOR for
  • 预处理器对宏指定的文本进行简单的替换,因此可以用宏来编写简单的函数
    栗子:
#define PI 3.1415
#define AREA_CIRCLE(r) ( PI * (r) * (r))

为什么要使用这么多括号?

对于上面的宏AREA_CIRCLE,若在代码中这样使用
cout << AREA_CIRCLE(4+6);
使用括号时,经预处理器处理后:
cout << (3.1415 * (4 + 6) * (4 + 6))
不使用括号时:
cout << 3.1415 * 4 + 6 * 4 + 6
你懂。。

宏的其他作用

  • 避免头文件的多次包含
    假设有两个头文件class1.h,class2.h,当我们不得不让两个类相互包含时,对预处理器来说,这样会造成递归问题;
    为了避免这种问题,可以使用宏和预处理器编译指令#ifndef(if-not-define)和endif。
#ifndef HEADER2_H_H            //如果宏HEADER2_H_H未定义
#define HEADER2_H_H            //定义宏HEADER2_H_H
#include <class2.h>

class class1
{
    ......
};

#endif


#ifndef HEADER1_H_H            //如果宏HEADER1_H_H未定义
#define HEADER1_H_H            //定义宏HEADER1_H_H
#include <class1.h>

class class2
{
    ......
};

#endif   

预处理器进行死板的文本替换,这可以减轻程序员的负担,当并不是能减轻编译器的负担。
其次,对于宏PI,我们没有太大的控制权,其类型是double还是float?答案是都不是。在预处理器看来,PI就是3.1415,根本不知道其数据类型。
所以定义常量时 const 是更好的选择
const double PI = 3.14215;
define与const的区别

  • 起作用的阶段不同:#define在预处理阶段起作用; const在编译或者运行阶段起作用;
  • 起作用的方式不同:#define进行简单的文本替换,不能进行类型检查;const定义的常量有对应的数据类型;
  • 储存方式不同:#define定义的宏常量在内存中有多个备份,而const定义的常量在内存中只有一个备份;
  • const常量可用于调试,而#define定义的常量不能用于调试(预处理阶段被替换掉了);

 评论