您的位置:首页 > 编程语言 > C语言/C++

C/C++预处理详解

2013-03-23 11:14 253 查看

1 预处理介绍

        对于任何一种非机器语言,都需要编译为机器语言,程序才能执行。高级语言(C语言/C++)在进行编译的时候,一般过程为预处理、编译、链接。本文主要总结C/C++中预处理的相关知识。

1.1 预处理命令  

        编译预处理命令:以#开头,以回车结束,独占一行;可出现在程序的任何位置,常置于源程序的开始;不属于C++的语法范畴。

        常用的预处理命令有:

#include
包含头文件

#if
条件

#else
否则

#elif
否则如果

#endif
结束条件

#ifdef
或 #if defined如果定义了一个符号,
就执行操作

#ifndef
或 #if !defined如果没有定义一个符号,就指执行操作

#define
定义一个符号

#undef
删除一个符号

#line
重新定义当前行号和文件名

#error
输出编译错误消息,停止编译

#pragma
提供机器专用的特性,同时保证与C++的完全兼容

1.2      预处理的作用

       
常见的预处理有:文件包含,条件编译和宏替换

文件包含
        为什么要用文件包含,在C/C++,为了方便程序员写代码,开发者将常用的函数等放在标准库中,当程序员需要用到某个功能的函数的时候就不需要自己在写代码,而自己将标准库中对应的文件包含进来,就可以直接用里面的函数。再者,程序员自己为了方便程序的管理,需要自己定义头文件。文件包含的命令如下:

#include <stdio.h>
#include“stdio.h”
#include <iostream>

        关于<>和“”。前者<>编译器只搜索包含标准库头文件的默认目录。后者首先搜索正在编译的源文件所在的目录,找不到时再搜索包含标准库头文件的默认目录。如果把头文件放在其他目录下,为了查找到它,必须在双引号中指定从源文件到头文件的完整路径。一般,包含系统的标准库中的头文件用<>,用户自己定义的头文件用“”。

条件编译
        一般情况下,源程序的所有代码行都会参加编译,以生成目标代码。但有时,需要根据不同的情况,生成不同的目标代码,这是就需要条件编译。条件编译命令可以使得编译器按不同的条件去编译程序不同的部分,产生不同的目标代码文件。

条件编译的常用格式有一下三种:

格式1:

#ifdef <标识符>
//程序段1
#else
//程序段2
#endif
格式2:

#ifndef <标识符>
//程序段1
#else
//程序段2
#endif
格式3:

#if <常量表达式1>
//程序段1
#elif <常量表达式2>
//程序段2
……
……
#elif <常量表达式n>
//程序段n
#else
//程序段n+1
#endif
        条件编译主要的运用包括:防止头文件重复引用,调试程序输出必要信息等。

防止头文件重复引用

        为了避免同一个文件被include多次,C/C++中有两种方式,一种是#ifndef方式,一种是#pragma
once方式。在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。在自己定义的头文件mylib.h中。

 
方式一:

#ifndefine _MY_H_
#define _MY_H_
//my.h中的其他内容
//
#endif
 
方式二:

#pragma once
... ... //声明、定义语句
            #ifndef的方式受C/C++语言标准支持。它不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。

        当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,编译器却硬说找不到声明的状况——这种情况有时非常让人抓狂。

    
4000
    由于编译器每次都需要打开头文件才能判定是否有重复定义,因此在编译大型项目时,ifndef会使得编译时间相对较长,因此一些编译器逐渐开始支持#pragma once的方式。

           #pragma once一般由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。你无法对一个头文件中的一段代码作pragma once声明,而只能针对文件。

其好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。大型项目的编译速度也因此提高了一些。

对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,这种重复包含很容易被发现并修正。

          #pragma once方式产生于#ifndef之后,因此很多人可能甚至没有听说过。目前看来#ifndef更受到推崇。因为#ifndef受C/C++语言标准的支持,不受编译器的任何限制;而#pragma
once方式却不受一些较老版本的编译器支持,一些支持了的编译器又打算去掉它,所以它的兼容性可能不够好。

Debug模式输出必要信息

        在程序调试时,经常需要在源程序中插入一些程序调试语句(主要是一些条件判断语句和输出语句)。这些语句是为了帮助程序调试而插入的,在调试完成后还需要逐一删除。如果用手工删除就显得比较麻烦,而且容易出错。这时,可以用条件编译命令来实现自动处理,其代码如下:

#define DEBUG 1
……
……
#if DEBUG
cout << "OK!"<<endl;
#endif
……
……
#if DEBUG
if (x<0)
cout << "Error : x < 0 "<<endl;
#endif
……
……
    在该例中,#if和#endif之间的程序段是专门用于程序调试的。当程序调试完成后,应取消这些语句。这时,可将DEBUG的宏定义改为:

#define DEBUG 0
宏替换
        首先说明一下。宏在C语言里极其重要,而在C++里用的很少。关于宏的第一规则是绝不应该去使用它,除非不得不这样做。几乎每个宏都表明程序设计语言里、程序里或者程序员的一个缺陷,因为他将编译器看到程序的正文之前重新摆布这些正文。宏也是许多程序设计工具的主要麻烦。

宏是代码处不加任何验证的简单替换。宏不是函数,并不会做任何参数类型的检验。这是使用宏不可避免的缺陷。

常量宏

格式:

#define标识符字符或字符串

其中标识符为宏名。

eg. #define PI 3.1415926

带参宏

格式:

#define宏名(参数表)使用参数的字符或字符串

eg. #define AREA(a,b) a*b

b=AREA(2.0,7.8);

2 预处理中的问题

       
C/C++编译系统编译程序的过程为预处理、编译、链接。那么具体一个工程在编译的时候预处理是怎样实现的呢?(最近总是被这类问题困扰)

 

Q1:对于有多个源文件的工程,编译时,先预处理再编译。那么是以源文件为单位,预处理源文件1,编译源文件1,在预处理文件2,编译文件2,…?还是,预处理源文件1,预处理源文件2,…,编译源文件1,编译文件2,…?编译的顺序是怎样的?

A:编译时,以源文件为单位,预处理源文件1,编译源文件1,在预处理文件2,编译文件2,…。

 

Q2:对于有多个源文件的工程,编译是从哪个头文件开始的?头文件,cpp文件,还是有main函数的cpp文件开始?

A:这个问题是个很难回答的问题,可能不同的编译器对此的处理都不相同。但是这个问题也不是特别重要,因为实际上,编译器会将不同的源文件分别、单独地编译成模块,然后再连接成一个可执行程序,因为都是独立编译的,理论上说编译顺序是不重要的。

 

Q3:针对一个源文件,编译时,预处理是怎么进行的?如下cpp源文件.

//test.cpp
#include <iostream>
#define DEBUG 1
using namespace std;
Void main(void)
{
Cout<<“XXXX”<<endl;
#if DEBUG
Cout<<“debugging”<<endl;
#else
Cout<<“not debugging”<<endl;
#endif
}
A:对test.cpp文件进行编译的时候,并不是扫描一遍整个源文件,找出其中的所有预处理命令,预处理完了再对整个程序进行编译。实际上,对该文件进行编译的时候,是从程序的第一行开始一行一行编译的。那为什么编译时都说“先预处理,再编译”呢?那是因为我们通常将预处理命令放在文件的开头,其实我我们自己在控制什么时候进行预处理,而不是编译器本来。其实要理解这点也并不难,不管是预处理命令也好,还是一般命令也好,都是命令。而编译器对待命令都是一样的。如上面的程序,如果我们将#include
<iostream>放在文件最后,那么编译是无法通过的。所以“先预处理,再编译”是程序员自己控制的,并不是编译器控制的。

 

参考:http://baike.baidu.com/view/2129177.htm

            http://www.cnblogs.com/lidabo/archive/2012/08/27/2658909.html

            http://blog.csdn.net/hzxph/article/details/6665089

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息