C语言参考手册

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

版权说明:

本资料内容摘录自《C程序设计语言(第二版)》K&R著 徐宝文 李志译 尤晋元审校 机械工业出版社出版 一书。版权属原作者和出版社所有。制作本资料为了我本人学习和参考,非商业用途。

建议读者阅读原书学习比较好,它更详细。

目录:

A.12 预处理

主要介绍预处理器的功能和预处理的过程。

A.12.1三字符序列

主要介绍 ??=, ??(, ??<等三字符序列。

A.12.2 行连接

主要介绍反斜杠\结束的指令行处理过程。

A.12.3 宏定义和扩展

主要介绍#define 标识符 记号序列,#define 标识符(标识符表opt) 记号序列,#undef 标识符,还有#和##等一些东西,有一些例子。

A.12.4 文件包含

主要介绍#include <文件名>和#include “文件名”指令。

A.12.5 条件编译

介绍#if 常量表达式/#ifdef 标识符/#ifndef 标识符,#elif 常量表达式,#else,

#endif语句。

A.12.6 行控制

介绍#line指令。

A.12.7 错误信息生成

介绍#error指令。

A.12.8 pragma

介绍#pragma。

A.12.9 空指令

介绍空指令#。

A.12.10 预定义名字

介绍__LINE__, __FILE__, __DATE__, __TIME__, __STDC__等。

A.12 预处理 返回目录

预处理器执行宏替换,条件编译以及包含指定的文件。以#开头的命令行(“#”前可以有空格)就是预处理器处理的对象。这些命令行的语法独立于语言的其它部分,他们可以出现在任何地方,其作用可以延续到所在编译单元的末尾(与作用域无关)。行边界是有实际意义的;每一行都将单独进行分析(有关如何将行连接起来的详细信息参考A.12.2节)。对预处理器而言,记号可以是任何语言记号,也可以是类似于#include指令(参见A.12.4节)中表示文件名的字符序列。此外,所有未进行其它定义的字符都将被认为是记号。但是,在预处理器指令行中,除空格,横向制表符外的其它空白字符的作用是没有定义的。

预处理过程在逻辑上可以划分为几个连续的阶段(在某些特殊的实现中可以缩减)。

1) 首先,将A.12.1节所述的三字符序列替换为等价字符。如果操作系统环境需要,还要在原文件的各行

之间插入换行符。

2) 将指令行中位于换行符前的反斜杠符\删掉,以把各指令行连接起来(参见A.12.2节)。

3) 程序分成用空白符分割的记号。注释将被替换为一个空白符。接着执行预处理指令,并且进行宏扩展

(参见A.12.3节~A.12.10节)。

4) 将字符常量和字符串字面值中的转义字符序列替换为等价字符,然后把相邻的字符串字面值连接起来。

5) 收集必要的程序和数据,并将外部函数和对象的引用与其定义连接,翻译经过以上处理的结果,然后与

其它程序和库连接起来。

A.12.1 三字符序列 返回目录

C语言源程序的字符集是7位ASCII码的子集,但它是ISO 646-1983不变代码集的超集。为了将程序通过这种缩减的字符集表示出来,下列所示的所有三字符序列都要用相应的单个字符替换,这种替换在进行所有其它处理之前进行。

??= # ??( [ ??< {

??/ \ ??) ] ??> }

??’ ^ ??! | ??- ~

除此之外不进行其它替换。

说明:三字符序列是ANSI标准新引入的特征。

A.12.2 行连接

通过将以反斜杠\结束的指令行末尾的反斜杠和其后的换行符删除掉,可以将若干指令行合并一行。这种处理要在分隔记号之前进行。

A.12.3 宏定义和扩展 返回目录

类似于下列形式的控制指令:

#define 标识符 记号序列

将使得预处理器把该标识符后续出现的各个实例用给定的记号序列替换。记号序列前后的空白符将被丢弃掉。第二次用#define指令定义同一个标识符是错误的,除非第二次定义的标记序列与第一次相同(所有的空白分隔符将被认为是相同的)。

类似于下列形式的指令行:

#define 标识符(标识符表opt) 记号序列

是一个带有形式参数(由标识符表指定)的宏定义,其中第一个标识符与圆括号(之间没有空格。同第一种形式一样,记号序列前后的空白符都将被丢掉。如果要对宏进行重新定义,则必须保证其形式参数个数、拼写及记号序列都必须与前面的定义相同。

类似于下列的控制指令:

#undef 标识符

用于取消标识符的预处理器定义。将#undef应用于未知标识符(即未用#define指令定义的标识符)并不会导致错误。

按照第二种形式定义宏时,宏标识符(后面可以跟一个空白字符,空白符是可选的)及其后用一对圆括号括起来的、由逗号分隔的记号序列就构成了一个宏调用。宏调用的实际参数使用逗号分隔的记号序列,用引号或嵌套的括号括起来的逗号不能用于分隔实际参数。在处理过程中,实际参数不能进行宏扩展。宏调用时,实际参数的数目必须与定义中的形式参数的数目匹配。实际参数被分离后,前导和尾部的空白符被删除。随后,由各实际参数产生的记号序列将替换未用引号引起来的相应形式参数的标识符(位于宏的替换记号序列中)。除非替换序列中形式参数的前面有一个#符号,或者其前面或后面有一个##符号,否这,在插入前要对宏的实际参数记号进行检查,并在必要时进行扩展。

两个特殊的运算符会影响替换过程。首先,如果替换记号序列中的某个形式参数前面直接是一个#符号(它们之间没有空白符),相应形式参数的两边将被加上双引号(″),随后,#和形式参数标识符将被用引号引起来的实际参数替换。实际参数中的字符串字面值、字符常量两边或内部的每个双引号(″)或反斜杠(\)前面都要插入一个反斜杠(\).

其次,无论哪种宏的定义记号序列中包含一个##运算符,在形式参数替换后都要把##及前后的空白符都删除掉,以便将相邻记号连接起来形成一个新记号。如果这样产生的记号无效,或者结果依赖于##与算符的处理顺序,则结果没有定义。同时,##也可以不出现在替换记号序列的开头和结尾。

对这两种类型的宏,都要重复扫描替换记号序列以查找更多的已定义标识符。但是,当某个标识符在某个扩展中被替换后,再次扫描并再次遇到此标识符是不再对其执行替换,而是保持不变。

即使执行宏扩展后得到的最终结果以#打头,也不认为它是预处理指令。

说明:有关宏扩展处理的细节信息,ANSI标准比第一版描述得更详细。更重要的变化是加入了#和##与算符,这就使得引用和连接成为可能。某些新规则(特别是与连接有关的规则)比较独特(参见下面的例子)。

例如,这种功能可以用来定义“表示常量”,如下例所示:

#define TABSIZE 100

int table[TABSIZE];

定义

#define ABSDIFF(a, b) ((a)>(b) ? (a)-(b) : (b)-(a))

定义了一个宏,它返回两个参数之差的绝对值。与执行相同功能的函数所不同的是,参数与返回值可以是任意算术类型,甚至可以是指针。同时,参数可能有副作用,而且需要计算两次,一次进行测试,一次则生成值。

假定有下列定义:

#define tempfile(dir) #dir ″/%s″

宏调用tempfile(/usr/tmp)将生成

″/usr/tmp″″/%s″

随后,该结果将被连接为一个单个的字符串。给定下列定义:

#define cat(x, y) x ## y

那么,宏调用cat(var, 123)将生成var 123。但是,宏调用cat(cat(1,2),3)没有定义:##阻止了外层调用的参数的扩展。因此,他将生成下列记号串:

cat( 1 , 2 )3

并且,)3不是一个合法的记号,他由第一个参数的最后一个记号与第二个参数的第一个记号连接而成。如果在引入第二层的宏定义,如下所示:

#define xcat(x,y) cat(x,y)

我们就可以得到正确结果。xcat(xcat(1,2),3)将生成123,因为xcat自身的扩张不包含##运算符。

类似地,ABSDIFF(ABSDIFF(a,b),c)将生成所期望的经完全扩展后的结果。

A.12.4 文件包含 返回目录

下列形式的控制指令:

#include <文件名>

相关文档
最新文档