0717-7821348
新闻中心

500万彩票网官方网站

您现在的位置: 首页 > 新闻中心 > 500万彩票网官方网站
C/C++预处理指令和常用预处理常量
2019-11-04 22:00:41

常见的预处理指令:

  1. #include 包括一个源代码文件
  2. #define 界说宏
  3. #undef 撤销已界说的宏
  4. #if 假如给定条件为真,则编译下面代码
  5. #ifdef 假如宏现已界说,则编译下面代码
  6. #ifndef 假如宏没有界说,则编译下面代码
  7. #elif 假如前面的#if给定条件不为真,当时条件为真,则编译下面代码
  8. #endif 完毕一个#if……#else条件编译块
  9. #error 中止编译并显现过错信息

10.编译C++程序时,编译器主动界说了一个预处理器姓名__cplusplus(留意前面有两个下划线),因而能够依据这个来判别该程序是否是C++程序,以便有条件地包括一些代码,如:

#ifndef MYHEAD_H
#define MYHEAD_H
#ifdef __cplusplus //若是C++程序,则需求包括C的库
extern "C" {
#endif
int DMpostprocessing();
#ifdef __cplusplus
}
#endif
#endif

11.在编译C程序时,编译器会主动界说预处理常量__STDC__。当然__cplusplus和__STDC__ 不会一同被界说;

12.别的两个比较有用的预界说常量是__LINE__(记载文件现已被编译的行数)和__FILE__(包括正在被编译的文件名称)。运用如下:

if(element_count==0)
cerr<<"Error:"<<__file>
<<":line"<<__line>
<<"element_count must be non-zero.\n";

13. __DATE__:编译日期,当时被编译文件的编译日期

14. __TIME__:编译时刻,当时被编译文件的编译时刻


什么是预处理指令?

预处理指令是以#号最初的代码行。#号有必要是该行除了任何空白字符外的榜首个字符。#后是指令关键字,在关键字和#号之间答应存在恣意个数的空白字符。整行句子构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转化。

曾经没有介意的学者留意了,预处理指令是在编译器进行编译之前进行的操作.预处理进程扫描源代码,对其进行开端的转化,发生新的源代码供给给编译器。可见预处理进程先于编译器对源代码进行处理。在许多编程言语中,并没有任何内涵的机制来完结如下一些功用:在编译时包括其他源文件、界说宏、依据条件决议编译时是否包括某些代码(避免重复包括某些文件)。要完结这些作业,就需求运用预处理程序。尽管在现在绝大多数编译器都包括了预处理程序,但一般以为它们是独立于编译器的。预处理进程读入源代码,检查包括预处理指令的句子和宏界说,并对源代码进行呼应的转化。预处理进程还会删去程序中的注释和剩余的空白字符。


#include包括一个源代码文件

这个预处理指令,我想是见得最多的一个,简略说一下,榜首种办法是用尖括号把头文件括起来。这种格局告知预处理程序在编译器自带的或外部库的头文件中查找被包括的头文件。第二种办法是用双引号把头文件括起来。这种格局告知预处理程序在当时被编译的应用程序的源代码文件中查找被包括的头文件,假如找不到,再查找编译器自带的头文件。选用两种不同包括格局的理由在于,编译器是安装在公共子目录下的,而被编译的应用程序是在它们自己的私有子目录下的。一个应用程序既包括编译器供给的公共头文件,也包括自界说的私有头文件。选用两种不同的包括格局使得编译器能够在许多头文件中区别出一组公共的头文件。


头文件中应该写什么

经过上面的评论,咱们能够了解到,头文件的效果便是被其他的 .cpp 包括进去的。它们本身并不参加编译,但实际上,它们的内容却在多个 .cpp 文件中得到了编译。经过"界说只能有一次"的规矩,咱们很简略能够得出,头文件中应该只放变量和函数的声明,而不能放它们的界说。由于一个头文件的内容实际上是会被引进到多个不同的 .cpp 文件中的,并且它们都会被编译。放声明当然没事,假如放了界说,那么也就相当于在多个文件中呈现了关于一个符号(变量或函数)的界说,纵然这些界说都是相同的,但关于编译器来说,这样做不合法。

所以,应该记住的一点便是,.h头文件中,只能存在变量或许函数的声明,而不要放界说。即,只能在头文件中写形如:extern int a; 和 void f(); 的句子。这些才是声明。假如写上 int a;或许 void f() {} 这样的句子,那么一旦这个头文件被两个或两个以上的 .cpp 文件包括的话,编译器会立马报错。(关于 extern,前面有评论过,这儿不再评论界说跟声明的区别了。)

但是,这个规矩是有三个破例的:

  • 一,头文件中能够写 const 目标的界说。由于大局的 const 目标默许是没有 extern 的声明的,所以它只在当时文件中有用。把这样的目标写进头文件中,即便它被包括到其他多个 .cpp 文件中,这个目标也都只在包括它的那个文件中有用,对其他文件来说是不行见的,所以便不会导致多重界说。一同,由于这些 .cpp 文件中的该目标都是从一个头文件中海尔兄弟包括进去的,这样也就确保了这些 .cpp 文件中的这个 const 目标的值是相同的,可谓一箭双雕。同理,static 目标的界说也能够放进头文件。
  • 二,头文件中能够写内联函数(inline)的界说。由于inline函数是需求编译器在遇到它的当地依据它的界说把它内联打开的,而并非是一般函数那样能够先声明再链接的(内联函数不会链接),所以编译器就需求在编译时看到内联函数的完好界说才行。假如内联函数像一般函数相同只能界说一次的话,这事儿就难办了。由于在一个文件中还好,我能够把内联函数的界说写在最开端,这样能够确保后边运用的时分都能够见到界说;但是,假如我在其他的文件中还运用到了这个函数那怎么办呢?这简直没什么太好的处理办法,因而 C++ 规则,内联函数能够在程序中界说屡次,只需内联函数在一个 .cpp 文件中只呈现一次,并且在所有的 .cpp 文件中,这个内联函数的界说是相同的,就能经过编译。那么明显,把内联函数的界说放进一个头文件中是十分正确的做法。
  • 三,头文件中能够写类(class)的界说。由于在程序中创立一个类的目标时,编译器只要在这个类的界说彻底可见的情况下,才干知道这个类的目标应该怎么布局,所以,关于类的界说的要求,跟内联函数是根本相同的。所以把类的界说放进头文件,在运用到这个类的 .cpp 文件中去包括这个头文件,是一个很好的做法。在这儿,值得一提的是,类的界说中包括着数据成员和函数成员。数据成员是要比及详细的目标被创立时才会被界说(分配空间),但函数成员却是需求在一开端就被界说的,这也便是咱们一般所说的类的完成。一般,咱们的做法是,把类的界说放在头文件中,而把函数成员的完成代码放在一个 .cpp 文件中。这是能够的,也是很好的办法。不过,还有另一种办法。那便是直接把函数成员的完成代码也写进类界说里边。在 C++ 的类中,假如函数成员在类的界说体中被界说,那么编译器会视这个函数为内联的。因而,把函数成员的界说写进类界说体,一同放进头文件中,是合法的。留意一下,假如把函数成员的界说写在类界说的头文件中,而没有写进类界说中,这是不合法的,由于这个函数成员此刻就不是内联的了。一旦头文件被两个或两个以上的 .cpp 文件包括,这个函数成员就被重界说了。

头文件中的保护措施

考虑一下,假如头文件中只包括声明句子的话,它被同一个 .cpp 文件包括再屡次都没问题——由于声明句子的呈现是不受约束的。但是,上面评论到的头文件中的三个破例也是头文件很常用的一个用处。那么,一旦一个头文件中呈现了上面三个破例中的任何一个,它再被一个 .cpp 包括屡次的话,问题就大了。由于这三个破例中的语法元素尽管"能够界说在多个源文件中",但是"在一个源文件中只能呈现一次"。想象一下,假如 a.h 中含有类 A 的界说,b.h 中含有类 B 的界说,由于类B的界说依靠了��� A,所以 b.h 中也 #include了a.h。现在有一个源文件,它一同用到了类A和类B,所以程序员在这个源文件中既把 a.h 包括进来了,也把 b.h 包括进来了。这时,问题就来了:类A的界说在这个源文件中呈现了两次!所以整个程序就不能经过编译了。你或许会以为这是程序员的失误——他应该知道 b.h 包括了 a.h ——但事实上他不应该知道。

运用 "#define" 合作条件编译能够很好地处理这个问题。在一个头文件中,经过 #define 界说一个姓名,并且经过条件编译 #ifndef...#endif 使得编译器能够依据这个姓名是否被界说,再决议要不要继续编译该头文中后续的内容。这个办法尽管简略,但是写头文件时必定记住写进去。


#define界说宏

有关#define这个宏界说,在C言语中运用的许多,由于#define存在一些缺乏,C++着重运用const来界说常量。宏界说了一个代表特定内容的标识符。预处理进程会把源代码中呈现的宏标识符替换成宏界说时的值。记住仅仅是进行标识符的替换。下面罗列一些#define的运用:

  1. 用#define完成求最大值和最小值的宏
#include 
#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))C/C++预处理指令和常用预处理常量;
#endif
#ifdef MIN
printf("3 and 5 the min is:%d\n",MIN(3,5));
#endif
return 0;
}
/*
* (1)三元运算符要比if,else效率高
* (2)宏的运用必定要仔细,需求把参数当心的用括号括起来,
* 由于宏仅仅简略的文本替换,不留意,简略引起歧义过错。
*/

2、宏界说的过错运用

#include 
#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))
* 所以,尽量运用小括号,将参数括起来。
*/

3、宏参数的衔接

#include 
#define STR(s) #s
#define CONS(a,b) (int)(a##e##b)
int main(void)
{
#ifdef STR
printf(STR(VCK));
#endif
#ifdef CONS
printf("\n%d\n"C/C++预处理指令和常用预处理常量,CONS(2,3));
#endif
return 0;
}
/* (绝大多数是运用不到这些的,运用到的话,检查手册就能够了)
* 榜首个宏,用#把参数转化为一个字符串
* 第二个宏,用##把2个宏参数粘合在一同,及aeb,2e3也便是2000
*/

4、用宏得到一个字的高位或低位的字节

#include 
#define WORD_LO(xxx) ((byte)((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte)((word)(xxx) >> 8))
int main(void)
{
return 0;
}
/*
* 一C/C++预处理指令和常用预处理常量个字2个字节,取得低字节(低8位),与255(0000,0000,1111,1111)按位相与
* 取得高字节(高8位),右移8位即可。
*/

5、用宏界说得到一个数组所含元素的个数

#include 
#define ARR_SIZE(a) (sizeof((a))/sizeof((a[0])))
int main(void)
{
int array[100];
#ifdef ARR_SIZE
printf("array has %d items.\n",ARR_SIZE(array));
#endif
return 0;
}

#ifdef,#ifndef,#endif...的运用

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

  1. 示例1:
#include 
#include
#define DEBUG
int main(void)
{
int i = 0;
char c;
while(1)
{
i++;
c = getchar();
if('\n' != c)
{
getchar();
}
if('q' == c || 'Q' == c)
{
#ifdef DEBUG//判别DEBUG是否被界说了
printf("We get:%c,about to exit.\n",c);
#endif
break;
}
else
{
printf("i = %d",i);
#ifdef DEBUG
printf(",we get:%c",c);
#endif
printf("\n");
}
}
printf("Hello World!\n");
return 0;
}
/*#endif用于中止#if预处理指令。*/

2、ifdef 和 #ifndef

#include 
#define DEBUG
main()
{
#ifdef DEBUG
printf("yes ");
#endif
#ifndef DEBUG
printf("no ");
#endif
}
//#ifdefined等价于#ifdef;
//#if!defined等价于#ifndef

3、#else指令

4、#elif指令

5、其他一些指令

#error指令将使编译器显现一条过错信息,然后中止编译。
#line指令能够改动编译器用来指出正告和过错信息的文件号和行号。
#pragma指令没有正式的界说。编译器能够自界说其用处。典型的用法是制止或答应某些烦人的正告信息。

作业中常常这样运用宏:

1. 常常运用宏来调试代码:

#if 0
///< 旧的代码(或函数) (旧的代码, 将会被预处理的时分,屏蔽掉, 不进行编译)
#else
///< 新的代码(或函数)
#endif

#ifndef JOE_DEBUG
///< 新的代码(或函数)
#else
///< 旧的代码(或函数) (旧的代码, 将会被预处理的时分,屏蔽掉, 不进行编译)
#endif

#ifdef C/C++预处理指令和常用预处理常量Q_DEBUG
///< 新的代码(或函数)
#else
///< 旧的代码(或函数) (旧的代码, 将会被预处理的时分,屏蔽掉, 不进行编译)
#endif


经过以上相似的办法, 能够避免由于过多的修正代码, 而把代码修正的乌烟瘴气. 主张修正代码的时分, 做到保护好曾经的代码, 尽量不进行代码的删去操作. 牢记, 能不删去, 就不删去...不要养成顺手就删去的习气. 要养成运用宏和注释代码的习气.

2. 运用宏来依据不同的渠道包括不同的文件. 许多时分, 咱们的代码是需求跨体系渠道编译和运转的. 比方: 一个小功用代码, 需求既能够在Win下面运转, 还要能够在Max, linux上面运转. 但是, 由于体系的不相同, 有些时分, 头文件的包括的姓名是不相同的. 所以,这时分, 便是用到了宏. 由于咱们运用编程东西分不同的体系渠道, 编程东西本身的环境就会包括不同渠道的体系宏, 假定OS_Win, OS_Mac, OS_Linux 别离代码三种体系不同的宏. 并且,Win版别的编程东西中现已界说了OS_Win, 相似的Mac下, 编程东西界说的是OS_Mac, Linux...

#ifdef OS_Win
#include
#endif

#ifdef OS_Mac
#include
#endif

#ifdef OS_Linux
#include
#endif

/** 不只运用在头文件的包括. 并且,关于不同的体系渠道. 你也能够运用不同的代码结构. */