预处理器
预处理发生在编译之前,预处理输出的是一个单一的文件,这个文件被送到编译器,进行编译。
预处理命令
每条预处理命令都控制预处理器的行为。每条预处理命令占据一行,有以下的格式:
* # character
* 预处理命令(one of define, undef, include, if, ifdef, ifndef, else, elif, endif, line, error, pragma)
* 参数
* 换行符
条件编译命令
预处理命令可以条件编译源程序的某一部分。指令包括:
#if expression
#ifdef expression
#ifndef expression
#elif expression
#else
#endif
expression must be const expression
Example
#define ABCD 2
#include <iostream>
int main(){
#ifdef ABCD
std::cout<<"1: yes\n";
#else
std::cout<<"1: no\n";
#endif
#ifndef ABCD
std::cout<<"2: no1\n";
#elif ABCD==2
std::cout<<"2: yes\n";
#else
std::cout << "2: no2\n";
#endif
#if !defined(DSDE) && (ABCD < 2*4 - 3)
std::cout<<"3: yes\n";
#endif
}
//output:
//1: yes
//2: yes
//3: yes
替换文本宏命令
语法:
#define identifier replacement-list(optional) (1)
#define identifier( parameters ) replacement-list (2)
#define identifier( parameters, ... ) replacement-list (3) (since C++11)
#define identifier( ... ) replacement-list (4) (since C++11)
#undef identifier (5)
#define
不能重复定义相同的宏,否则,编译会报错。但是,如果重复定义的宏完全相同,则,没有任何问题。
object-like macro
(1)的形式。
function-like macro
(2)(3)(4)的形式
(2) 参数个数固定
(3)(4) 可变参数个数,参数能够被访问通过:VA_ARGS
# and ## operators
#被用在replacement-list 中,放在变量前,目的是字符串化该变量。生成的结果是“...”。
#define showlist(...) puts(#__VA_ARGS__)
showlist(); // expands to puts("")
showlist(1, "x", int); // expands to puts("1, \"x\", int")
##用来连接两个identifier
#include <iostream>
#define FUNCTION(name, a) int fun_##name(){return a;}
FUNCTION(abcd, 12)
FUNCTION(fff, 2)
FUNCTION(qqq, 23)
#undef FUNCTION
#define FUNCTION 34
#define OUTPUT(a) std::cout<<#a<<std::endl;
int main()
{
std::cout << "abcd: " << fun_abcd() << '\n';
std::cout << "fff: " << fun_fff() << '\n';
std::cout << "qqq: " << fun_qqq() << '\n';
std::cout << FUNCTION << '\n';
OUTPUT(million); //note the lack of quotes
}
#include命令
include其他源文件到当前文件中,并且,放在该指令之后。语法:
#include <filename> (1)
#include "filename" (2)
#include 搜索文件的路径为:standard include directories 。标准c库和c++库的目录都被加入到了standard include directories。standard include directories 通常通过编译选项来定义。如果在standard include directories没有搜索到该文件,程序将会报错。
#include "filename" 搜索文件的路径为: 先在当前文件所在目录下搜索,如果没有搜索到,将在standard include directories下搜索。如果,都没有搜索到,程序报错。
注意问题:
一个文件可能被重复Include,或者递归地include, 为了避免这个问题,通常要在include的文件中,加入一些预处理命令(通常是头文件)。
#ifndef FOO_H_INCLUDED /* any name uniquely mapped to file name */
#define FOO_H_INCLUDED
// contents of the file are here
#endif
Example:
#ifndef TEXT
#define TEXT
#include __FILE__
int main(){
//如果不防止重复include该文件,将会不停include文件,程序ill
}
#endif
Error 命令
语法:
#error error_message
用于对编译前的检测,程序遇到error,就会停止编译。
编译器自定义指令
语法: #pragma pragma_paramsC++ 标准不包含这些指令,但是编译器厂商会根据自己的需要,定义自己的指令。如果程序遇到不认识的#pragma指令,会直接跳过,不会报错。
改变文件名和行号指令
语法:
#line lineno (1)
#line lineno "filename" (2)
改变指令之后的LINE变量和FILE变量。
#include <cassert>
#define FNAME "test.cc"
int main()
{
#line 777 FNAME
assert(2+2 == 5);
}
//output: test: test.cc:777: int main(): Assertion `2+2 == 5' failed.