#ifndef 是"if not defined"的简写,是预处理功能(宏定义、文件包含、条件编译)当中的条件编译,可以根据是否已经定义了一个变量来进行分支选择,其作用是:

  1、防止头文件的重复包含和编译;

  2、便于程序的调试和移植;

  下面分别举例描述。

一、防止头文件的重复包含和编译

下面是错误示范:


headfile_1.h

 #include <iostream>
class CTest_1 {
CTest_1() {
//do something,eg:init;
}
~CTest_1() {
//do something ,eg:free;
}
void PrintScreen()
{
std::cout << "this is Class CTest_1!" << std::endl;
}
};

headfile_2.h

 #include "headfile_1.h"
class CTest_2 {
CTest_2() {
//do something,eg:init;
}
~CTest_2() {
//do something,eg:free;
}
void PrintScreen()
{
std::cout << "this is Class CTest_2!" << std::endl;
}
};

sourcefile.cpp

 #include <iostream>
#include "headfile_1.h"
#include "headfile_2.h" int main()
{
return ;
}

编译时提示重定义错误:


  以上显示headfile_1.h中的类CTest_1重定义了。

  一般地,假如有一个C源文件(如sourcefile.cpp),它包含两个头文件(如headfile_1.h和headfile_2.h),而头文件headfile_2.h又包含了headfile_1.h,则最终的效果是该源文件包含了两次headfile_1.h。如果你在头文件里定义了结构体或者类类型,那么问题来了,编译时将会报重复定义的错误。

  加上条件编译"ifndef"则问题可解决。在headfile_1.h中加上条件编译,如下:

headfile_1.h

 #ifndef _HEADFILE_1_H
#define _HEADFILE_1_H
#include <iostream>
class CTest_1 {
CTest_1() {
//do something,eg:init;
}
~CTest_1() {
//do something ,eg:free;
}
void PrintScreen()
{
std::cout << "this is Class CTest_1!" << std::endl;
}
}; #endif //end of _HEADFILE_1_H

编译通过!

  分析:当第一次包含headfile_1.h时,由于没有定义_HEADFILE_1_H,条件为真,这样就会执行#ifndef _HEADFILE_1_H和#endif之间的代码;当第二次包含headfile_1.h时,前面一次已经定义了_HEADFILE_1_H,条件为假,#ifndef _HEADFILE_1_H和#endif之间的代码也就不会再次被包含,这样就避免了重定义。

  

  小结:还是把头文件的内容都放在#ifndef和#endif中吧。不管你的头文件会不会被多个文件引用,你最好都加上这个。一般格式是这样的:

#ifndef <标识>
#define <标识> ...... ...... #endif

  <标识>在理论上来说是可以自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的明明规则一般是头文件名全大写,前面加下划线,并把文件名中的"."也变成下划线,如:stdio.h

#ifndef _STDIO_H
#define _STDIO_H ...... ...... #endif

注意:#ifndef起到的效果是防止一个源文件多次包含同一个头文件,而不是防止两个源文件包含同一个头文件。事实上,防止同一头文件被两个不同的源文件包含这种要求本身就是不合理的,头文件存在的价值就是被不同的源文件包含。

  

二、便于程序的调试和移植

  在调试程序时,常常需要对程序中的内容进行选择性地编译,即可以根据一定的条件选择是否编译。

  主要分以下几种:

1、

#ifndef 标识符

程序段 

#else

程序段 

#endif

它的作用是当“标识符”没有由#define定义过,则编译“程序段1”,否则编译“程序段2”。

2、

#ifndef 标识符

#define 标识符

程序段 

#else

程序段 

#endif

它的作用是当“标识符”没有由#define定义过,则编译“程序段1”,否则编译“程序段2”。

3、

#if 表达式

程序段 

#else

程序段 

#endif

它的作用是当“表达式”值为真时,编译“程序段1”,否则编译“程序段2”。

注:以上三种形式中#else不是强制的,可省略;当然,当#else后需要嵌套#if时,可以使用预处理命令#elif,它相当于#else#if。

小结:在程序中使用条件编译主要是为了方便程序的调试和移植。

#ifndef详解的更多相关文章

  1. #ifndef HeaderName_h #define HeaderName_h #endif 使用详解(转)

    原文:#ifndef HeaderName_h #define HeaderName_h #endif 使用详解 想必很多人都看到过头文件中写有:#ifndef HeaderName_h       ...

  2. trie字典树详解及应用

    原文链接    http://www.cnblogs.com/freewater/archive/2012/09/11/2680480.html Trie树详解及其应用   一.知识简介        ...

  3. 【转载】C/C++中extern关键字详解

    1 基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义.此外extern也可用来进行链接指定. 也就是说extern ...

  4. C++预处理详解

    本文在参考ISO/IEC 14882:2003和cppreference.com的C++ Preprocessor的基础上,对C++预处理做一个全面的总结讲解.如果没有特殊说明,所列内容均依据C++9 ...

  5. AFNetworking 与 UIKit+AFNetworking 详解

    资料来源 : http://github.ibireme.com/github/list/ios GitHub : 链接地址 简介 : A delightful iOS and OS X networ ...

  6. 转载:C/C++源代码到可执行程序的过程详解

    C/C++源代码到可执行程序的过程详解 编译,编译程序读取源程序(字符流),对之进行词法和语法的分析,将高级语言指令转换为功能等效的汇编代码,再由汇编程序转换为机器语言,并且按照操作系统对可执行文件格 ...

  7. C++宏定义详解

    一.#define的基本用法     #define是C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率,但学生在学习时往往不能 理解该命令的本质 ...

  8. cppunit使用详解

    cppunit使用详解 第一步:如何安装 (我的运行环境: fc7 Linux, gcc4)    cppunit 的安装是相当标准的linux的安装过程    a. 下载cppunit的源文件    ...

  9. jni.h头文件详解二

    作者:左少华 博客:http://blog.csdn.net/shaohuazuo/article/details/42932813 转载请注明出处:http://blog.csdn.net/shao ...

随机推荐

  1. 洛谷 P2764 最小路径覆盖问题 解题报告

    P2764 最小路径覆盖问题 问题描述: 给定有向图\(G=(V,E)\).设\(P\) 是\(G\) 的一个简单路(顶点不相交)的集合.如果\(V\) 中每个顶点恰好在\(P\) 的一条路上,则称\ ...

  2. luogu1169 棋盘制作 (单调栈)

    先预处理出来从每个位置 以0开始 往右交替最多能放多少格 然后就相当于对每一列做HISTOGRA #include<bits/stdc++.h> #define pa pair<in ...

  3. Elasticsearch基础知识要点QA

    前言:本文为学习整理实践他人成果的记录型博客.在此统一感谢各原作者,如果你对基础知识不甚了解,可以通过查看Elasticsearch权威指南中文版, 此处注意你的elasticsearch版本,版本不 ...

  4. Looper Handler Mssage

    1. 一个Handler只有一个队列;2. 在调用Handler.post(Runnable runnable)方法时,会将runnable封装成一个Message;3. 在队列执行时,会判断当前的M ...

  5. (转)Servlet的生命周期——初始化、运行、销毁全部过程

    背景:面试中很基础的一个问题,所以有必要好好整理一番. Servlet体系结构是建立在 Java 多线程机制上的,它的生命周期由 Web 容器负责. 当客户端第一次请求某个 Servlet 时,Ser ...

  6. Gradle 从svn 中检出的父项目后处理配置【我】

    前提: 一个用gradle配置的 类似maven的聚合项目的项目,然后它在svn上就是一个父工程的目录. 检出方式: 在eclipse中,直接用svn资源库检出 父项目 的目录. 然后,在父项目下面的 ...

  7. Linux就该这么学--第五期 学习笔记

    第一节:基础 <Linux就该这么学>第二期视频 Linux就该这么学第5期第二节: ------------你的价值:你对公司做出的共享和你的不可替代性------------瑞尔系统R ...

  8. js位操作

    1.&(与) 都是1才是1 例如:14&15  (14  二进制  1110 15  二进制   1111 &与的结果  1110  ---->结果14) (14& ...

  9. Kafka集群优化篇-调整broker的堆内存(heap)案例实操

    Kafka集群优化篇-调整broker的堆内存(heap)案例实操 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.查看kafka集群的broker的堆内存使用情况 1>. ...

  10. linux系统下安装ruby环境

    1. 移除现有 Ruby 默认源 输入以下指令 $gem sources --remove https://rubygems.org/ 2. 使用新的源 输入以下指令 $gem sources -a  ...