C++预处理和头文件保护符

一预处理

1.常见的预处理功能

预处理器的主要作用就是把通过预处理的内建功能对一个资源进行等价替换,最常见的预处理有:文件包含,条件编译、布局控制和宏替换4种。
文件包含:#include 是一种最为常见的预处理,主要是做为文件的引用组合源程序正文。
条件编译:#if,#ifndef,#ifdef,#endif,#undef等也是比较常见的预处理,主要是进行编译时进行有选择的挑选,注释掉一些指定的代码,以达到版本控制、防止对文件重复包含的功能。
布局控制:#pragma,这也是我们应用预处理的一个重要方面,主要功能是为编译程序提供非常规的控制流信息。
宏替换:#define,这是最常见的用法,它可以定义符号常量、函数功能、重新命名、字符串的拼接等各种功能。

2.常见的预处理指令:

#define 宏定义
#undef 未定义宏
#include 文本包含
#ifdef 如果宏被定义就进行编译
#ifndef 如果宏未被定义就进行编译
#endif 结束编译块的控制
#if 表达式非零就对代码进行编译
#else 作为其他预处理的剩余选项进行编译
#elif 这是一种#else和#if的组合选项 //后面有例子的
#line 改变当前的行数和文件名称
#error 输出一个错误信息
#pragma 为编译程序提供非常规的控制流信息

3.预处理标识符

为了处理一些有用的信息,预处理定义了一些预处理标识符,虽然各种编译器的预处理标识符不尽相同,但是他们都会处理下面的4种:
__FILE__ 正在编译的文件的名字
__LINE__ 正在编译的文件的行号
__DATE__ 编译时刻的日期字符串,例如: "25 Jan 2006"
__TIME__ 编译时刻的时间字符串,例如: "12:30:55"
  例如:

cout<<"The file is :"<<__FILE__"<<"! The lines is:"<<__LINE__<<endl; 

二、头文件保护符

1.条件编译

[html] view plaincopy

#ifdef XXX   //或者 #ifndef
//…
(#else)
//…
#endif  

例如:

#ifdef WINDOWS
//....
//....
#endif
#ifdef LINUX
//....
//....
#endif 

2.编写头文件保护符

头文件应该含有保护符,即使这些头文件不会被其他头文件包含。编写头文件保护符并不困难,而且如果头文件被包含多次,它可以避免难以理解的编译错误。

利用宏定义和条件编译#ifndef指示检测指定的预处理变量是否未定义。如果预处理器变量未定义,那么跟在后面的所有指示都被处理,直到出现#endif。
可以使用这些措施来预防多次包含同一头文件:

/*** 头文件salesitem.h ***/
#ifndef SASESITEM_H
#define SALESITEM_H
//...这里是内容
#endif  

条件指示#ifndef SALESITEM_H测试 SALESITEM_H 预处理器变量是否未定义。如果 SALESITEM_H 未定义,那么 #ifndef 测试成功,跟在 #ifndef 后面的所有行都被执行,直到发现#endif。相反,如果 SALESITEM_H 已定义,那么 #ifndef 指示测试为假,该指示和 #endif 指示间的代码都被忽略。

为了保证头文件在给定的源文件中只处理过一次,我们首先检测 #ifndef。第一次处理头文件时,测试会成功,因为 SALESITEM_H 还未定义。下一条语句定义了 SALESITEM_H。那样的话,如果我们编译的文件恰好又一次包含了该头文件。#ifndef 指示会发现 SALESITEM_H 已经定义,并且忽略该头文件的剩余部分。

当没有两个头文件定义和使用同名的预处理器常量时,这个策略相当有效。我们可以为定义在头文件里的实体(如类)命名预处理器变量来避免预处理器变量重名的问题。一个程序只能含有一个名为 Sales_item 的类。通过使用类名来组成头文件和预处理器变量的名字,可以使得很可能只有一个文件将会使用该预处理器变量。

头文件保护方式二 --------------------------------------------------------------------------------------

#pragma once

这是一个比较常用的指令,只要在头文件的最开始加入这条指令就能够保证头文件被编译一次

#pragma once用来防止某个头文件被多次include,#ifndef,#define,#endif用来防止某个宏被多次定义。

#pragma once是编译相关,就是说这个编译系统上能用,但在其他编译系统不一定可以,也就是说移植性差,不过现在基本上已经是每个编译器都有这个定义了。

#ifndef,#define,#endif这个是C++语言相关,这是C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式

三、比较

#pragma once与 #ifndef的区别

为了避免同一个文件被include多次

1 #ifndef方式
2 #pragma
once方式

在能够支持这两种方式的编译器上,二者并没有太大的区别,但是两者仍然还是有一些细微的区别。
方式一:

#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
... ... // 一些声明语句
#endif

方式二:

#pragma once
... ... // 一些声明语句

#ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。当然,缺点就是如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况

#pragma
once则由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。

方式一由语言支持所以移植性好,方式二可以避免名字冲突

时间: 2024-10-23 04:51:10

C++预处理和头文件保护符的相关文章

小猪猪逆袭成博士之C++基础篇(二) 常量、处理类型、自定义头文件

小猪猪逆袭成博士之C++基础篇(二) const .auto. decltype 上一章我们介绍了一些常用的类型和常见的问题,下面再介绍一些学习的时候不是特别常用但是在实际工程中很有用的一些东西. 一.常量const 我们想要定义一种不能改变它的值的变量,我们就对这个变脸的类型加上限定符Const. Const 对象必须要初始化,并且它一旦创建了就不能改变,所以Const变量是只能出现在等号的右边的. 另外,在C11标准中会有一种Constexpr的类型来编译器验证变量的值是是否是一个常量表达式

预处理命令(宏定义,条件编译,头文件)

宏定义 <1>不带参数宏定义:#define PI 3.1415926 <2>带参数的宏定义: #define PI  3.1415926 #define S(r)  (PI*(r)*(r)) void main(){float  a=2,area;area=S(a); } 用宏可以得到几个结果: #define CIRCLE(R,L,S,V)  L=2*PI*(R);S=PI*(R)*(R);V=4.0/3.0*PI*(R)*(R)*(R) void main(){float r

C预编译, 预处理, C/C++头文件, 编译控制,

在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征.依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的. 其格式一般为: #Pragma Para 其中Para 为参数,下面来看一些常用的参数. (1)message 参数. Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输

C语言中,头文件和源文件的关系(转)

简单的说其实要理解C文件与头文件(即.h)有什么不同之处,首先需要弄明白编译器的工作过程,一般说来编译器会做以下几个过程: 1.预处理阶段 2.词法与语法分析阶段 3.编译阶段,首先编译成纯汇编语句,再将之汇编成跟CPU相关的二进制码,生成各个目标文件 (.obj文件)4.连接阶段,将各个目标文件中的各段代码进行绝对地址定位,生成跟特定平台相关的可执行文件,当然,最后还可以用objcopy生成纯二进制码,也就是去掉了文件格式信息.(生成.exe文件) 编译器在编译时是以C文件为单位进行的,也就是

PCI/PCIe接口卡Windows驱动程序(3)- 驱动程序代码(头文件)

在WDF的PCIe驱动程序中,共有四个.h文件(Public.h  Driver.h  Device.h  Trace.h).本文将分别对四个文件源代码进行详细的解释. Public.h 1 #ifndef _USER_H 2 #define _USER_H 3 // 4 // Define an Interface Guid so that app can find the device and talk to it. 5 // 6 #include <initguid.h> 7 // {4

C++预编译头文件

以前只是学过C++中的预编译头文件,但一直没用过:既然今天又遇到了这个问题,所以还是决定写点总结 算是做个笔记吧! 在C++中之所以出现预编译的概念主要是因为在C++项目中导致整个程序的编译过程变得很缓慢的一个很重 要的原因就是C++头文件的存在,在每一个.cpp文件中都会包含许多.h的头文件,如果所包含的头文件过多或过大 就会导致.cpp文件过大而编译缓慢,但是事实上在许多.cpp中所包含的头文件都是重复出现的,即有很多头文件被 重复编译了许多次,这当然会导致项目整体的编译速度变慢. 为了解决

Makefile自动生成头文件依赖

前言 Makefile自动生成头文件依赖是很常用的功能,本文的目的是想尽量详细说明其中的原理和过程. Makefile模板 首先给出一个本人在小项目中常用的Makefile模板,支持自动生成头文件依赖. CC = gcc CFLAGS = -Wall -O INCLUDEFLAGS = LDFLAGS = OBJS = seq.o TARGETS = test_seq .PHONY:all all : $(TARGETS) test_seq:test_seq.o $(OBJS) $(CC) -o

C/C++ 中头文件相互包含引发的问题

今天下午遇到一个头文件相互包含而导致的编译问题,花了我不少时间去调试没找到问题,最后晚上跟师兄讨论不少时间,突然有所顿悟! 问题重现 我把问题脱离于项目简单描述一下:我写了一个函数 bool func(ClassA* CA) 需要加到项目中,我就把这个函数的声明放到 head1.h 中,函数参数类型 ClassA 定义在另一个头文件 head2.h 中,因此我需要在 head1.h 中包含 head2.h:而 head2.h 中之前又包含了 head1.h,这样就构成了一种头文件相互包含的场景.

gcc 查看 引用头文件的位置

场景: 代码 使用了 msgpack库,但makefile 没有指定 msgpack的头文件所在位置,也可以编译通过. 可以得出 一定是在系统的某个位置 安装了 msgpack,并且被找到. 但具体头文件在哪个位置呢? 从网上 搜到 看到 都是 gcc/g++ 头文件的 搜索路径,-I指定头文件的搜索路径. 知道了搜索路径,那我 需要 遍历 搜索路径上的每个目录 去查找, 这样比较费时, 但也是最容易想到的. 但可以通过 gcc 编译程序的 过程,让 gcc 自己列出所在位置. gcc 由源码到