编程之路,从0开始:预处理详解(完结篇)

        Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

7841ec7230ea42e1ab5d1d00b0132cf5.gif

我的博客:<但凡.

我的专栏:编程之路

        这一篇预处理详解是我们C语言基础内容学习最后一篇,也是我们的专栏:编程之路最后一篇从今日起,我将不定期更新新的内容,开始新的章节,感谢各位一路以来的支持和陪伴!

目录

1、什么是预处理

2、预处理指令

2.1预定义符号

2.2 #define定义常量

2.3#define定义宏

2.4带副作用的宏参数

2.5宏与函数的对比

3、#和##

3.1#运算符

3.2##运算符

4、#undef

5、条件编译


 

1、什么是预处理

        我们的系统拿到各种文件后,要进行一系列的操作过程,才能将文件转化成我们能够看得懂的信息。

        翻译环境能够把源代码转换成可执行的机器指令,这个过程主要有编译和链接两个过程组成。

        现在我们拆解一下编译链接的过程:

299f4148ecec4334b8a7c6e6cca68e90.png

        预处理就是编译和链接过程中的一个阶段。在预处理阶段,源文件和头文件都会被处理成.i为后缀的文件,预处理阶段主要是处理那些源文件中以#开头的预处理指令

2、预处理指令

2.1预定义符号

从语言设置了一些预定义符号,可以直接使用:

28734145692743f5b096866ab525e6f5.png举个例子:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
	printf("%d %s", __LINE__,__FILE__);//打印出当前行的行号和文件的所在位置
	return 0;
}

输出结果:

274abd6a30794475a082925312a49e55.png需要注意的是,__STDC__无法在vs中使用。

2.2 #define定义常量

现在我们用define定义一个常量,并打印出来:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define M 6
int main()
{
	printf("%d",M);
	return 0;
}

输出结果:02926ff5f9024c6283f8b377a36a75e7.png需要特别注意的是,define定义常量时不要再define行末尾加上;

2.3#define定义宏

什么是宏?我们先来段代码感受一下:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) a+b
int main()
{
	int a = 5;
	int b = 3;
	printf("%d",ADD(a,b));
	return 0;
}

输出结果:

6aa71eb4fc23480a936473facce168eb.png        我们定义了一个宏ADD(a,b),这个宏ADD(a,b)可以计算传入宏的两个参数a,b的和。

        但是宏有一些容易出错的地方,例如,我们输出下面这串代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) a*b
int main()
{
	int a = 5;
	int b = 3;
	printf("%d",ADD(a+1,b+1));
	return 0;
}

输出结果:

aa7c7f3c3e9e4ca9b8b4549df8074b6c.png

        这不对啊!a+1是6,b+1是4,输出结果应该是24啊!这里是9,这是怎么回事呢?

        其实,我们在把两个参数传过去后,他计算的其实是5+1*3+1,根据运算法则,,先算乘法再算加法,所以输出结果为9。

        那有什么办法解决呢?其实也很简单,我们只要加两个括号就解决了:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) (a)*(b)
int main()
{
	int a = 5;
	int b = 3;
	printf("%d",ADD(a+1,b+1));
	return 0;
}

输出结果:

c8d25cff3a1c41a4b58e5e8e57cf48ae.png

2.4带副作用的宏参数

我们来看这串代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) ((a)>(b)?(a):(b))
int main()
{
	int a = 3;
	int b = 6;
	printf("%d %d %d",a,b,ADD(a++,b++));
	return 0;
}

        注意:我们把a的值换成了3,b的值换成了6.

输出结果:

7ecdecae23b74471ba5981127e4f4697.png我们来分析一下,首先我们先把宏的值全都替换掉:

printf("%d %d %d",a,b, ((a++) > (b++) ? (a++) : (b++)));

        a++的特性是先用后加,所以我们实际比较的是3和6两个数。再比较完之后,a变成4,b变成7。然后返回值为b++。同样是先用后加,所以先返回b的值7,然后再自增1,现在b等于8。分析结果和我们的实际结果相符。

2.5宏与函数的对比

        我们看到这应该已经能发现了,宏和函数功能及其相似,那他们两个到底谁更好呢?

宏相对于函数的优势:

1、在进行简单运算时,宏所需的时间代码规模上更胜一筹。

2、宏对于参数类型没有限制,而函数限制参数类型。

函数相对于宏的优势:

1、函数相对于宏更方便调试。

2、函数可以递归,宏不可以递归。

3、函数的结果更容易预料,宏存在带副作用参数和操作符优先级等问题。

其实我们在写程序时函数用的更多一些,函数的优势更大一些。

3、#和##

3.1#运算符

        我们都知道,在使用宏时,传入的参数后自动将宏的语句里面的参数名替换为相应的值。那么如果我们不想让他替换呢?这时候就需要我们的“字符串化”运算符#。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define TEST(n) printf("变量"#n"的值为:%d",n)
int main()
{
	int a = 3;
	int b = 6;
	TEST(a);
	return 0;
}

输出结果:

ebe866e67ba14e7b9a1c12814c93dbdc.png

3.2##运算符

##可以把两边的符号合成一个符号,所以##被称为记号粘合。

举例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define GENERIC_MAX(type)\
type type##_MAX(type x,type y)\
{\
return(x > y ? x : y);\
}\
// \为续行符
GENERIC_MAX(int)//定义函数
int main()
{
	int a = 3;
	int b = 6;
	printf("%d", int_MAX(a, b));
	return 0;
}

输出结果:

348490bcbc8349ad8bc73dc119a87ee6.png

        这里我们就是把type和MAX合并成了一个函数名。实际上我们很少用到##操作符,但是当我们看到##操作符时要知道这是什么意思。

4、#undef

我们可以用#undef来移除一个宏定义:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) (a)*(b)
int main()
{
	int a = 5;
	int b = 3;
	printf("%d", ADD(a + 1, b + 1));
	#undef ADD(a,b)//移除宏
	printf("%d", ADD(a + 1, b + 1));//程序报错,引用了未定义符号ADD
	return 0;
}

5、条件编译

        我们可以使用条件编译,让一段代码满足一定条件时进行编译:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) (a)+(b)
#define __DEBUG__
int main()
{
	int a = 5;
	int b = 3;
#ifdef __DEBUG__
	printf("%d", ADD(a, b));
#endif
	return 0;
}

        我们还可以这样使用:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define ADD(a,b) (a)+(b)
int main()
{
	int a = 5;
	int b = 3;
#if defined ADD(a,b) //如果定义了ADD宏
	printf("%d", ADD(a, b));
#endif
	return 0;
}

        #if和if的区别是,如果不满足条件的话,#if后的语句根本就不进行编译,这样会节省系统执行的时间。但其实这个操作用的也不是很多。

        好了今天的内容就分享到这,我们下期再见!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值