预处理
宏定义
在前面的学习中经常遇到用#define
命令定义符号常量的情况,其实使用#define
命令就是要定义一个可替换的宏。
专注于为中小企业提供成都网站设计、成都网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业满洲免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了1000+企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。
宏定义是预处理命令的一种,它提供了一种可以替换源代码中字符串的机制。
根据宏定义中是否有参数,可以将宏定义分为不带参数的宏定义和带参数的宏定义两种,下面分别进行介绍。
使用#define
进行宏定义的好处是需要改变一个常量时只需改变#define
命令行,整个程序的常量都会改变,大大提高了程序的灵活性。
宏名要简单且意义明确,一般习惯用大写字母表示以便与变量名相区别。注意 : 宏定义不是C语句,不需要在行末加分号。
不带参
#define 宏命 字符串
#
:表示这是一条预处理命令- 宏命是一个标识符,必须符合 C 语言标识符的规范
- 字符串可以是常数、表达式、格式字符串等
#define PI 3.
该语句的作用是在该程序中用PI替代3.,在编译预处理时,每当在源程序中遇到PI就自动用3.代替。
带参宏定义
带参宏定义不是简单的字符串替换,还要进行参数替换,其一般形式如下:
#define 宏名(参数列表) 宏体
- 对两个参数实现混合运算
test1.c
#define MIX(x, y) ((x)*(y)+(y))
#include "stdio.h"
int main() {
int a = 5, b = 9;
printf("the mix num is:%d", MIX(a, b));
}
对上述代码执行 预处理命令
gcc -E test1.c
输出为(省略其他部分):
int main() {
int a = 5, b = 9;
printf("the mix num is:%d", ((a)*(b)+(b)));
}
可见在预处理阶段,对源码中的宏名实现了参数的代入和宏体的替换。
用宏替换代替实在的函数的一个好处是宏替换增加了代码的速度,因为不存在函数调用。但增加速度也有代价:由于重复编码而增加了程序长度。
- 1、带参数宏定义时,参数要加括号,因为参数可能是一个表达式。如果不加括号,结果可能正确,也可能错误。或者我们在红定义的时候不带括号,但是在使用宏的地方带上括号。
#include "stdio.h"
#define MIX(x, y) ((x)*(y)+y)
#define MIX_1(x, y) (x*y+y)
int main() {
int a = 5, b = 9;
// 这行输出是没问题的
printf("the mix num is : %d", MIX(a, b));
// 这行是有问题的
printf("the mix num is : %d", MIX_1(2 + 4, 1 + 7));
// 这行是没问题的
printf("the mix num is : %d", MIX_1((2 + 4), (1 + 7)));
}
同样执行预处理命令后的输出为:
int main() {
int a = 5, b = 9;
printf("the mix num is : %d", ((a)*(b)+b));
printf("the mix num is : %d", (2 + 4*1 + 7 +1 + 7));
printf("the mix num is : %d", ((2 + 4)*(1 + 7)+(1 + 7)));
}
对于宏 #define MIX_1(x, y) (x*y+y)
来说,在使用的时候,给参数加上括号,也是可以是计算满足预期,但这不是好的方式。所以建议在定义宏体的时候,就给参数加上括号:#define MIX(x, y) ((x)*(y)+y)
。
- 2、宏扩展必须使用括号来保护表达式中低优先级的操作符,以确保调用时达到想要的效果。
- 3、对带参数的宏的展开,只是将语句中的宏名后面括号内的实参字符串代替
#define
命令行中的形参。 - 4、在宏定义时,宏名与带参数的括号之间不可以加空格,否则会将空格以后的字符都作为替代字符串的一部分。
- 5、在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。
include 指令
在一个源文件中使用#include指令可以将另一个源文件的全部内容包含进来,也就是将另外的文件包含到本文件之中。#include使编译程序将另一源文件嵌入带有#include的源文件,被读入的源文件必须用双引号或尖括号括起来。例如:
#include "stdio.h"
#include
这两行代码均使用C编译程序读入并编译,用于处理磁盘文件库的子程序。
上面给出了双引号和尖括号的形式,这两者之间的区别是
-
用尖括号时,系统到存放C库函数头文件所在的目录中寻找要包含的文件,这为标准方式;
-
用双引号时,系统先在用户当前目录中寻找要包含的文件,若找不到,再到存放C库函数头文件所在的目录中寻找要包含的文件。
通常情况下,如果为调用库函数用#include命令来包含相关的头文件,则用尖括号可以节省查找时间,如果要包含用户自己编写的文件,一般使用 双引号,用户自己编写的文件通常在当前目录中。如果不在,则双引号要给出文件的路径。
经常用在文件头部的被包含的文件称为“标题文件”或“头部文件”,一般以.h为后缀,如本实例中的f1.h。
一般情况下将如下内容放到.h文件中:
- 宏定义。
- 结构、联合和枚举声明。
- typedef声明。
- 外部函数声明。
- 全局变量声明。
关于“文件包含”有以下几点需要注意。
- 一个**#include**命令只能指定一个被包含的文件。
- 文件包含是可以嵌套的,即在一个被包含文件中还可以包含另一个被包含文件。
- 若file1.c中包含文件file2.h,那么在预编译后就成为一个文件而不是两个文件,这时如果file2.h中有全局静态变量,则该全局变量在file1.c文件中也有效,这时不需要再用extern声明。
条件编译
预处理器提供了条件编译功能,一般情况下,源程序中所有的行都参加编译,但是有时希望只对其中一部分内容在满足一定条件时才进行编译,这时就需要使用到一些条件编译命令。使用条件编译可方便地处理程序的调试版本和正式版本,同时还会增强程序的可移植性。
if
#if
的基本含义是:如果#if
命令后的参数表达式为真,则编译#if
到#endif
之间的程序段,否则跳过这段程序。#endif
命令用来表示#if
段的结束。
#if
命令的一般形式如下:
#if 常数表达式
语句段
#else
#if 常数表达式
语句段
#endif
#endif
如果常数表达式为真,则该段程序被编译,否则跳过不编译。
文章标题:预处理
网站路径:http://hbruida.cn/article/dsoijpo.html