Preprocessor directives:预处理指令,宏定义,行控制,条件包含,错误提示,源文件包含,Pragma

预处理器指令

预处理器指令是代码中的哪些以#开头的行,这些行不是程序的一部分,而是作为预处理器的标识。预处理器在开始编译前检查代码,并且在regularstatements生成代码前处理掉所有的指令。
这些预处理器指令只占代码的一行。一旦检测到换行字符,预处理器指令就结束了。预处理器指令的末尾不需要分号
(;)。预处理器指令延伸到多行的唯一方法是在一行的末尾添加反斜杠(\)

1.宏定义指令(#define,#undef)
你可以使用#define来定义一个预处理器宏,它的语法是:
#define identifier replacement

当预处理器遇到这个指令,就会把identifier替换成replacement。这个replacement可以是一个表达式,声明,代码块或者是任何东西。严格的说,预处理器不懂的C++,它只是在遇到identifier的时候替换成replacement

1
2
3
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
int table2[TABLE_SIZE]; 

在预处理器替换了TABLE_SIZE后,代码就等价于这样:

1
2
int table1[100];
int table2[100]; 

#define也可以带参数来定义函数宏

 
#define getmax(a,b) a>b?a:b 

这不仅会把getmax后面带有两个参数的情况替换成后面的表达式,而且会把每个参数替换成每个的指令,就好像一个函数一样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// function macro
#include <iostream>
using namespace std;

#define getmax(a,b) ((a)>(b)?(a):(b))

int main()
{
  int x=5, y;
  y= getmax(x,2);
  cout << y << endl;
  cout << getmax(7,x) << endl;
  return 0;
}
5
7

定义宏不会影响块结构。宏会一直存在知道遇到#undef预处理器指令:

1
2
3
4
5
#define TABLE_SIZE 100
int table1[TABLE_SIZE];
#undef TABLE_SIZE
#define TABLE_SIZE 200
int table2[TABLE_SIZE];

生成的代码如下:

1
2
int table1[100];
int table2[200];

函数宏定义在替换串里接受两个特殊的操作符(###):

如果#被用在替换串的一个参数前面,这个参数会被替换成一个字符串(就像是被双引号包括一样)。

1
2
#define str(x) #x
cout << str(test);

这会被翻译成:

 
cout << "test";

##可以连接两个参数,两个之间没有空格:
Theoperator ## concatenatestwo arguments leaving no blank spaces between them:

1
2
#define glue(a,b) a ## b
glue(c,out) << "test";

翻译结果是:

 
cout << "test";

因为预处理器替换是在C++语法检查之前,所以宏定义是一个巧妙的特性。但是,请注意:因为在很多情况下复杂宏的语法表达和一般的程序员期望的不一样,所以带有复杂宏的代码不易读。

2.条件包含指令(#ifdef,#ifndef, #if, #endif, #else and #elif)

这些指令允许在某个条件满足是包含或者抛弃代码的一部分。
#ifdef 只有在宏的参数被定义,无论它的值是什么的时候,允许程序的某一部分被编译:

1
2
3
#ifdef TABLE_SIZE
int table[TABLE_SIZE];
#endif  

在这种情况下,inttable[TABLE_SIZE];这行代码只会在TABLE_SIZE在之前通过#define定义后被编译,TABLE_SIZE的值不造成影响。如果TABLE_SIZE没有被定义,这行代码就不会被包含进程序编译。
#ifndef恰恰相反:#ifndef#endif指令之间的代码只有在之前没有定义过的情况下被编译。例如:

1
2
3
4
#ifndef TABLE_SIZE
#define TABLE_SIZE 100
#endif
int table[TABLE_SIZE];

在这种情况下,如果TABLE_SIZE宏还没有被定义,将会定义一个宏TABLE_SIZE,且值为100.如果已经存在TABLE_SIZE宏,将不会改变宏的值,因为#define指令根本没有执行。
#if,#elseand#elif(i.e.,"else if") 指令在特定的条件满足时才会使得它们包围的代码被编译。#ifor#elif后的条件可以只可以是包含宏的常量表达式,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#if TABLE_SIZE>200
#undef TABLE_SIZE
#define TABLE_SIZE 200
 
#elif TABLE_SIZE<50
#undef TABLE_SIZE
#define TABLE_SIZE 50
 
#else
#undef TABLE_SIZE
#define TABLE_SIZE 100
#endif
 
int table[TABLE_SIZE]; 

注意#if,#elifand#else连锁指令的整个结构是以#endif结束的
#ifdefand#ifndef的行为可以分别由#if或者#elif指令加上defined!defined来替换

1
2
3
4
5
6
#if !defined TABLE_SIZE
#define TABLE_SIZE 100
#elif defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
int table[TABLE_SIZE];
#endif 

3.行控制(#line)

当我们在编译程序时出现了一些错误,编译器会显示包含发生错误的文件名的错误信息和和一个行号,这样能更加准确的找到产生错误的代码。
#line指令可以帮助我们控制这两件事,当错误发生后我们需要的源文件里的行号和文件名。它的格式如下:
#line number"filename"
number规定了下一行代码的行号。接下来的行号会依次递增(步长为1)。
"filename"作为可选参数可以重新定义要显示的文件名。例如:

1
2
#line 20 "assigning variable"
int a?;

这段代码会产生一个错误,且会被显示成错误发生在文件"assigningvariable"line20

4.错误提示指令(#error)

当发现错误提示指令,就会中止编译,并产生一个以它的参数标识的编译错误:

1
2
3
#ifndef __cplusplus
#error A C++ compiler is required!
#endif 

这个例子会中止编译处理,如果宏__cplusplus没有被定义(这个宏名称默认由C++编译器定义)

5.源文件包含指令 (#include)

当预处理器找到一个#include指令,就会把它替换成头文件或者其他文件的内容。有两种使用#include的方式:

1
2
#include <header>
#include "file" 

第一种情况下,头文件在尖括号中间.这是被用来包含系统提供的头文件的,比如说组成标准库的头文件(iostreamstring等)。无论这些头文件是真的文件还是以其他的形式存在是不是实现定义,都应该用这个指令包含进来。
另一种情况是#include使用引号包含一个文件。这个文件按照实现定义的方式查找,一般包含在当前路径下。如果按照这种方式没有找到文件,编译器就会认为这是头文件包含,就好像把引号换成了尖括号。

Pragma指令 (#pragma)

这个指令用来设定编译器的不同参数。这些参数由平台和你使用的编译器一起决定。你可以查看你的编译器的指南或参考来了解你可以通过#pragma设置哪些可能的参数.
如果你的编译器不支持#pragma某个特殊的参数,则它会被忽略而不会产生语法错误。

6.预定义宏名称

下面的宏名称一般会被提前定义好(它们都是以下划线_开始和结束);

macro

value

__LINE__

表示当前正在编译的代码的行号的整形变量(可能不是真正的行号)。

__FILE__

表示当前正在编译的文件的假定文件名(可能不是真正的名字)。

__DATE__

表示编译器开始编译的日期,格式是“Mmmdd yyyy”

__TIME__

表示编译器开始编译的时间,格式是“hh:mm:ss”

__cplusplus

一个整形变量。所有的C++编译器都定义了一个这样的常量(值不确定)。它的值取决于编译器支持的标准的版本。

  • 199711L:ISO C++ 1998/2003

  • 201103L:ISO C++ 2011

不符合标准的编译器往往把这些常量定义为最多不多于5位的长整形数字。有许多编译器都不能完全符合标准,所以他们的常量常常和上面的数字都不一样。

__STD_HOSTED__

1 如果是完全实现(所有的标准头文件都可用)
0 其他情况。


以下的宏是可选的,主要依赖于特性是不是可用:

macro

value

__STDC__

C:如果定义成1,那么实现是遵守标准C的。
C++:实现定义(Implementationdefined)

__STDC_VERSION__

C:

  • 199401L:ISO C 1990,修订(Amendment) 1

  • 199901L:ISO C 1999

  • 201112L:ISO C 2011

C++:实现定义(Implementationdefined)

__STDC_MB_MIGHT_NEQ_WC__

1如果多字节编码可能会给同一个字符不同的值。

__STDC_ISO_10646__

一个形如yyyymmL的值,表示Unicode标准的日期后面是wchar_t字符。

__STDCPP_STRICT_POINTER_SAFETY__

1如果实现是严格指针安全的(get_pointer_safety)

__STDCPP_THREADS__

1如果程序支持多线

个别的实现可能会定义另外的常数。
例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
// standard macro names
#include <iostream>
using namespace std;

int main()
{
  cout << "This is the line number:" 
  cout << __LINE__<<”.\n”;
  cout << "This is the file:"
  cout << __FILE__ << ".\n";
  cout << "Its compilation began:" << __DATE__;
  cout << " at " << __TIME__ << ".\n";
  cout << "The compiler gives a __cplusplus value of " << __cplusplus;
  return 0;
}
This is the line number 7.
This is the file:/home/jay/stdmacronames.cpp.
Its compilation began:Nov  1 2005 at 10:12:29.
The compiler gives a __cplusplus value of 1

原资料地址,翻译不当之处,请参看原文,最好能帮忙指正,谢谢



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值