目录
🍊前言🍊:
🍈一、宏与函数🍈:
1.宏与函数对比:
2.宏与函数的命名约定:
🍓二、预处理操作符🍓:
1.预处理操作符 " # ":
2.预处理操作符 " ## ":
🥝三、条件编译🥝:
1.简述条件编译指令:
2.常见条件编译指令:
🍒总结🍒:
🛰️博客主页:✈️銮同学的干货分享基地
🛰️欢迎关注:👍点赞🙌收藏✍️留言
🛰️系列专栏:💐【进阶】C语言学习
🧧 C语言学习
🛰️代码仓库:🎉VS2022_C语言仓库
家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!
关注我,关注我,关注我,你们将会看到更多的优质内容!!
🏡🏡 本文重点 🏡🏡:
🚅 宏与函数 🚃 预处理操作符 🚃 条件编译🚏🚏
🍊前言🍊:
在上节课中,我们对程序编译的环境、编译链接过程以及很重要的预处理指令 #define 进行了研究,对于程序的预处理过程有了一定的了解,而这节课我们将要继续学习预处理的一些相关知识,帮助我们更加全面的掌握程序的预处理过程。
🍈一、宏与函数🍈:
1.宏与函数对比:
我们之前在使用宏 #define 时,常用于定义一些全局的宏常量等。而现在我们要知道的是,宏还常常被应用于执行简单的运算,例如比较两数的大小我们可以这样用:
#define MAX(a,b) ((a)>(b)?(a):(b))
#include<stdio.h>
int main()
{
int a = 5;
int b = 10;
printf("MAX = %d\n", MAX(a, b));
return 0;
}
那么可能有的小伙伴就会问了,那为什么我们不使用函数来完成这个过程呢?
★ 在这里使用宏而不用函数的主要原因有两个:
①. 相比于函数宏在程序规模与速度方面更胜一筹:由于函数在调用时牵扯到函数栈帧的创建与销毁等操作,所以用于调用函数以及从函数返回的耗时可能比实际执行这个小型计算工作的耗时更多。
②. 宏是类型无关的:更重要的是我们都知道,在声明、定义和使用函数时,函数的参数必须是特定的类型,所以函数只能在类型合适的特定表达式上使用。反观宏,无论是整形、长整型还是浮点型,都可以进行比较。
但是我们说宏也有缺点:
★ 当然宏与函数相比也有劣势:
①. 宏只能处理简单运算:我们知道在预处理阶段宏将会进行符号替换,这就意味着每次在使用宏的时候,一份宏定义的代码将插入到程序中。这样一来,如果宏比较长,就将会大幅度增加程序代码的长度。
②. 宏是没有办法进行调试的。
③. 由于宏具有类型无关的特点,因此也不够严谨。
④. 宏可能会带来运算符优先级的问题,导致程序容易出错。
可是,宏也可以做到函数做不到的事。比如在宏中可以出现类型,而函数不可以:
#define MALLOC(num,type) (type*)malloc(num*sizeof(type))
#include<stdio.h>
#include<string.h>
int main()
{
MALLOC(10, int);
//预处理器替换后:(int*)malloc(10 * sizeof(int));
return 0;
}
2.宏与函数的命名约定:
一般来讲,我们使用的宏定义函数,与普通函数的使用语法很相似,这就导致语言本身无法帮我们区分二者。所以我们通常对二者的命名进行约定:
①. 宏的名称全部大写。
②. 函数名不要全部大写。
如此就可以帮助我们区分二者,提升我们程序代码的可读性了。
🍓二、预处理操作符🍓:
首先我们来看这样的一段代码:
int main()
{
char* p = "Hellow ""world\n";
//*p指向两个常量字符串
printf("Hellow world\n");
printf("%s", p);
return 0;
}
当我们将其编译运行发现,打印结果均为字符串“ Hellow world”:
于是由此我们就可以得知,字符串具有自动连接的特点。那么我们是不是就可以写出下面这样的代码呢?
#define PRINT(FORMAT,VALUE) printf("The value of VALUE is "FORMAT"\n",VALUE);
//可以使用宏来定义函数
//字符串"The value of VALUE is "与"\n"将会自动连接
//构造函数等价于printf("the value of VALUE is %d\n",VALUE);
#include<stdio.h>
int main()
{
int a = 10;
PRINT("%d", a + 3);
return 0;
}
在这段代码中,我们使用了宏来构造一个新的函数,在这个我们自己构造出来的宏函数中,其中的两个短字符串将会自动连接成为一整个长字符串,并根据我们输入的参数进行打印。于是在这个过程中,就衍生出了两个实用的预处理操作符:# 与 ##。
1.预处理操作符 " # ":
预处理操作符" # "的作用:将宏参数转变成其对应的字符串。
这是什么意思呢?例如下面这段代码中所使用的 #N :
#define _CRT_SECURE_NO_WARNINGS 1 #define PRINT(N) printf("The value of "#N" is %d\n",N); #N将被视为字符串"N",且字符串 N 在打印时打印的是字符串 N 的内容 #include<stdio.h> int main() { int a = 10; PRINT(a + 3); return 0; }
在这段代码中,#N 将会被处理为字符串 N ,即" a + 3 ",于是整个宏定义函数就相当于:
printf("The value of ""a + 3"" is %d\n",VALUE);
即相当于字符串"The value of "、字符串"a + 3"与字符串" is %d\n"之间进行了自动连接,成为了一整个长字符串。
则最终的输出结果为:
2.预处理操作符 " ## ":
预处理操作符" ## "的作用:将位于其两边的符号合成为一个符号。
并且,它允许宏定义从分离的文本片段中创建标识符。
#define CAT(CLASS,NAME) CLASS##NAME //宏定义函数的作用为将符号 CLASS 与符号 NAME 合并为符号 CCLASSNAME #include<stdio.h> int main() { int class3xiaohong = 888; int back = CAT(class3, xiaohong); //使用宏定义函数 CAT ,将符号 class3 与符号 xiaohong 合并为 class3xiaohong //而符号 class3xiaohong 表示的是值为 888 的 int 类型变量,故用 int 类型变量 back 接收并打印或执行其他操作 printf("%d\n", back); return 0; }
在这段代码中,通过宏定义的预处理操作符" ## "将符号" class3 "与符号" xiaohong "进行了合并,合并成了符号" class3xiaohong ",接着我们看到,在这之前我们就已经定义并初始化了 int 类型变量 class3xiaohong,于是我们就可以使用 int 类型的变量对宏定义函数所合成的符号" class3xiaohong "所表示的值进行接收了。
最后进行打印,验证符号合成结果:
🥝三、条件编译🥝:
1.简述条件编译指令:
通过使用条件编译指令,我们在编译一段代码时如果要将一条(或一组)语句编译或舍弃是很方便的。例如一些调试性的代码,删除可惜,保留碍事,于是我们就可以通过使用条件编译指令来实现选择性的进行编译。
例如:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
arr[i] = i + 1;
printf("%02d ", arr[i]);
#if 0
printf("\n");
#endif
}
return 0;
}
在这段代码中,包含在条件编译指令 #if 与 #endif 之间的换行打印是否执行,就取决于条件编译指令的参数,此时参数为 0 ,即为假,则不进行换行打印:
那么我们再将参数改为1,即真:
#include<stdio.h>
int main()
{
int arr[10] = { 0 };
int i = 0;
for (i = 0; i < 10; i++)
{
arr[i] = i + 1;
printf("%02d ", arr[i]);
#if 1
printf("\n");
#endif
}
return 0;
}
这时条件编译指令参数为真,于是执行其中的代码进行换行打印:
2.常见条件编译指令:
①. 单分支条件编译指令:
#if 常量表达式
//执行操作
#endif
//或:常量表达式由预编译器求值:
#define __DEBUG__ 1
#if __DEBUG__
//执行操作
#endif
②. 多分枝条件编译指令:
#if 常量表达式
//执行操作
#elif 常量表达式
//执行操作
#elif 常量表达式
//执行操作
...
#endif
③. 判断是否被定义:
#if defined(symbol)
//如果 symbol 被定义过,则执行操作
#endif
//或:
#if !defined(symbol)
//如果 symbol 没有被定义过,则执行操作
#endif
④.嵌套指令:
#if defined(MAX)
#if 1
printf("%d\n", a > b ? a : b);
#endif
#elif define(MIN)
#if 0
printf("%d\n", a < b ? a : b);
#endif
#endif
通过灵活的嵌套使用条件编译指令,就能实现某条或某组程序代码的选择性编译。
🍒总结🍒:
到这里,我们关于预处理部分的讲解就全部结束了。但是各位小伙伴们可不能就此放松,基础知识的讲解并不意味着我们学习的终结。C 语言是高深而奇妙的,语言学习的道路永无尽头,希望各位小伙伴们能够静下心来多多深入研究,不断发掘C语言的深层逻辑,提升个人的编程能力。同时我们关于 C 语言的进阶之路也就到此为止了。至此我们的 C 语言语法已经全部讲解完毕了,再往后就将进入我们的数据结构与算法部分的学习之中了,各位小伙伴们,期待那时与你们的再次相会,我们随后再会!
🔥🔥只有承担起旅途风雨,最终才能守得住彩虹满天🔥🔥
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们真的对我很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!