浅谈预编译指令常见用法

很多优秀的代码,都会借用预编译指令来完善代码,今天就来讲讲关于预编译指令的内容。
常见的预处理指令如下:
#指令,无任何效果
#include含一个源代码文件
#define义宏
#undef消已定义的宏
#if果给定条件为真,则编译下面代码
#ifdef果宏已经定义,则编译下面代码
#ifndef果宏没有定义,则编译下面代码
#elif果前面的if定条件不为真,当前条件为真,则编译下面代码
#endif束一个if……#else件编译块
#error止编译并显示错误信息
本来只是想了解一下#ifdef,#ifndef,#endif的,没想到查出来这么多的预处理指令,上面的多数都是常见的,但是平时没有怎么注意预处理这方面的内容,所以这里梳理一下知识吧。
同时有什么不妥的地方,或者遗漏了什么内容,还请留言指出。
什么是预处理指令?
预处理指令是以#号开头的代码行。#号必须是该行除了任何空白字符外的第一个字符。#后是指令关键字,在关键字和#号之间允许存在任意个数的空白字符。整行语句构成了一条预处理指令,该指令将在编译器进行编译之前对源代码做某些转换。
预处理指令是在编译器进行编译之前进行的操作,对其进行初步的转换,产生新的源代码提供给编译器。可见预处理过程先于编译器对源代码进行处理。在很多编程语言中,并没有任何内在的机制来完成如下一些功能:在编译时包含其他源文件、定义宏、根据条件决定编译时是否包含某些代码(防止重复包含某些文件)。
要完成这些工作,就需要使用预处理程序。尽管在目前绝大多数编译器都包含了预处理程序,但通常认为它们是独立于编译器的。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行响应的转换。预处理过程还会删除程序中的注释和多余的空白字符。#include包含一个源代码文件这个预处理指令,我想是见得最多的一个,简单说一下;
第一种方法是用尖括号把头文件括起来。
#include 《stdio.h》
这种格式告诉预处理程序在编译器自带的或外部库的头文件中搜索被包含的头文件。
第二种方法是用双引号把头文件括起来。
#include “test.h”
这种格式告诉预处理程序在当前被编译的应用程序的源代码文件中搜索被包含的头文件,如果找不到,再搜索编译器自带的头文件。采用两种不同包含格式的理由在于,编译器是安装在公共子目录下的,而被编译的应用程序是在它们自己的私有子目录下的。
一个应用程序既包含编译器提供的公共头文件,也包含自定义的私有头文件。采用两种不同的包含格式使得编译器能够在很多头文件中区别出一组公共的头文件。#define定义宏有关#define这个宏定义,在c语言中使用的很多,因为#define存在一些不足,c++强调使用const来定义常量。宏定义了一个代表特定内容的标识符。预处理过程会把源代码中出现的宏标识符替换成宏定义时的值。记住仅仅是进行标识符的替换。下面列举一些#define的使用:
用#define实现求最大值和最小值的宏
#include 《stdio.h》
#define max(x,y) (((x)》(y))?(x):(y))
#define min(x,y) (((x)《(y))?(x):(y))
int main(void)
{
#ifdef max //判断这个宏是否被定义
printf(“3 and 5 the max is:%d
”,max(3,5));
#endif
#ifdef min
printf(“3 and 5 the min is:%d
”,min(3,5));
#endif
return 0;
}
/*
* (1)三元运算符要比if,else效率高
* (2)宏的使用一定要细心,需要把参数小心的用括号括起来,
* 因为宏只是简单的文本替换,不注意,容易引起歧义错误。
*/
宏定义的错误使用
#include 《stdio.h》#define sqr(x) (x*x)int main(void)
{
int b=3;
#ifdef sqr//只需要宏名就可以了,不需要参数,有参数的话会警告
printf(“a = %d
”,sqr(b+2));
#endif
return 0;
}
/*
*首先说明,这个宏的定义是错误的。并没有实现程序中的b+2的平方
* 预处理的时候,替换成如下的结果:b+2*b+2
* 正确的宏定义应该是:#define sqr(x) ((x)*(x))
* 所以,尽量使用小括号,将参数括起来。
*/
宏参数的连接
#include 《stdio.h》#define str(s) #s#define cons(a,b) (int)(a##e##b)int main(void)
{
#ifdef str
printf(str(vck));
#endif#ifdef cons
printf(“
%d
”,cons(2,3));
#endif
return 0;
}
/* (绝大多数是使用不到这些的,使用到的话,查看手册就可以了)
* 第一个宏,用#把参数转化为一个字符串
* 第二个宏,用##把2个宏参数粘合在一起,及aeb,2e3也就是2000
*/
用宏得到一个字的高位或低位的字节
#include 《stdio.h》#define word_lo(xxx) ((byte)((word)(xxx) & 255))#define word_hi(xxx) ((byte)((word)(xxx) 》》 8))int main(void)
{
return 0;
}
/*
* 一个字2个字节,获得低字节(低8位),与255(0000,0000,1111,1111)按位相与
* 获得高字节(高8位),右移8位即可。
*/
用宏定义得到一个数组所含元素的个数
#include 《stdio.h》#define arr_size(a) (sizeof((a))/sizeof((a[0])))int main(void)
{
int array[100];
#ifdef arr_size
printf(“array has %d items.
”,arr_size(array));
#endif
return 0;
}
/*
*总的大小除以每个类型的大小
*/
关于#define宏的使用,应该特别小心,尤其是含有参数计算的时候如下2示例,最保险的做法将参数用括号括起来。#ifdef,#ifndef,#endif.。。的使用以上这些预编译指令,都是条件编译指令,也就是说,将决定那些代码被编译,而哪些不被编译。
示例1:
#include 《stdio.h》#include 《stdlib.h》#define debugint main(void)
{
int i = 0;
char c;
while(1)
{
i++;
c = getchar();
if(‘
’ != c)
{
getchar();
}
if(‘q’ == c || ‘q’ == c)
{
#ifdef debug//判断debug是否被定义了
printf(“we get:%c,about to exit.
”,c);
#endif
break;
}
else
{
printf(“i = %d”,i);
#ifdef debug
printf(“,we get:%c”,c);
#endif
printf(“
”);
}
}
printf(“hello world!
”);
return 0;
}
/*#endif用于终止#if预处理指令。*/
ifdef 和 #ifndef
#include 《stdio.h》#define debug
main()
{
#ifdef debug
printf(“yes ”);
#endif#ifndef debug
printf(“no ”);
#endif
}
//#ifdefined等价于#ifdef;//#if!defined等价于#ifndef
#else指令
#elif指令
其他一些指令
#error指令将使编译器显示一条错误信息,然后停止编译。
#line指令可以改变编译器用来指出警告和错误信息的文件号和行号。
#pragma令没有正式的定义。编译器可以自定义其用途。典型的用法是禁止或允许某些烦人的警告信息。


未来,5G与智能交通会开启哪些新的市场成长空间?
动则百万,应届生也可年薪50万,芯片工程师为什么变的这么贵?
我们该怎样看待区块链的现在和未来
[原创] Infineon Xilinx Ultra Scale KintexFPGA开发方案
GPU运算速度比CPU快的原因 GPU和CPU的区别
浅谈预编译指令常见用法
战报!电商服务商战事焦灼,杭州碧橙一个“敢”字出彩双十一
ios10.3.2迎来正式版,值得升级吗?ios11也要来?苹果今日放出ios10.3.3测试版
D类音频功率放大器的热耗散分析
高低温试验箱温湿度计的测量原理
锂电池智能制造的四个阶段
区块链遇上共享经济后将会带来什么?
有嗅觉的AI芯片,可识别爆炸物气味
调频放大器电路原理图讲解
FARO Orbis移动激光扫描助力林业数字化转型,实现智能森林
电机是如何驱动的呢?
MacooxMC-X7评测 功能方便UI设计合理
便携式气体质量流量计的思路设计
晶闸管的结构与工作原理
什么是传输线?什么是信号完整性分析?为什么传输线要测试差分信号?