C/C++常用预处理指令总结(#ifdef,#ifndef,#endif,#pragma once ...)

  本文主要记录了C/C++常见预处理指令。包括#include、#define、#ifdef,#ifndef,#endif,#pragma once


什么是预处理指令?

  预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。

  预处理指令是在编译器进行编译之前进行的操作。预处理过程扫描源代码,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理。在很多编程语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码(防止重复包含某些文件)。要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。


#include包含一个源代码文件

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


#define定义宏

  有关#define这个宏定义,在C语言中使用的很多,因为#define存在一些不足,C++强调使用const来定义常量。宏定义了一个代表特定内容的标识符。预处理过程会把源代码中出现的宏标识符替换成宏定义时的值。记住仅仅是进行标识符的替换。下面列举一些#define的使用:

1、用#define实现求最大值和最小值的宏

#include <stdio.h>
#define MAX(x,y) (((x)>(y))?(x):(y))
#define MIN(x,y) (((x)<(y))?(x):(y))
int main(void)
{
#ifdef MAX    //判断这个宏是否被定义
    printf("3 and 5 the max is:%d\n",MAX(3,5));
#endif
#ifdef MIN
    printf("3 and 5 the min is:%d\n",MIN(3,5));
#endif
    return 0;
}

/*
 1. 三元运算符要比if,else效率高
 2. 宏的使用一定要细心,需要把参数小心的用括号括起来,
 3. 因为宏只是简单的文本替换,不注意,容易引起歧义错误。
*/

2、 宏定义的错误使用

#include <stdio.h>
#define SQR(x) (x*x)
int main(void)
{
    int b=3;
#ifdef SQR
    printf("a = %d\n",SQR(b+2));
#endif
    return 0;
}

/*
 *首先说明,这个宏的定义是错误的。并没有实现程序中的B+2的平方
 * 预处理的时候,替换成如下的结果:b+2*b+2
 * 正确的宏定义应该是:#define SQR(x) ((x)*(x))
 * 所以,尽量使用小括号,将参数括起来。
*/

  关于#define宏的使用,应该特别小心,尤其是含有参数计算的时候如小2示例,最保险的做法将参数用括号括起来。


#ifdef,#ifndef,#endif

  以上这些预编译指令,都是条件编译指令,也就是说,将决定哪些代码被编译,而哪些不被编译。

  头文件中用到的 #ifndef/#define/#endif 来防止该头文件被重复引用。“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的。如:存在a.h文件#include “c.h”而此时b.cpp文件导入了#include “a.h” 和#include “c.h”此时就会造成c.h重复包含。

  头文件被重复引用引起的后果:

  1. 有些头文件重复引用,只是增加了编译工作的工作量,不会引起太大的问题,仅仅是编译效率低一些,但是对于大工程而言编译效率就是很重要的了。
  2. 有些头文件重复包含,会引起编译错误,比如在头文件中定义了全局变量或写了函数的实现而不是声明(虽然这种方式不被推荐,但确实是C规范允许的),这种会引起重复定义。

例如:要编写头文件test.h

 #ifndef _TEST_H    //如果没有定义_TEST_H,即没有包含test.h

 #define _TEST_H    //就定义_TEST_H

 ···  //此处放头文件中本来应该写的代码


 #endif

  _TEST_H是一个标识,若未定义_TEST_H,则这里就定义_TEST_H,然后运行里面的内容;若下次还走到了这个文件,则进行#ifndef的判断,此时发现_TEST_H已经定义过了,说明test.h之前已经编译过了,则#ifndef与#endif之间的内容不会再次被载入。

  每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前后加下划线,并把文件名中的“.”也变成下划线。

  但是,必须记住的是预处理器仍将整个头文件读入,即使这个头文件所有内容将被忽略。由于这种处理将减慢编译速度,所以如果可能,应该避免出现多重包含。


#pragma once

  在头文件的最开始加入这条指令表示:这个头文件只被编译一次,是由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。

  可见#pragma once的作用有点类似于#ifdef,#ifndef,#endif。但各有优缺点。

  • #ifndef,#define,#endif是C/C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,移植性好,所以如果写的程序要跨平台,最好使用这种方式。但缺点是宏名字不能冲突。
  • #pragma 可以避免名字冲突,缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。且不是所有编译器都支持这种方式。

猜你喜欢

转载自blog.csdn.net/qq_34342154/article/details/78888174