文档章节

第16章 C预处理器和C库 16.2 明显常量:#define

idreamo
 idreamo
发布于 2017/07/19 06:49
字数 1385
阅读 24
收藏 1

与所有预处理指令一样,预处理指令#define用#符号作为行的开头。

ANSI标准允许#符号前有空格或制表符,而且该标准还允许在#和指令的其他部分之间有空格。

指令可出现在源文件的任何地方。指令定义的作用域从定义出现的地方开始直到文件结尾

预处理器指令从#开始,到其后第一个换行符为止。也就是说指令的长度限于一行代码。但是正如前文提到的,在预处理开始前,系统会删除反斜线和换行符的组合。因此可以把指令扩展到几个物理行,由这些物理行组成单个逻辑行。

程序清单16.1   一些#define 指令的用法和属性

/*preproc.c--简单的预处理器的例子*/
#include <stdio.h>
#define TWO 2
#define OW "Consistency is the last refuge of the unimagina\
tive.-Oscar Wilde"    /*反斜线把这个定义延续到下一行*/
#define FOUR TWO*TWO
#define PX printf("X is %d.\n",x)
#define FMT "X ix %d.\n"

int main(void)
{
    int x=TWO;

    PX;
    x=FOUR;
    printf(FMT,x);
    printf("%s\n",OW);
    printf("TWO:OW\N");
    return 0;
}

每个#define 行(即逻辑行)由三部分组成。第一部分为指令#define自身第二部分为所选择的缩略语,这些缩略语称为宏(macro)。像本例中的这些宏用来代表值,它们被称为 类对象宏。宏的名字中不允许有空格,而且必须遵循C变量命名规则:只能使用字母、数字和下划线,第一个字符不能是数字。第三部分(#define 行的其余部分)称为替换列表(replacement list )或主体(body)。预处理器在程序中发现宏的实例后,总会用实体代替该宏。从宏变成最终的替换文体的过程称为宏展开

一般而言,预处理器发现程序中的宏后,会用它的等价替换文本代替宏。如果该字符串中还包括宏,则继续替换这些宏。例外的情况是双引号中的宏。将直接打印字符,而不是替换文本。因此下面的语句:

printf("TWO:OW");

将打印出:TWO:OW。

可以使用这行代码,打印出对应的列表:

printf("%d: %s\n",TWO,OW);

这里的宏位于双引号之外

什么时候应该使用符号常量?对大多数数字常量应该使用符号常量。如果是用于计算式的常量,那么使用符号名会更加清楚。如果数字代表数组大小,那么使用符号名后更容易改变数组大小和循环界限。如果数字是系统代码(如EOF),那么使用符号表示会使程序更加易于移植(只须改变EOF的定义)。记忆值的能力、易更改性、可移植性,这些功能使得符号常量很有使用价值。

16.2.1  语言符号

从技术方面讲,系统把宏的主体当作语言符号(token)类型字符串,而不是字符型字符串。C预处理器中的语言符号是宏定义主体中的单独的“词(word)”。用空白字符把这些词分开。例如:

#define FOUR 2*2

这个定义中有一个语言符号:即序列2*2。但是:

#defeine SIX 2 * 3

这个定义中有三个语言符号:2、*和3。

在处理主体中的多个空格时,字符型字符串和语言符号型字符串采用不同的方法。考虑下面的定义:

#define EIGHT 4 * 8

把主体解释成字符型字符串时,预处理器用4 * 8替换EIGHT。也就是说额外的空格也当作替换文体的一部分。但是,当把主体解释成语言符号类型时,预处理器用由单个空格分隔的三个语言符号,即4 * 8来替换EIGHT。换句话说,用字符型字符串的观点看,空格也是主体的一部分;而用语言符号字符串的观点看,空格只是分隔主体中语言符号的符号。

顺便提一下,C编译器处理语言符号的方式比预处理器的处理方式更加复杂。编译器能理解C的规则,不需要用空格来分隔语言符号。例如,C编译器把2*2当作3个语言符号。原因是C编译器认为每个2都是一个常量,而*是一个运算符。

16.2.2  重定义常量

假设您把LIMIT定义为20,后来在该文件中又把LIMIT定义为25。这个过程被称为重定义常量。

不同编译器采用不同的重定义策略。ANSI标准采用的方式:只允许新定义与旧定义完全相同。

相同定义,意味着主体具有相同顺序的语言符号。因此,下面两个定义相同:

#define SIX 2 * 3    #define SIX 2    *    3

两者都有相同的语言符号,而且额外的空格不是主体的一部分。下面的定义则被认为是不相同的:

#define SIX 2*3

上式只有一个语言符号,因此与前面的两个定义不相同。可以使用#undef指令重新定义宏

如果确实需要重定义常量,使用const关键字和作用域规则可能会更容易 。

© 著作权归作者所有

idreamo
粉丝 18
博文 139
码字总数 224743
作品 0
青岛
产品经理
私信 提问
#define 中的“ # 运算符”和“ ## 运算符”

利用宏参数创建字符串:# 运算符 在类函数宏(function-like macro)的替换部分中,“#”符号用作一个预处理运算符,它可以把语言符号(token)转化为字符串。例如,如果 x 是一个宏参量,那...

TMDJoJo
2012/07/07
0
0
[编程语言]C陷阱与缺陷

内容摘要 作者以自己1985年在Bell实验室时发表的一篇论文为基础,结合自己的工作经验扩展成为这本对C程序员具有珍贵价值的经典著作。写作本书的出发点不是要批判C语言,而是要帮助C程序员绕过...

21gprs
2014/05/23
0
0
C Primer Plus 第11章 11.7 ctype.h字符函数和字符串

第7章“C控制语句 分支和跳转”介绍了ctype.h系列字符相关的函数。这些函数不能被 应用于整个字符串,但是可以被应用于字符串中的个别字符。程序清单11.26定义了一个函数,它把toupper( )函数...

idreamo
2016/08/27
29
0
C语言基础教程之预定义和宏处理

define看起来很炫酷!C语言基础教程之预处理和宏定义 C语言预处理器 C语言预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C语言预处理器只不过是一个文本替换工具...

这个人很懒什么都没留下
2018/08/18
0
0
define看起来很炫酷!C语言基础教程之预处理和宏定义

C语言预处理器 C语言预处理器不是编译器的组成部分,但是它是编译过程中一个单独的步骤。简言之,C语言预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处...

诸葛青云999
2018/08/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

进程命令pgrep

命令pgrep 名称缩写: Process Grep 用途: 进程检索,以名称为依据从运行进程队列中查找进程,并显示查找到的进程id 语法: ]# pgrep [选项] 参数 选项: -d, --delimiter <string> 指定输出分...

迷失De挣扎
8分钟前
0
0
rest 的理解

rest:表现层状态转移。 什么是restful协议?https://en.wikipedia.org/wiki/Representational_state_transfer 使用restful的好处。 Rest是一种体系结构样式,他定义了一组用于创建web服务的...

xiaoxiao_go
15分钟前
0
0
聊聊spring cloud的CachingSpringLoadBalancerFactory

序 本文主要研究一下spring cloud的CachingSpringLoadBalancerFactory CachingSpringLoadBalancerFactory spring-cloud-openfeign-core-2.2.0.M1-sources.jar!/org/springframework/cloud/o......

go4it
昨天
4
0
一篇文章搞定——JDK8中新增的StampedLock

一、StampedLock类简介 StampedLock类,在JDK1.8时引入,是对读写锁ReentrantReadWriteLock的增强,该类提供了一些功能,优化了读锁、写锁的访问,同时使读写锁之间可以互相转换,更细粒度控...

须臾之余
昨天
4
0
Android Camera原理之CameraDeviceCallbacks回调模块

在讲解《Android Camera原理之openCamera模块(二)》一文的时候提到了CameraDeviceCallbacks回调,当时没有详细展开,本文我们详细展开讲解一下。 CameraDeviceCallbacks生成过程: 《Android...

天王盖地虎626
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部