欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C/C++的预编译和宏定义

程序员文章站 2024-02-02 19:07:04
预编译是整个编译过程的第一步,是g++ -e选项输出的结果。 这个步骤处理的是源文件/头文件中的宏,宏指令常用的有以下几类: 文件包含:#include 宏定义:#defin...

预编译是整个编译过程的第一步,是g++ -e选项输出的结果。

这个步骤处理的是源文件/头文件中的宏,宏指令常用的有以下几类:

文件包含:#include
宏定义:#define、#undef
条件编译:#ifdef、#ifndef、#if、#elif、#else、#endif
1. 文件包含 #include
预处理会把要包含的文件的内容全部包含进来,比如下面这个文件prepro.cpp:

[cpp]
#include "prehead.h" 
int main(){ 
  add(1, 2); 

引入了头文件prehead.h:

[cpp]
#ifndef _prehead_h 
#define _prehead_h 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif 
使用命令g++ -e prepro.cpp预编译,输出结果如下:

[plain]
# 1 "prepro.cpp" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "prepro.cpp" 
# 1 "prehead.h" 1 
 
 
 
 
 
 
int add(int, int); 
# 2 "prepro.cpp" 2 
 
int main(){ 
  add(1, 2); 

可以看到结果中两个文件的宏定义都被删除了,文件prehead.h中注释也被删除了,而剩下的内容被引入了prepro.h中。至于#ifndef的意思最后介绍。

2. 宏定义:#define、#undef
#define宏定义的一种用法是声明一个宏,用来实现条件编译,这种用法后面和#ifdef等宏命令一起介绍。

#define宏定义的另一种用法是用来定义常量,定义准inline的函数。这种用法不好,应该避免使用。

使用#define定义的变量,比如:#define pi 3.14,预编译以后程序中所有的pi都会被3.14替代。再比如上次谈g++那篇博客的这个例子:

[cpp]
#define one 1 
#define two 2 
 
int add_one_two(){ 
  return one + two; 

预编译后结果如下:

[plain]
# 1 "add.cpp" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "add.cpp" 
 
int add_one_two(){ 
  return 1 + 2; 

可以看到定义的one被替换成了1,定义的two被替换成了2。

使用常量是好的,但是使用#define这种方法的不好是调试时候变量名都被替换了,失去了常量的效果。最好用下面这种方式定义变量:

[plain]
const type var_name=value 
使用#define定义准inline函数,比如下面这个的例子(引自《effective c++》):

[cpp]
#define max(a, b) ( (a) > (b) ? (a) : (b) ) 
一般也没什么问题,但是:

[cpp]
int a = 5, b = 0; 
max(++a, b);            /* a自增了两次 */ 
max(++a, b + 10);       /* b自增了一次*/ 
所以这样定义是会出问题的。使用#define定义函数的初衷是为了提高程序运行速度,但是这种方式提高速度有限,根本上应该从改进数据结构,算法方面入手。这种#define,果断写成inline就ok了,那么多括号写着累,看着也累,没必要。

3. 条件编译:#ifdef、#ifndef、#if、#elif、#else、#endif
条件编译算是宏最精髓的应用吧。先来说说这几个指令的意思,如果你看过我前面的博客,而且坚持在usaco上做题,那么想必你已经熟悉了c++最基本的语法,那这几个指令看一眼也能基本知道大概意思。下面用注释方式解释:

[plain]
#ifdef              //if define,如果定义了。。。 
#ifndef             //if not define,如果没有定义。。。 
#if             // 如果。。。 
#elif               // 或者如果。。。 
#else               // 或者。。。 
#endif              // 结束if 
第一种条件编译
用来防止一个头文件引入两次。比如开始那个例子里面的头文件prehead.h:

[cpp]
#ifndef _prehead_h      /* 如果没有定义_prehead_h */ 
#define _prehead_h      /* 定义_prehead_h */ 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif                          /* 结束上面对应的#ifndef */ 
然后更改下我们的prepro.cpp文件,增加一行#include "prehead.h"

[cpp]
#include "prehead.h" 
#include "prehead.h" 
int main(){ 
  add(1, 2); 

这里我手工展开,变成下面这个样子,然后在注释里面分析一下:
[cpp]
#ifndef _prehead_h      /* 如果没有定义_prehead_h */  /* 的确没有定义 */ 
#define _prehead_h      /* 定义_prehead_h */  /* 那么就定义之 */ 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif                          /* 结束上面对应的#ifndef */ 
 
#ifndef _prehead_h      /* 如果没有定义_prehead_h */  /* 上面已经定义了,条件为false */ 
#define _prehead_h      /* 定义_prehead_h */  /* 所以从这里开始到endif都会被预处理器删除 */ 
/* declaration of a function that add two integer */ 
int add(int, int); 
#endif                          /* 结束上面对应的#ifndef */  /* 后面的编译独立于第二个#ifndef _prehead_h */ 
 
int main(){ 
  add(1, 2); 

使用命令g++ -e prepro.cpp,输出和第一个例子里面一样,如下:

[plain]
# 1 "prepro.cpp" 
# 1 "<built-in>" 
# 1 "<command-line>" 
# 1 "prepro.cpp" 
# 1 "prehead.h" 1 
 
 
 
int add(int, int); 
# 2 "prepro.cpp" 2 
 
int main(){ 
  add(1, 2); 

或许你会说,这个sb,一个头文件include两次,哈哈哈哈。但是,这只是个例子,实际项目中文件引入比较复杂,比如上面的prepro.cpp引入了第三个文件,然后第三个文件引入prehead.h,那么没有条件编译的话prepro.cpp里面就会有两个prehead.h。

使用#ifndef... #define... #endif可以保证一个文件只引入一次,这里的...可以是任何内容,只要你能保证定义的这个东西其他地方没定义,但是惯例是定义文件名的大写然后加几个下划线,像上面那个例子一样。

第二种条件编译
用来实现跨平台编译。比如下面这样:

[cpp]
#ifdef _linux_ 
// linux平台相关代码。。。 
#endif 
#ifdef _windows_ 
// windows平台相关代码。。。 
#endif 
#ifdef _macos_ 
// mac os平台相关代码 
#endif 

然后在某个include的配置文件config.h里面,如果有如下定义:#define _linux_,那么编译器就只编译linux平台相关的代码;如果有如下定义:#define _windows_,那么只编译windows平台相关的代码。

最后要说明的是,配置文件config.h不是手动写的,而是脚本自动生成的。至于如何编写脚本或者使用工具生成脚本,算是另一个话题了,以后再介绍吧。

 

宏指令还有其他一些,但是用的很少,我就不写了,想了解的朋友可以参考这个网址:点击进入。这个网站c++的文档,手册,帮助都一流,学习c++可以多看看。


参考文献:

effective c++, third edition. scott meyers. 2005

摘自 程序猿