c/c++编译预处理

(1)#include<头文件名称>
(2)#include"头文件名称"
第一种形式一般用来包含开发环境提供的库头文件,它指示编译预处理器
在开发环境中设定的路径中找所需的头文件。
第二种形式一般用来包含自己写的头文件,它指示编译预处理器在当前工
作目录中搜索头文件,如果找不到再到开发环境所设定的路径中找
.#include是一个伪指令

内部包含卫哨和外部包含卫哨

为了避免同一个编译单元包含同 ? 个头文件的内容超过一次(这会导致类
型重 复定义错),我们需要在头文件里面使用内部包含卫哨。内部包含卫哨实
际上是使用 预处理器的一种标志宏。有了内部包含卫哨,我们就可以放心地在
同一个编译单元 及其包含的头文件中多次包含同一个头文件而不会造成重复包
含。例如:
// stddef.h
#ifndef_STDDEF_H_INCLUDED_
#define _STDDEF_H_INCLUDED_
...... //头文件的内容
#endif //! STDDEF H INCLUDED
// xxx.cpp 
#include “stddef.h”
#include “stddef.h” // No problem!
    !当包含一个头文件的时候,如果能始终如一地使用外部包含卫哨,可以显著地提
高编译速度,因为当一个头文件被一个源文件反复包含多次时(常常是因为递归的
#include指令展开后所致),可以避免多次查找和打开头文件的操作。例如:
#if !definde (_INCLUDED_STDDEF_H_)
#include <stddef.h>
#define _ INCLUDED_STDDEF_H_
#endif //! INCLUDED_STDDEF H
建议外部包含卫哨和内部包含卫哨使用同一个标志宏,这样就可以少定义一个 | 宏。例如:
#if ! defined (_STDDEF_H_INCLUDED_)
#include 〈stddef.h〉
#endif //!_STDDEF_H_INCLUDED_
因为文件stddefh中的内部包含卫哨就是—STDDEF_HJNCLUDED—,所以这里
!不需要再次定义。
可以仅在头文件中包含其他头文件时使用外部包含卫哨,源文件中可以不使用,

也基本不会影响编译速度。

宏不可以调试,因为宏不会进入符号表(符号表是编译器创建的,在编译 时宏已经消失了),即使宏替换后出现了语法错误,编译器也会将错误定位到源程序 中而不是定位到具体的某个宏定义中。

##与#

#include<iostream>
using namespace std;
#define STRING(x) #x #x #x
#define TEXT(x) "class" #x "info"
int main()
{
	int abc = 100;
	cout << STRING(abc) << endl;
	cout << TEXT(abc) << endl;
	system("pause");
}


合并操作符##将出现在其左右的字符序列合并成一个新的标识符(注意,不 |是字符串)。例如:
#define CLASS_NAME(name) class##name 
#define MHRGE(x, y) x##y##x
则宏引用:
CLASS_NAME(SysTimer)
MERGE(me, To)
将分别扩展为如下两个标识符:
classSysTimer 
meTome
使用合并操作符##时,产生的标识符必须预先有定义,否则编译器会报“标识符未定义”的编译错误。

#pragma

编译伪指令#pragma用于执行语言实现所定义的动作(见示例9 - 5), 具体参考你 I所使用的编译器的帮助文档。
示例9 - 5
#pragma pack(push, 8)	/*对象成员对齐字节数*/
#pragma pack(pop)
#pragma waming(disable:4069) /* 不要产生第 C4069 号编译警告 */
#pragma comment(lib, "kemel32.1ib”)
#pragma comment(lib, "user32.1ib")
#pragma comment(lib, "gdi32.1ib")

建议

(1)虽然宏定义很灵活,并且通过彼此结合可以产生许多变形用法,但 是C++/C程序员不要定义很复杂的宏,宏定义应该简单而清晰;
(2)宏名采用大写字符组成的单词或其缩写序列,并在各单词之间使用分隔;
(3)如果需要公布某个宏,那么该宏定义应当放置在头文件中,否则放 置在实现文件(.cpp)的顶部;
(4)不要使用宏来定义新类型名,应该使用typedef,否则容易造成错误。
(5)给宏添加注释时请使用块注释(/* */),而不要使用行注释。因为有些编译器可能会把宏后面的行注释理解为宏体的一部分;
(6)尽量使用const取代宏来定义符号常量;
(7)对于较长的使用频率较髙的重复代码片段,建议使用函数或模板而不要使用带参数的宏定义;而对于较短的重复代码片段,可以使用带参 数的宏定义,这不仅是出于类型安全的考虑,而且也是优化与折衷的体现;
(8)尽量避免在局部范围内(如函数内、类型定义内等)定义宏,除非它只在该局部范围内使用,否则会损害程序的清晰性。

条件编译

使用条件编译可以控制预处理器选择不同的代码段作为编译器的输入,从而使 !得源程序在不同的编译条件下产生不同的目标代码。条件编译伪指令就如同程序控 !制结构中的选择结构,不同的是前者只在编译预处理阶段即在正式编译之前发挥作 丨用(不生成运行时代码),而后者则是在运行时起作用的。条件编译为程序的移植和 !调试带来了极大的方便,可以用它来暂时或永久性地阻止一段代码的编译。我们将 在下面看到它的价值。条件编译伪指令主要包括#if、#ifdef. #ifhdef、#elif、#else、
丨#endif、defined。每一个条件编译块都必须以#if开始,##endif结束,#if必须与它 丨下面的某一#endif配对;defined必须结合甜丨或#6证使用,而不能单独使用。条件 I编译块可以出现在程序代码的任何地方。

在编程过程中,当需要暂时放弃编译一段代码的时候,我们习惯于使用块注释 来把它屏蔽掉。但是如果这段代码本身就含有块注释时,那么双重注释很麻烦。此时我们可以使用条件编译伪指令#if来屏蔽这段代码,如下代码所示。如果要使这段代码生效,只需把0改为任何一个非0的值(例如1)即可。但是千万要记住:不要企图用#if来代替块注释,两者根本不是同一种用途。
#if 0
	... /*...*/ //希望禁止编译的代码段
	... /*...*/ //希望禁止编译的代码段
#endif

由于条件编译由编译预处理器来处理,显然预编译伪指令无法计算有变量参与其中的表达式或sizeof表达式,因此只能用常量表达式。如果常量表达式的值非0, 则条件为真:否则条件为假(见示例9-2)。
示例9-2
#define FLAG_DOS 2 
#defme FLAG_UNIX 1 
#define FLAG_WIN 0 
#define OS 1
#if OS == FLAG_DOS
	cout<<"DOS platform" <<endl;
#elif OS == FLAG_UNIX
	cout<< "UNIX platform" << endl; 
#elif OS = FLAG 一WIN
	cout<< "Windows platform" << endl; 
#else
	cout<< "Unknown platform" << endl; 
#endif

9.3.2 #ifdef 和 #ifndef
预编译伪指令#ifdefXYZ等价于#ifdefmed(XYZ),此处XYZ称为调试宏。如果 前面曾经用#defme定义过宏XYZ,那么#ifdefXYZ表示条件为真,否则条件为假。 例如:
#define XYZ
。。。
#ifdef XYZ 
DoSomethingO;
#endif
如果你不想让DoSomething();语句被编译,那么删除#define XYZ,或者在其后 用#undef XYZ取消该宏即可。 预编译伪指令#ifhdef XYZ等价于#if !defmed(XYZ)。示例9-3演示了内部包含卫
哨的用法。
示例9-3
#ifndef GRAPHICS_H //防止graphics.h被重复引用
#define GRAPHICS_H 
	#include "myheader.h"//引用非标准库的头文件 
 	#include <math.h>//引用标准库的头文件
...//一些函数的定义和声明
#endif





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值