-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcpp_and_macros
More file actions
177 lines (132 loc) · 6.44 KB
/
cpp_and_macros
File metadata and controls
177 lines (132 loc) · 6.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
Macro(宏)来源于希腊语,意思就是大,远.在计算机科学中指的是规则,这个规则指定了
输入和输出之间的关系(是不是优点类似于函数?).所有的宏都是一个概念,那就是输入
与输出之间的映射关系,这个和函数本质上是相同的.但是宏和函数处理方式基本上是不同
的,比如在C语言中,宏是在preprocess阶段被替换掉,而函数(讨论一般意义上的函数,不
讨论inline函数)在运行时调入栈中运行.而且不同的语言对宏的处理也是不同的,比如C
语言中,宏是无法区分作用域的(编译单元内的作用域),但是在metapost中,恰恰是可以
在宏体里定义局部变量的.
参看:http://en.wikipedia.org/wiki/Macro_%28computer_science%29
下面讨论C语言中的宏.
谈到宏,不得不提到preprocess directives(预处理指令).预处理指令是预处理器在
预处理阶段所要执行的操作.具体在宏中就是执行宏替换功能.基本的一个就是#define.
好,现在先谈谈宏的定义.
宏就是处理输入和输出的规则.所以更直观的一点就是带个输入参数.
#define ADD(foo) foo+10
这样就定义了一个宏add,它的输入是foo,输出是foo+10,规则就是输入加上10.那么在
预处理阶段是如何处理的呢?比如,
bar.c
#define ADD(foo) foo+10
int main()
{
ADD(3);
return 0;
}
这个程序在预处理阶段会被替换成
int main()
{
3+10;
return 0;
}
可以查阅相关的编译器手册,指定相应的选项(options),来察看这个结果.在GCC中,使用
-E选项(其中会输出一些额外的信息,现在不必理会).这个宏,可以带参数,形式上和函数
很像,所以有的人会称它为宏函数,这是个非正式称呼.
更常见的定义宏的方式就是没有输入(即没有参数),比如,
#define PI 3.14
定义了宏PI, 没有输入,输出为3.14,规则就是得到3.14.处理方式同上.形式上,和常量很
相像,所以又有了宏常量的称呼.
#define FREEBSD
定义了宏FREEBSD,这个宏没有输入,没有输出,没有规则,仅仅是定义了这个宏.
但是请记住,宏和函数,在功能上是一样的,都是处理输入得到输出的过程.只不过语言或者
编译器对他们的处理方式不一样.
C语言中,一些内置的宏:
这些内置的宏,不需要自己再define了,直接拿来用就可以.
__LINE__ 表示当前行号.
__FILE__ 表示编译单元的文件名
__DATE__ 表示编译时的日期字符串
__TIME__ 表示编译时的时间字符串
__STDC__ 是否是符合标准,如果是结果为1,否则为0,这个主要是针对编译器编译时的选项.
更多的宏参考在线文档http://gcc.gnu.org/onlinedocs/gcc-4.6.1/cpp/Standard-Predefined-Macros.html#Standard-Predefined-Macros
注意,很多人在调试的时候使用__FUNCTION__(gcc扩展)或者__func__(C99标准),这两个都
不是宏,应为在预处理阶段根本无法确认函数.
宏定义需要注意的问题
1.宏是预处理阶段的替换(expansion),不进行计算,所以要时刻把()加上.
拿上面定义的一个例子来看:
bar.c
#define ADD(foo) foo+10
int i = 2 * ADD(3);
语句会被替换成:
int i = 2 * foo+10;
这样就错误了.本意是要3+10,然后再乘以2,结果为26,现在变成了30了.
#define ADD(foo) (foo+10)
最好把参数全部加上(),对于这个例子,没有必要,但是对其他的例子就很有必要了:
#define MUL(foo, egg) foo*egg
MUL(2+3,4+5);
期望的结果是(2+3)*(4+5)=45,结果却成了2+3*4+5=19.
合理的定义方式是:
#define MUL(foo, egg) ((foo) * (egg))
2.宏是否可以用来定义注释.
#define HEAD_COMMENT /*
#define END_COMMENT */
答案是否定的.注释的处理在预处理指令处理之前,也就是说在define指令被处理之前注释
已经不存在,如果在预处理指定处理过后又出来了定义的话,编译器一定会不认识.
Preprocessor(预处理器),在计算机科学中指的是一个处理代码输入并且把输出做为另外
一个程序输入的程序,它也是一个程序.参看:http://en.wikipedia.org/wiki/Preprocessor
GNU的预处理器可以处理C/C++/Objective-c,因为都和C沾边(或者就是从开始遗留下来的
习惯)所以就叫C PreProcessor(cpp 不是c plus plus:-) )
CPP处理预处理指令,有#include,#define(undef),#if(ifdef,ifndef,else if,elif,endif),#error
,#line, #pragma,#,##,当然预处理也处理注释(comment),而且优先于预处理指令.
1.文件包含(file inclusion)
#include指令
#include <stdio.h> 从标准路径搜索stdio.h
#include "nids.h"
从当前目录开始搜索nids.h如果找不到再到命令行参数指定的目录寻找
最后是标准目录下寻找.
2.宏定义(macros defination)
前述.
3.条件编译(conditinal compilation)
#if __STDC__ == 1
#if defined FREEBSD
#if !defined FREEBSD
#endif
#ifdef FREEBSD
#else if
#define LINUX
#endif
4.错误产生(error generation)
#error this line shouldnt be reached
5.行号控制(line control)
#line 299 "ip.c" 下一行是ip.c的299行
#line 299 下一行是本文件的299行
6 #,##
#和##常常用于宏定义里,这两个是预处理运算符,不是指令,因为预处理指令不能用于宏定义.就像C99添加的_Pragma操作符.
#define tempfile(dir) #dir "/%s"
tempfile(/usr/tmp)被扩展成 "/usr/tmp" "/%s",也就是 "/usr/tmp/%s"
#把宏参数字符串化(C语言表示的字符串),而且这个的"优先级"比较高.
看一个来自cpp手册的例子:
#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo) 由于有#,表示参数首先被字符串化,所以不再有任何扩展.
→ "foo"
xstr (foo) 没有#,所以参数被扩展.
→ xstr (4)
→ str (4)
→ "4"
解释:
s is stringified when it is used in str, so it is not macro-expanded first. But
s is
an ordinary argument to xstr, so it is completely macro-expanded before xstr
itself is
expanded . Therefore, by the time str gets to its argument, it has already been macro-expanded.
#define cat(x,y) x##y
cat(2,3) 扩展为23
cat(wolf,python) 扩展为wlfpython
##把tokens连接起来(tokens concatenation)
通常##两边的tokens可以是标识符,数字或者运算符,不可以是C语言表示的字符串.
比如cat("2","3")是错误的.
7.#pragma
#pragma用于向编译器提供信息.(由于是指令,所以无法用于宏定义,C99定义了
_Pragma运算符)
#pragma once 可以用于防止头文件被重复包含(另外一种方式是使用条件编译)
8.空指令 #
#后面仅跟着一个换行符,就是一个空指令,什么也不做,在预处理阶段没有动它.