C语言中的宏定义

本文详细介绍了C语言中的宏定义,包括简单宏、带参数的宏、#和##运算符的用法,以及宏定义中的do-while循环和空操作。宏定义在程序中用于简化代码、提供类型安全、控制条件编译等,但也存在一些缺点,如无类型检查和可能导致代码膨胀。文章还讨论了预定义宏和常用宏技巧,并提供了实例来说明宏的实际应用。
摘要由CSDN通过智能技术生成
               

1. 简单宏定义

简单的宏定义有如下格式:

[#define指令(简单的宏)]  #define  标识符替换列表

替换列表是一系列的C语言记号,包括标识符、关键字、数、字符常量、字符串字面量、运算符和标点符号。当预处理器遇到一个宏定义时,会做一个 “标识符”代表“替换列表”的记录。在文件后面的内容中,不管标识符在任何位置出现,预处理器都会用替换列表代替它。

不要在宏定义中放置任何额外的符号,否则它们会被作为替换列表的一部分。一种常见的错误是在宏定义中使用 = :

#define N = 100       /*** WRONG ***/int a[N];            /* 会成为 int a[= 100]; */

在上面的例子中,我们(错误地)把N定义成一对记号(= 和100)。

在宏定义的末尾使用分号结尾是另一个常见错误:

#define N 100;       /*** WRONG ***/int a[N];            /*    become int a[100;]; */

这里N被定义为100和;两个记号。

在一个宏定义中,编译器可以检测到绝大多数由多余符号所导致的错误。但不幸的是,编译器会将每一处使用这个宏的地方标为错误,而不会直接找到错误的根源——宏定义本身,因为宏定义已经被预处理器删除了。

简单的宏主要用来定义那些被Kernighan和Ritchie称为“明示常量”(manifest constant)的东西。使用宏,我们可以给数值、字符和字符串命名。

#define STE_LEN 80#defineTRUE     1#defineFALSE    0#definePI        3.14159#defineCR        '\r'#defineEOS       '\0'

使用#define来为常量命名有许多显著的优点:

1) 、 程序会更易读。一个认真选择的名字可以帮助读者理解常量的意义。否则,程序将包含大量的“魔法数”,使读者难以理解。

2) 、 程序会更易于修改。我们仅需要改变一个宏定义,就可以改变整个程序中出现的所有该常量的值。“硬编码的”常量会更难于修改,特别是有时候当他们以稍微不同的形式出现时。(例如,如果一个程序包含一个长度为100的数组,它可能会包含一个从0到99的循环。如果我们只是试图找到所有程序中出现的100,那么就会漏掉99。)

3) 、可以帮助避免前后不一致或键盘输入错误。假如数值常量3.14159在程序中大量出现,它可能会被意外地写成3.1416或3.14195。

虽然简单的宏常用于定义常量名,但是它们还有其他应用。

4) 、可以对C语法做小的修改。实际上,我们可以通过定义宏的方式给C语言符号添加别名,从而改变C语言的语法。例如,对于习惯使用Pascal的begin和end(而不是C语言的{和})的程序员,可以定义下面的宏:

#define BEGIN  {
    #define END    }

我们甚至可以发明自己的语言。例如,我们可以创建一个LOOP“语句”,来实现一个无限循环:

#define LOOP   for (;;)

当然,改变C语言的语法通常不是个好主意,因为它会使程序很难被其他程序员所理解。

5) 、对类型重命名。在5.2节中,我们通过重命名int创建了一个Boolean类型:

#define BOOL int

虽然有些程序员会使用宏定义的方式来实现此目的,但类型定义(7.6节)仍然是定义新类型的最佳方法。

6) 、控制条件编译。如将在14.4节中看到的那样,宏在控制条件编译中起重要的作用。例如,在程序中出现的宏定义可能表明需要将程序在“调试模式”下进行编译,来使用额外的语句输出调试信息:

#define DEBUG

这里顺便提一下,如上面的例子所示,宏定义中的替换列表为空是合法的。

当宏作为常量使用时,C程序员习惯在名字中只使用大写字母。但是并没有如何将用于其他目的的宏大写的统一做法。由于宏(特别是带参数的宏)可能是程序中错误的来源,所以一些程序员更喜欢使用大写字母来引起注意。其他人则倾向于小写,即按照Kernighan和Ritchie编写的The C Programming Language一书中的样式。

2. 带参数的宏

带参数的宏定义有如下格式:

[#define指令—带参数的宏]  #define 标识符(x1, x2,…,xn)替换列表

其中x1, x2,…,xn是标识符(宏的参数)。这些参数可以在替换列表中根据需要出现任意次。

在宏的名字和左括号之间必须没有空格。如果有空格,预处理器会认为是在定义一个简单的宏,其中(x1,x2,…,xn)是替换列表的一部分。

当预处理器遇到一个带参数的宏,会将定义存储起来以便后面使用。在后面的程序中,如果任何地方出现了标识符(y1,y2,…,yn)格式的宏调用(其中y1,y2,…,yn是一系列标记),预处理器会使用替换列表替代,并使用y1替换x1y2替换x2,依此类推。

例如,假定我们定义了如下的宏:

#define MAX(x,y)    ((x)>(y) ? (x) :(y))#define IS_EVEN(n)   ((n)%2==0)

现在如果后面的程序中有如下语句:

i = MAX(j+k, m-n);if (IS_EVEN(i)) i++;

预处理器会将这些行替换为

i = ((j+k)>(m-n)?(j+k):(m-n));if (((i)%2==0)) i++;

如这个例子所显示的,带参数的宏经常用来作为一些简单的函数使用。MAX类似一个从两个值中选取较大的值的函数。IS_EVEN则类似于另一种函数,该函数当参数为偶数时返回1,否则返回0。

下面的例子是一个更复杂的宏:

#define TOUPPER(c)('a'<=(c)&&(c)<='z
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值