预处理详解

发布时间:2024年01月19日

目录

1.预定义符号

2.#define定义常量

3.#define定义宏

4.带有副作用的宏参数

5.宏替换的规则

6.宏函数的对比

7.#和##

8.#undef

9.条件编译

10.头文件的包含

11.嵌套文件包含


1.预定义符号

已经设置的预定义符号,可以直接使用,预定义符号在预处理期间处理.

#include <iostream>

int main()
{
	using namespace std;
	cout << __FILE__ << endl;//编译的源文件
	cout << __LINE__ << endl;//当前文件的行号
	cout << __DATE__ << endl;//编译的日期
	cout << __TIME__ << endl;//编译的时间
	//cout << __STDC__ << endl;//如果编译器支持ANSI C,其值为1,否则未定义

	return 0;
}

?

?

2.#define定义常量

基本形式:

#define name stuff

?

#include <iostream>
#define MAXX 100//定义标识符常量
#define ll long long//为关键字创建更短的名字
#define p a
#define do_10 for(int i = 0; i < 10; i++)//用符号来表示这个实现过程
#define CASE  break; case//定义CASE,使其不用多次写break;
#define DEBUG_PRINT cout << __FILE__ << endl\
                         << __LINE__ << endl\
						 << __DATE__ << endl\
						 << __TIME__ << endl;// \是续行符

int main()
{
	using namespace std;
	ll a = 100;
	cout << p << endl << endl;;
	do_10
	{
		cout << i << endl;
	}
	cout << endl;
	switch (5)
	{
			case 1:cout << 1 << endl;
			CASE 2:cout << 2 << endl;
			CASE 3:cout << 3 << endl;
			CASE 4:cout << 4 << endl;
			CASE 5:cout << 5 << endl;
			CASE 6:cout << 6 << endl;
	}
	cout << endl;
	DEBUG_PRINT;

	return 0;
}

?

?注意!#define在预编译阶段直接把定义展开,所以一般#define定义后面不加;

?

3.#define定义宏

#define 定义宏允许把参数代替到文本中

基本形式:

#define name(parament-list) stuff//name后面的括号必须紧邻
#include <iostream>
#define Add(x, y) x + y

int main()
{
	using namespace std;
	int ret = Add(42, -13);
	cout << ret << endl;

	return 0;
}

?

?再来看看这个代码:

#include <iostream>
#define Mul(x,y) x * y

int main()
{
	using namespace std;
	int ret = Mul(3 + 3, 4 + 2);
	cout << ret << endl;

	return 0;
}

?

?

?为什么不是36而是17呢,前面说过了,预处理是直接将#define替换,上面替换后:

3 * 4
3 + 3 * 4 + 2

?由优先级和结合律易得17,所以定义宏时要将参数括上括号,再将总体加上括号:

#include <iostream>
#define Mul(x,y) ((x) * (y))

int main()
{
	using namespace std;
	int ret = Mul(3 + 3, 4 + 2);
	cout << ret << endl;

	return 0;
}

?

4.带有副作用的宏参数

副作用就是表达式求值时出现的永久性效果,当宏参数在宏定义中出现超过一次的时候,参数带有副作用,使用宏可能会有危险。

#include <iostream>
#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main()
{
	using namespace std;
	int a = 10;
	int b = 6;
	int ret = MAX(a++, b++);
	cout << ret << endl;
	cout << a << endl << b << endl;

	return 0;
}

?

?a++与b++首先在比大小时各自加1,此时a == 11 ;b == 7; 判断为真,执行(x),此时返回 a == 11的值,返回后a自增1 ,此时a == 12。

5.宏替换的规则

1.调用宏时,首先对参数进行检查,看看是否包含任何#define定义的符号,有则首先被替换。

#include <iostream>
#define A 100
#define B 200
#define MAX ((A) > (B) ? (A) : (B))

int main()
{
	using namespace std;
	int ret = MAX;
	cout << ret << endl;

	return 0;
}

?2.替换文本随后被插入到程序中原来文本的位置,宏,参数命被它们的值替换

3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号,如果是,就重复上述处理过程。?

4.不能递归#define定义,即不能在#define里面再#define,但可以出现其他#define 的符号。

?

5.当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。比如“A”就是“A”,并不会被替换成“100”.?

6.宏函数的对比

宏函数常被应用于执行简单的运算,因为:

1.调用常规函数并返回可能比宏函数执行时间要长,因为常规函数要由开辟函数栈帧等一系列操作,宏函数对比常规函数执行小型运算规模和速度会更胜一筹。

2.我们已经注意到了,上面定义宏函数我们并没有给出返回值和参数的类型,而常规函数需要指出类型。

但宏函数也有其缺点:

1.使用宏定义时,预编译时属于文本替换,除非宏函数比较短,否则可能大幅度增加程序的长度。

2.宏函数没办法调试,常规函数可以进入函数内部逐句调试。

3.宏函数没指出类型,可能不够严谨;

4.宏可能出现运算优先级问题(记得加括号)

宏也可以做到常规函数做不到的事情,宏的参数可以出现类型,常规函数做不到这一点:

#include <iostream>
#define MALLOC(num,type) (type*)malloc(num * sizeof(type))

int main()
{
	using namespace std;
	int* ptr = MALLOC(10, int);//替换后(int*)malloc(10 * sizeof(int))
	for (int i = 0; i < 10; i++)
	{
		*(ptr + i) = i + 10000;
	}
	for (int i = 0; i < 10; i++)
	{
		cout << *(ptr + i) << endl;
	}

	return 0;
}

?

7.#和##

#运算符将宏的一个参数转换为字符串字面量,它仅允许出现在带参数的宏的替换列表中。

#include <iostream>
#include <cstdio>
#define Print(a) printf("The value of " #a " is %d",a) 

int main()
{
	using namespace std;
	int b = 10;
	Print(b);

	return 0;
}

?

## 可以把位于两边的符号合成一个符号,它允许宏定义从分离的文本片段创建标识符,##被称为记号粘合,这样连接必须产生一个合法的标识符,否则其结果未定义。

应用于宏定义常规函数:

#include <iostream>
#define MAX(type)\
type type##_Max(type x,type y)\
{                          \
	return ((x) > (y) ? (x) : (y));\
}
MAX(int);
MAX(double);

int main()
{
	using namespace std;
	int ret = int_Max(2, 3);
	cout << ret << endl;
	double ret2 = double_Max(1.3, 4.5);
	cout << ret2 << endl;

	return 0;
}

这样就避免了类型不同但结构相似的函数的重复编写。?如果没有##,上面就只有type_Max()一个函数。

8.#undef

移除一个宏定义

9.条件编译

常见的条件编译指令:

#if 常量表达式
//...
#endif


#if 常量表达式
//...
#elif 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif


//判断是否被定义
#if defined(symbol)
//...
#endif

#ifdef symbol
//...
#endif

#if !defined(symbol)
//...
#endif

#ifndef symbol
//...
#endif
#include <iostream>

int main()
{
	using namespace std;
	int arr[10];
	int j = 0;
	for (int &i : arr)
	{
		i = j + 1;
		j++;
	}
#if 1
	for (int i : arr)
	{
		cout << i << endl;
	}
#endif

	return 0;
}

?

?

#include <iostream>
#define MAXX 100

int main()
{
	using namespace std;
#if MAXX == 1
	cout << 1 << endl;
#elif MAXX == 90
	cout << 2 << endl;
#elif MAXX == 100
	cout << 3 << endl;
#else
	cout << 4 << endl;
#endif

	return 0;
}

?

?

#include <iostream>
#define MAXX 100

int main()
{
	using namespace std;
#if defined(MAXX)
	cout << 1 << endl;
#endif

#ifdef LIN
	cout << 2 << endl;
#endif

#if !defined(LIN)
	cout << 3 << endl;
#endif

#ifndef MAXX
	cout << 4 << endl;
#endif

	return 0;

?

?条件编译可以嵌套:

#include <iostream>
#define MAXX 100

int main()
{
	using namespace std;
#if MAXX == 100
	#ifdef MAXX
	cout << 1 << endl;
	#endif
	#ifndef LIN
	cout << 2 << endl;
	#endif
	#if MAXX == 99
	cout << 3 << endl;
	#else
	cout << 4 << endl;
	#endif
#endif

	return 0;
}

?

10.头文件的包含

头文件包含形式有:

#include <>//本地文件包含
#include ""//库文件包含

?两种的区别在于查找顺序不同:

#incldue "":先在源文件所在目录下查找,如果该文件未找到,编辑器就像查找库函数头文件一样在标准位置查找头文件。

#include <>:直接去标准位置查找,找不到就报错。

对于本地文件和库文件,都可以用#include “”,但对于库文件来说,最好用#include<> ,因为#include ""多了一步查找,对于库文件是没必要的,会导致查找效率变低。

11.嵌套文件包含

?这样预编译时会进行5次文本替换,如果工程量大,预处理后代码量剧增,应该避免重复引用。

可以使用条件编译来解决头文件重复引用:

?也可以用#pragma once

?这样头文件就只引入一次。

文章来源:https://blog.csdn.net/cookies_s_/article/details/135688383
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。