预处理

宏定义

在前面的学习中经常遇到用#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