【C语言】预处理指令— —宏定义、文件包含、条件编译

author:&Carlton

tags:C语言,暑期实践

topic:预处理指令— —宏定义、文件包含、条件编译

book:谭浩强 《C语言程序设计》第五版

date:2023年7月10日


目录

预处理指令

宏定义

不带参数的宏定义

带参数的宏定义

”文件包含“处理

条件编译


预处理指令

        C语言允许源程序加入”预处理指令“(preprocessing directive),这些预处理指令是由C标准建议的,但它并不是C语言本身的组成部分,C编译程序不能识别它们

        需要对这些指令进行预处理(preprocess,也称”编译预处理“或”预编译),由C预处理器(preprocessor)的程序负责处理(现在的C编译系统把C预处理器作为其系统的一个组成部分,编译时一气呵成)

        主要有3个功能:

        ①宏定义

        ②文件包含

        ③条件编译

        这些指令以”#“开头,且指令后面没有分号。

宏定义

        使用宏定义记得在宏定义指令处添加必要的括号

        常见的问题有四则运算优先顺序导致的矛盾

        #define P 2+5

        P*3         //变为2+5*3,不符

        #define P (2+5)

        P*3        //变为(2+5)*3,正确

不带参数的宏定义

        #define 标识符 字符串

        核心是简单的字符替换,宏名习惯全部用大写字母表示,并不会检查语法错误可以引用已定义的宏名,层层替换。

        其作用域为该指令行到本源文件结束,可以用#undef指令终止宏定义的作用域:

        #define G 9.8

        ……                //将G替换为9.8

        #undef G

        ……                //停止替换效果

        

        使用宏定义的优点

        见名知义,一改全改,提高程序通用性。

带参数的宏定义

        #define 宏名(参数表) 字符串

        要进行参数替换及参数以外的字符替换

        如果预处理指令处字符串中包含参数表中的参数,则将程序语句中相应的实参(不限制数据类型,可以是常量、变量或表达式)代替形参。如果不是则按照字符替换的方式保留

        

#define S(a,b) a*b
……
area=S(3,2)

实际效果即为:
area=3*2

         

       应注意:

       在定义宏时,应当在字符串中的形参外面加上括号,以防带来运算优先顺序带来的问题。

       宏名与带参数的括号之间不要有空格,否则会识别为第一种不带参数的宏定义,做简单的字符替换。

        带参数的宏与函数的区别

        ①对实参处理方式不同。函数调用时会先求出实参表达式的值

        ②处理时间、对形参处理方式不同。函数调用是在程序运行时处理的,为形参分配临时的内存单元。

        ③对参数的数据类型要求不同。函数的实参和形参都要定义类型,且类型要保持一致。

        ④可得到的结果数量不同。函数本身只可得到一个返回值,宏可以设法得到多个结果:
 

#define PI 3.14
#define CIRCLE(R,L,S,V) L=2*PI*R;S=PI*R*R;V=4.0/3.0*PI*R*R*R

CIRCLE(r,l,s,v);    //运行时得到r,l,s,v四个结果

        ⑤对源程序长度影响不同。使用宏次数多时,宏展开源程序变长。

        ⑥处理时间不同。宏替换不占运行时间,只占预处理时间,而函数则占用运行时间(分配单元、保留现场、值传递、返回)。

        宏定义的好处

        善于使用宏定义可以实现程序的简化,可以写出各种输入输出的格式或者定义一些标准数据。

        单独编成一个文件,相当于一个”格式库“,其他文件只需将这个文件包含进就可以方便使用这些标准。(即结合”文件包含“的预处理方式)        

”文件包含“处理

        #include ”文件名“

        #include <文件名>

        用尖括号形式时,系统到存放C库函数头文件的目录中寻找要包含的文件,称为标准方式

        用双撇号时,系统先在用户当前目录中寻找,若找不到再按照标准方式寻找。若文件没有放在用户当前目录(源程序存放的目录中),应在双撇号内给出文件路径。

(如 #include"C:\wang\file2.h")

        

        文件包含指令的作用

        将file2.c文件的全部内容复制、取代在file1.c中对file2.c文件包含指令#include <file2.c>,完成file1.c包含file2.c文件的操作,进行编译时将经过预处理的file1.c文件作为一个源文件单位进行编译。

           

       这些常用在文件头部的被包含的文件称为”标题文件“或”头文件“,常以”.h“为后缀(header)

       一个#include指令只能包含一个文件,文件包含可以嵌套

        使用”文件包含“指令的好处

        ①可以节省程序设计人员的重复劳动,可以将定义的各种格式宏作为一个头文件,供大家使用。 

        ②修改某些常用的参数时,不必修改每个程序,仅仅修改放置参数的头文件即可。但被包含文件修改后,凡包含此文件的所有文件都要全部重新编译

 

条件编译

        有时希望程序中的一部分内容只在满足一定条件下才进行编译,即对这一部分内容指定编译的条件,这就是”条件编译“

         有以下3种形式:

//(1)
#ifdef 标识符
    程序段1
#else             //#else部分可以没有
    程序段2
#endif

//(2)
#ifndef 标识符    //多了个"n",not
    程序段1
#else            //#else部分可以没有
    程序段2
#endif

//(3)
#if 表达式
    程序段1
#else
    程序段2
#endif

        

        条件编译的作用

        (1)若所指定的标识符已经被#define指令定义过,则在程序编译阶段对程序段1进行编译(保留程序段1,忽略/删除程序段2),否则对程序段2进行编译。

        (2)情况与(1)相反。

        (3)指定的表达式值为真时编译程序段1,否则编译程序段2。(注意条件编译是预处理指令,在预处理过程起效,程序还未正式编译运行,故指定的表达式通常结合宏定义使用

#define LETTER 1
……

#if LETTER
……
#endif

        条件编译的好处

       

        ①提高程序通用性。

        如果一个C源程序在不同计算机系统上运行,而不同计算机又有一定的差异,如对int类型整数存放字节数的差异(2或4),用条件编译处理:      

  

#ifdef COMPUTER_A
    #define INTEGER_SIZE 16
#else
    #define INTEGER_SIZE 32
#endif

        只要在前头出现过#define COMPUTER_A 等定义COMPUTER_A的语句就会在预编译时包括#define INTEGER_SIZE 16

        ②有开关的作用,一发而动全身。

        调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。

        为了避免一 一删改printf语句,利用条件编译只需删除前面一条#define DEBUG或者加上一条#define RUN即可。

        

        ③可以减少被编译的语句,从而减少目标程序的长度,减少运行时间。

        

#define DEBUG        //退出调试时,删除此宏定义即可

#ifdef DEBUG
    printf("x=%d,y=%d,z=%d\n",x,y,z);
#endif


#define RUN        //退出调试并要运行程序时,加上此宏定义即可

#ifndef RUN
     printf("x=%d,y=%d,z=%d\n",x,y,z);
#endif

        

        预处理功能是C语言特有的,有利于程序的可移植性,增加程序的灵活性,在学习深入并编写较大的程序时,善于利用预处理指令,对提高程序的质量很有帮助。

欢迎指正与分享,谢谢!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值