【预处理详解】

提示:以下是本篇文章正文内容,下面案例可供参考

1.预定义符号

FILE    //进行编译的源文件
LINE    //文件当前的行号
DATE    //文件被编译的日期
TIME    //文件被编译的时间
STDC    //如果编译器遵循ANSI C,其值为1,否则未定义

来看下面的例子:

#include<stdio.h>
int main()
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("name:%s file:%s line:%d date:%s time:%s i=%d\n", __func__, __FILE__, __LINE__, __DATE__, __TIME__, i);
	}
	return 0;
}

在这里插入图片描述

2.#define 定义标识符

其实在预处理阶段#define 定义标识符,会进行一个替换
来看下面的例子:

#define NUM 100
#define STR "abcdef"
int main()
{
	int num = 0;
	char* str = STR;
	return 0;
}

经过vs设置处理,在test.i的文件中我们可以看到下面预处理之后的结果:
在这里插入图片描述
但是呢有的人就会问在定义时想到,可不可以在后面加分号,答案是不可以的

代码如下(示例):

#define NUM 100;//在这里加分号
#define STR "abcdef"

来看下面的例子:

#define NUM 100;   //在这里加分号
#define STR "abcdef"
int main()
{
	int num = 0;
	char* str = STR;
	if (1)
		num = NUM;
	else
		num = -1;
	return 0;
}

我们编译并且在看一下预处理之后的结果:
在这里插入图片描述
注意 所以我们在使用#define 定义标识符是,尽量不要在后面加符号,避免产生不必要的错误。

3.#define 定义宏

来看下面的例子:

#define MAX(x, y)  (x>y?x:y)

int main()
{
	int a = 10;
	int b = 20;
	int c = MAX(a, b);//其实这条语句被替换为//int c = (a > b ? a : b);

	return 0;
}

我们编译并且在看一下预处理之后的结果:
在这里插入图片描述
我们可以发现#define 定义宏允许把参数替换到文本中
我们可以发现#define 定义宏允许把参数替换到文本中
注意:参数列表的左括号必须与name紧邻。如果两者之间有任何空白存在,参数列表就会被解释为宏内容的一部分
其次: 还要注意一些括号的问题,我们以下面的代码为例

来看下面的例子:

#define SQUARE(x) x*x
int main()
{
	int a = 10;
	int r = SQUARE(a);
	printf("%d\n", r);
	return 0;
}

看上面的代码会打印什么结果呢?我们让代码跑起来看一下:结果是100没有问题。
在这里插入图片描述
但是如果我们这样改代码呢?结果还是10嘛?

#define SQUARE(x) x*x
int main()
{
	int a = 9;
	int r = SQUARE(a+1);
	printf("%d\n", r);
	return 0;
}

我们让代码跑起来看一下:为什么是19呢,我们具体来看一下预处理之后的结果
在这里插入图片描述
我们编译并且在看一下预处理之后的结果:在这里插入图片描述
预处理之后a+1直接被替换过去我们想计算的是10 * 10的值,但实际上计算的是9+1*1+9,所以结果计算19


这就是在写宏的时候出现的一个问题,我们可以这样修改一下

#define SQUARE(x) ((x)*(x))
int main()
{
	int a = 9;
	int r = SQUARE(a+1);
	printf("%d\n", r);
	return 0;
}

在这里插入图片描述
这样就达到了我们想要的结果,这样就比较清晰了,由替换产生的表达式没有按照预想的次序进行求值在宏定义上加上两个括号,这个问题便轻松的解决了

我们在来看下面的例子:

#define DOUBLE(x) (x)+(x)
int main()
{
	int ret = 3 * DOUBLE(20);
	printf("%d\n", ret);
	return 0;
}

我们想要的结果是120,但是真的会打印120嘛?我们来看一下。
结果确是80,这又是为什么呢,我们加括号了啊,呢么我们就在来看一下,预处理之后的结果:
在这里插入图片描述
在这里插入图片描述
预处理之后20直接被替换过去我们想计算的是3 * 40的值,但实际上计算的是3*20+20,所以结果计算80
所以写宏的时候,不光参数带括号,整体也要带括号
提示:所以用于对数值表达式进行求值的宏定义都应该用这种方式加上括号,避免在使用宏时由于参数中的操作符或邻近操作符之间不可预料的相互作用

4.#define 替换规则

1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换.
2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换.
3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程.
注意:
1.宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归
2.当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索.

5.带副作用的宏参数

当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用,那么你在使用这个宏的时候就可能出现危险,导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。

x+1;//不带副作用
x++;//带有副作用

6. 宏和函数对比

在这里插入图片描述

7. 头文件被包含的方式

  • 本地文件包含
    我们在来看下面的例子:
#include "filename"
#include "game"
#include "test.h"

查找策略:先在源文件所在目录下查找,如果该头文件未找到,编译器就像查找库函数头文件一样在标准位置查找头文件。如果找不到就提示编译错误

  • 库文件包含
    我们在来看下面的例子:
#include "stdio.h"
#include "assert.h"
#include "stdilb.h"

查找策略:查找头文件直接去标准路径下去查找,如果找不到就提示编译错误。


总结

以上就是一些关于预处理的详解
🌟Hello world 我们下期见!

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@小辉灰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值