预处理

宏定义

只是替换文本,需要加上括号,这里很容易掉入坑中,因为先替换后计算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 变量case
#define N 2+9
int a = N*N; // 预期输出121
cout << a << endl; // 输出29,因为a=2+9*2+9=29,可见只是简单的替换,应更改为#define N (2+9)

// 函数case
#define area(x) x*x
int y = area(2+2); // 预期输出16
cout << y << endl; // 输出8,因为y=2+2*2+2=8

// 尝试解决函数case
#define area(x) (x)*(x)
int yy = area(2+2); // 预期输出16
cout << yy << endl; // 输出16,因为yy=(2+2)*(2+2)=16
int yyy = area(2+2)/area(2+2); // 预期输出1
cout << yyy << endl; // 输出16,因为yyy=(2+2)*(2+2)/(2+2)*(2+2)=16

// 唯一解
#define area(x) ((x)*(x))
int yyyy = area(2+2)/area(2+2); // 预期输出1
cout << yyyy << endl; // 输出1,因为yyyy=((2+2)*(2+2))/((2+2)*(2+2))

do…while(0)

用来让多行语句变成非复合语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// do...while(0)
#define Foo(x) do {\
statement one;\
statement two;\
}while(0) // 没有分号

// 宏定义函数
#define Foo(x) {\
statement one;\
statement two;\
}

if (condition)
Foo(x); // 如果是宏定义函数,one two有两个分号,而if没有花括号,所以会导致编译错误
else
...;

条件编译

1
2
3
4
5
6
7
8
9
10
11
#ifdef 标识符 // 当标识符被定义过(一般用#define定义),则编译1,否则编译2,#else部分也可以没有
程序段1
#else
程序段2
#endif

#if 表达式 // 表达式为真,编译1
程序段1
#else
程序段2
#endif

调试代码巧用条件编译

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;
#define _DEBUG_ // 目的在于定义_DEBUG_标识符,后面写什么字符串都无所谓,甚至可以不写字符串
int main() {
int x = 10;
#ifdef _DEBUG_
cout << "File:"<<__FILE__<<",Line:"<<__LINE__<<",x:"<<x<<endl;
#else
printf("x = %d\n", x);
cout << x << endl;
#endif
return 0;
}

extern “C”块与条件编译结合,在C/C++混合编程的环境,extern “C”就是告诉编译器按C编译(比如没有C++的函数重载等等),所以这种方法可以保证C/C++的兼容性

1
2
3
4
5
6
7
8
9
#ifdef __cplusplus
extern "C" {
#endif

...

#ifdef __cplusplus
}
#endif

宏定义常量与const常量的区别

#define pi 3.1415926

  1. 编译器处理阶段不同:宏定义常量在预处理阶段展开;const常量在编译运行阶段使用
  2. 类型和安全检查不同:宏定义常量没有类型检查,仅仅展开;const常量有具体类型,在编译阶段执行类型检查
  3. 存储方式不同:宏定义常量不分配内存,仅仅是展开而已;const常量会在内存中分配,
  4. 常量只在类中有效只能用const,而且const数据成员只在某个对象生存期内是常量,对于整个类而言是可变的,因为类可以有多个对象,每个对象的const成员值可以不同(不能在类中初始化const数据成员)

宏定义函数与内联函数的区别

define MAX(a, b) ((a)>(b)?(a):(b))

  1. 编译器处理阶段不同:宏定义是由预处理器进行宏展开,函数内联是通过编译器来控制实现
  2. 类型和安全检查不同:宏定义函数没有类型检查
  3. 存储方式不同:内联函数是代码段,直接嵌入,而宏函数是简单的替换
  4. 内联函数在普通函数的前面加一个关键字 inline 来标识。编译器对内联函数会在编一阶段将其展开,而不会把它当做一个函数,这大大减少了函数调用的开销,因为函数调用需要函数栈、压栈blabla的

添加inline关键字只是向编译器建议内联,具体是否内联还要看编译器的想法,如果函数内有循环体或switch或代码很长,则很可能不内联

添加inline必须要在定义前,只在声明前添加inline不会内联