Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!
我的博客:<但凡.
我的专栏:编程之路
这一篇预处理详解是我们C语言基础内容学习的最后一篇,也是我们的专栏:编程之路的最后一篇!从今日起,我将不定期更新新的内容,开始新的章节,感谢各位一路以来的支持和陪伴!
目录
1、什么是预处理
我们的系统拿到各种文件后,要进行一系列的操作过程,才能将文件转化成我们能够看得懂的信息。
翻译环境能够把源代码转换成可执行的机器指令,这个过程主要有编译和链接两个过程组成。
现在我们拆解一下编译链接的过程:
预处理就是编译和链接过程中的一个阶段。在预处理阶段,源文件和头文件都会被处理成.i为后缀的文件,预处理阶段主要是处理那些源文件中以#开头的预处理指令。
2、预处理指令
2.1预定义符号
从语言设置了一些预定义符号,可以直接使用:
举个例子:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
printf("%d %s", __LINE__,__FILE__);//打印出当前行的行号和文件的所在位置
return 0;
}
输出结果:
需要注意的是,__STDC__无法在vs中使用。
2.2 #define定义常量
现在我们用define定义一个常量,并打印出来:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#define M 6
int main()
{
printf("%d",M);
return 0;
}
输出结果:需要特别注意的是,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;
}
输出结果:
我们定义了一个宏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;
}
输出结果:
这不对啊!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;
}
输出结果:
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.
输出结果:
我们来分析一下,首先我们先把宏的值全都替换掉:
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;
}
输出结果:
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;
}
输出结果:
这里我们就是把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后的语句根本就不进行编译,这样会节省系统执行的时间。但其实这个操作用的也不是很多。
好了今天的内容就分享到这,我们下期再见!