C语言之路 第七章 C语言预处理器

第七章 C语言预处理器

每一发奋努力的背后,必有加倍的赏赐。 

C语言预处理器就像它的名字一样,是源程序编译之前的一段预处理程序。预处理器命令可以被认为是C语言内部的语言。如果不懂预处理器及它的功能,也可以写出C语言程序。实际上,预处理器非常方便,几乎所有的C语言程序员都愿意使用它。本章主要研究预处理命令,以及在程序中使用预处理命令的利与弊。

7.1需要掌握的预处理指令组要有一下几种

   ●宏指令扩展

   ●文件包含

   ●条件编译

   ●其他指令

7.2 宏指令扩展

#define PI 3.1415

其中,PI通常称为宏模板,而3.1415就是对应的宏扩展。

C语言程序中,习惯上用大写字母来表示宏模板。

切记,宏定义不要用分号结束。

7.3 为什么要使用宏指令扩展

●使程序更加易于理解。例如,词组"\x1B[2J"是清楚屏幕的意思,可以这样来定义。#define CLEARSCREEN "\x1B[2J"

●修改方便

假如有一个像3.1415这样的常量在程序中多次出现,某一天,它的值要改为3.141592。通常情况下,必须将这个程序阅读一遍,然后在每一个出现该常数的地方,手工修改成3.141592。但是,如果在#define指令中定义了PI,则只需要在#define指令中修改一次即可。

总之,无论是对小程序还是大程序而言,宏定义都可以带来便利,宏定义几乎是程序设计中必不可少的一部分。

7.4 也可以通过变量来达到同样的目的,而不是采用宏模板。但为什么不使用变量呢?

●变量效率不高。因为使用常量时,编译器产生的代码比使用变量时产生的代码速度更快,代码更紧凑;

●使用一个实际上是常量的变量,会使思维变得松散,程序更难理解,如果变量的值总是不变,很难想象它还是变量;

●变量的值有可能会在程序中不经意地被改变,不再是你认为的那个常数了,这是很危险的。

7.5 #define指令常用来定义如下所示的运算符

    #define AND &&

    #define OR ||

    #define也可以用来替换如下所示的条件

    #define AND &&

    #define ARANGE (a>25ANDa<50)

    #define指令还可以用来替换如下所示的完整C语言语句。

    #define FOUND printf("The Yankee Doodle Virus");

7.6 带参数的宏

与函数一样,宏也可以有参数。

/*带参数宏的例子*/

#include <stdio.h>

#define AREA(x) (3.14*x*x)

int main(void)

{

      float r1=6.25,r2=2.5,a;

      a=AREA(r1);

      printf("面积为%f",a);

      a=AREA(r2);

      printf("面积为%f",a);

      system("pause");

}

/*另一个带参数宏的例子*/ 

#include <stdio.h>

#define ISDIGIT(y) (y>=48&&y<=57)

int main(void)

{

      char ch;

      printf("Enter any digit");

      scanf("%c",&ch);

      if(ISDIGIT(ch))

      printf("\nYou entered a digit");

      else

      printf("\nIllegal input");

      system("pause");

}

7.7 宏还可以分成多行,只需要在每行末尾加一个\。

7.8 宏与函数的比较★★★★★

    宏调用中,预处理程序只是机械地、按符号逐字地用宏扩展去替换宏模板。与此相反,函数调用时,是通过参数将控制传递给函数,可以再函数中执行某些计算,并从函数返回一个有用的值。

这给我们带来一个问题:什么时候该用带参数的宏,什么时候该用函数?

    宏使程序的运行速度快一些,但是宏增加了程序的大小,如果宏在程序中要反复使用上百次,宏扩展就需要在源代码中上百个不同的地方出现,这样会增加程序的大小。

而函数使程序更简洁和紧凑。即使在程序中上百个不同的地方调用函数,程序所占用的空间是一样的。但是将参数传递给函数,再从函数获得返回值,时间开销较大,会降低程序的运行速度。

因此,主要是要协调好内存开销和时间开销两方面的问题。

对于简单的宏,它用一些好记的缩写避免了过多的函数调用的负担。但是,如果宏过大或使用过多,或许还是应该用函数而不是用宏。

7.9文件包含(暂时先做了解)

什么时候使用文件包含?为什么要使用文件包含?

●如果程序很大,代码最好分为几个不同的文件,每个文件含有一组相关的函数。一个 良好的编程习惯是将一个大程序按功能的不同,处理成相互独立的部分。这些文件用 #include将它们放在主程序的开始处

●有一些函数和宏定义几乎在所写的程序中都要被用到。可以将这些常用的函数和宏定义存放在一个文件中。将这个文件包含进我们所写的每一个程序中,文件中的内容就会插入到程序中,就像在程序中事先输入了这个文件中的语句一样。

举例来说,在程序中,像下面这样的代码行:#include "my_file.h"也是预处理指令。在进行编译的时候,my_file.h文件的一份拷贝将会复制到文件中这条指令所在的位置。#include指令可以出现在文件中的任何位置,但一般都是在文件的起始位置。文件名两边的双引号是必要的。有一种称为头文件(header file)的包含文件可以包含#define指令以及其他#include指令。

再详细的举例来说,我们来创建一个头文件,里面放入以下这几行代码:

#include <stdio.h>

#define PI 3.1416

把这个头文件保存为haha.h;

下面来编写一个小程序计算圆的面积

/*计算圆的面积*/

#include "haha.h"

int main(void)

{

float r,area;

printf("输入半径r:");

scanf("%f",&r);

area=PI*r*r;

printf("圆的面积为%f",area);

system("pause");

  }

7.10 条件编译

本节书上的内容讲的有点混乱。故从网上截取了一篇文章以飨自己。

C语言中条件编译命令(#if #else #endif)

预处理程序提供了条件编译的功能。 可以按不同的条件去编译不同的程序部分,因而产生不同的目标代码文件。这对于程序的移植和调试是很有用的。 条件编译有三种形式, 下面分别介绍:
①第一种形式:
#ifdef 标识符
程序段1
#else
程序段2
#endif
它的功能是,如果标识符已被 #define命令定义过则对程序段1进行编译;否则对程序段2进行编译。如果没有程序段2(它为空),本格式中的#else可以没有,即可以写为:
  #ifdef 标识符
程序段 #endif
#define NUM ok
main(){
struct stu
{
int num;
char *name;
char sex;
float score;
} *ps;
ps=(struct stu*)malloc(sizeof(struct stu));
ps->num=102;
ps->name="Zhang ping";
ps->sex='M';
ps->score=62.5;
#ifdef NUM
printf("Number=%d\nScore=%f\n",ps->num,ps->score);
  #else
printf("Name=%s\nSex=%c\n",ps->name,ps->sex);
  #endif
free(ps);
}

由于在程序的第16行插入了条件编译预处理命令,因此要根据NUM是否被定义过来决定编译那一个printf语句。而在程序的第一行已对NUM作过宏定 义,因此应对第一个printf语句作编译故运行结果是输出了学号和成绩。在程序的第一行宏定义中,定义NUM表示字符串OK,其实也可以为任何字符串, 甚至不给出任何字符串,写为: #define NUM 也具有同样的意义。 只有取消程序的第一行才会去编译第二个printf语句。读者可上机试作。
②第二种形式:
#ifndef 标识符
程序段1
#else
程序段2
#endif
与第一种形式的区别是将“ifdef”改为“ifndef”。它的功能是,如果标识符未被#define命令定义过则对程序段1进行编译,否则对程序段2进行编译。这与第一种形式的功能正相反。

③第三种形式:
#if 常量表达式
程序段1
#else
程序段2
#endif
它的功能是,如常量表达式的值为真(非0),则对程序段1 进行编译,否则对程序段2进行编译。因此可以使程序在不同条件下,完成不同的功能
#define R 1
main(){
float c,r,s;
printf ("input a number: ");
scanf("%f",&c);
#if R
r=3.14159*c*c;
printf("area of round is: %f\n",r);
#else
s=c*c;
printf("area of square is: %f\n",s);
#endif
}
   本例中采用了第三种形式的条件编译。在程序第一行宏定义中,定义R为1,因此在条件编译时,常量表达式的值为真,故计算并输出圆面积。上面介绍的条件编译 当然也可以用条件语句来实现。但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序 段2,生成的目标程序较短。如果条件选择的程序段很长, 采用条件编译的方法是十分必要的。

本章小结
1. 预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程序员在程序中用预处理命令来调用这些功能。
2. 宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在宏调用中将用该字符串代换宏名。
3. 宏定义可以带有参数,宏调用时是以实参代换形参。而不是“值传送”。
4. 为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两边也应加括号。
5. 文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编译,结果将生成一个目标文件。
6. 条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了内存的开销并提高了程序的效率。
7. 使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计 

     原文转自(真心感谢作者)----http://blog.sina.com.cn/joyce11210125

7.11 其他指令

#undef指令 用来解除一个宏  #undef PENTIUM 这样,系统就会将原来已经声明的宏PENTIUM删除掉。

#pragma指令



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值