您的位置:首页 > 其它

C89标准宏定义部分(中文翻译)

2010-07-04 23:05 375 查看
宏替换
1、当且仅当两个替换序列中的预处理标记具有相同的数目、顺序、拼写和空白分隔符时,它们才是等价的,其中所有的空白分隔符都被看成是等同的。

2、被定义为对象式宏(不具有lparen的宏)的标识符可被另外一个#define预处理指令重定义,只要第二个定义也是对象式宏,且它们的替换序列等价;否则程序是非法的。

3、被定义为函数式宏(具有lparen的宏)的标识符可被另外一个#define预处理指令重定义,只要第二个定义也是函数式宏,并同第一个函数式宏具有相同数目和拼写的参数,且它们的替换序列等价;否则程序是非法的。例如:
#define a(b) b
#define a(c) c
在vc中,上述定义是合法的,因为它们的定义在逻辑上完全等价。但依据标准,两个函数式宏的参数拼写不同(第一个为b,第二个为c),所以非法。

4、函数式宏调用的实参数目应该同宏定义中的形参数目一致,调用以预处理标记 ) 终止。[注:在程序文本中,只要一个函数式宏名的后面跟一个左括号,便认为是对此函数式宏的调用,而不对调用参量的数目进行检查匹配,这也是预处理器中需要向前看一个预处理标记的地方。如果函数式宏调用的实参数目与宏定义中的形参数目不一致将如何处理呢,不同的预处理器应该有不同的处理方法,这也是标准中没有明确的地方。下面以vc6为目标对上述情况进行测试,测试代码如下:
#define sum(b, c) b+c
int sum;
sum = sum(1, 2, 3);
在上述代码中,sum宏调用的实参数目多于其定义的形参数目,这种情况下vc6会忽略多于的实参,仅产生一个警告信息:warning C4002: too many actual parameters for macro 'sum'。
如果将上述代码中的最后一行改写为
sum=sum(1);
这种情况下sum宏调用的实参数目少于其定义的形参数目,vc6将同时报告一个警告信息和一个错误信息:
warning C4003: not enough actual parameters for macro 'sum';
rror C2059: syntax error : ';'
]

5、函数式宏的参数标识符在其作用域内必须是唯一声明的。例如:
#define a(b, b) b
是非法的,因为函数式宏a的参数b在其作用域内重复声明了两次。

6、紧跟在define后面的标识符称为宏名。对于宏名,存在一个独立的名字空间。上述任何形式的宏(对象式或函数式),预处理标记替换序列之前或之后的空白字符都将忽略而不作为替换序列的一部分。

7、如果标识符位于#预处理标记之后,且在词法上#位于合法的预处理指令起始点,则不对此标识符进行宏替换。例如:
#define test if
#test 1
在第二行中,test位于#之后,且#位于合法的预处理指令起始点,所以不将test替换为第一行定义的if。

8、具有如下形式的预处理指令
# define identifier replacement-list new-line
定义了一个对象式宏,并且致使每一个后续的宏名实例(在宏替换期间,所有的字符文字和字符串文字都是预处理标记,而不是可能包含标识符子序列的序列,所以不对其进行扫描以搜索宏名或参数)被替换为预处理指令中的替换序列(候选标记不是标识符,即使其拼写完全由字母和下划线组成。所以无法定义一个名字等于候选标记的宏)。之后,按下文将描述的方式对替换序列进行二次扫描以搜索更多的宏名。

9、具有如下形式的预处理指令
# define identifier lparen identifier-listopt ) replacement-list new-line
定义了一个带有参数的函数式宏,在句法上,其类似于函数调用。宏的参数由圆括号中可选的标识符列表指定,参数的作用域始于在标识符列表中的声明,止于#define预处理指令的终止符——换行符。对于每一个后续函数式宏名实例,如果其之后的预处理标记为(,那么该宏名实例引入了一组将被宏定义中的替换列表所替换的预处理标记序列(一次宏调用)。
被替换的预处理标记序列止于同lparen配对的 ) 预处理标记。在触发函数式宏调用的预处理标记序列中,换行符被当作普通的空白字符对待。例如:
#define sum(a, b) a+b
int m, n, s;
m = 11;
n = 23;
s = sum(
m,
n
);
上述代码中对sum函数式宏的调用是合法的,因为宏调用中的换行符等同于普通的空白字符。

10、被最外层配对圆括号界定的预处理标记序列构成了函数式宏的参量列表。参量由逗号预处理标记分隔,但位于内层配对圆括号内的逗号不起分隔参量的作用。如果(在参量置换之前)任一参量为空,程序的行为是未定义的。另外,如果参量列表中的预处理标记序列能构成预处理指令,程序的行为也是未定义的。
[例如:
#define sum(a, b) a+b
int s = sum((1, 2), 3);
在vc6中,s的结果为5。
]

16.3.1 参量置换
1、在识别出函数式宏调用的参量之后,进行参量替换操作。除了前置#或##的预处理标记或后置##的预处理标记的参数之外,替换序列中的参数在所有宏展开之后被替换为相应的参量。在置换发生之前,对每一个参量预处理标记进行完全的宏替换,就好像它们独自构成了翻译单元的后续部分,而不存在其它的预处理标记(由此导致的一个结果是:在对参量预处理标记进行宏替换之后,对于替换结果的最后一个预处理标记不再进行函数式宏检测)。

16.3.2 #操作符
1、在函数式宏的替换序列中,每一个#预处理标记后面都必须跟一个参数。
2、在替换序列中,如果一个参数紧跟在#预处理标记之后,那么它们将被一字符串文字预处理标记所替换,字符串文字的内容等于相应参量预处理标记的拼写。在字符串文字中,用单个空格替换参量的预处理标记间的每一处空白(见下面的例子)。构成参量的第一个预处理标记之前和最后一个预处理标记之后的空白将被删除。另外,参量中的每一个预处理标记的拼写被原封不动地保留在字符串文字中,但为了保留原始拼写需要进行一些特殊处理:
在字符文字或字符串文字中的每一个”和/字符之前插入/字符。如果替换结果不是一个有效的字符串文字,程序的行为是未定义的。#和##操作符的处理顺序是未指定的。例如:
#define x(s) #s
char *s = x(a b/**/ c); // s = “a b c”;
char *p = x(a/nb); // p = “a//nb”;

16.3.3 ##操作符
1、对任一形式的宏定义(对象式宏或函数式宏),##预处理标记都不能出现在替换序列的开始或末尾。
2、在替换序列中,如果一个参数紧位于##预处理标记之前或之后,它将被相应参量的预处理标记序列替换。
3、不论是对象式宏调用还是函数式宏调用,在对替换序列进行再次扫描以搜索更多的宏名之前,替换序列中的每一个##预处理标记实例(而不是由参量引入的)将被删除,##预处理标记的前置与后置预处理标记被连接在一起形成单个预处理标记。如果结果不是一个有效的预处理标记,程序的行为是未定义的。结果标记对于二次宏替换是有效的。##操作符的处理顺序是未指定的。

16.3.4 重扫描和二次替换
1、当替换序列中所有参数被置换之后,所得的预处理标记同源文件中的所有后续预处理标记一起被重扫描,以搜索更多的宏名进行替换。
2、如果在扫描替换序列时,发现了正被替换的宏名,不再对该宏名进行替换(防止直接递归,如下例中,在扫描y的替换序列y, +, x时,首先找到了正被替换的宏名y,此时不再对其进行展开,否则会造成死循环)。再者,如果在任意嵌套的替换中遇到了正被替换的宏名,也不再对其进行替换(防止间接递归)。这些没有被替换的宏名预处理标记在进一步的替换中不再有效,即使在稍后的二次检测中该宏名预处理标记是可替换的(参见16.3.5中的第5款)。例如:
#define y y+x
3、不对宏替换后的预处理标记序列进行预处理指令的识别和处理。例如:
#define o(s) s
o(#define g 100)
第2行的宏展开应该为#define g 100,但按上述要求,不对宏替换后的预处理标记序列进行预处理指令的处理,所以预处理器不能正确识别此条宏定义指令,从而把它留给编译器处理。毫无疑问,编译器处理这样的语句是会发生错误的,vc对上述定义的报错信息如下:
… : error C2121: '#' : invalid character : possibly the result of a macro expansion
… : error C2146: syntax error : missing ';' before identifier 'g'
… : error C2501: 'define' : missing storage-class or type specifiers
… : fatal error C1004: unexpected end of file found

16.3.5 宏定义的作用域
1、宏定义(独立于块结构)在遇到相应的#undef指令之前或(没有遇到相应的#undef指令)在翻译单元结束之前一直有效。
2、形式如下的预处理指令
# undef identifier new-line
导致指定的标识符不再被定义为宏名。如果指定的标识符当前本来就不是宏名,该指令被忽略。
3、[注:宏定义的最简单用法是用来定义“明示常量”,如下所示:
#define TABSIZE 100
int table[TABSIZE];
4、下面的代码定义了一函数式宏,其值取参量的最大值。宏定义的优点有:对于任何类型兼容的参量均适用;产生内联代码从而避免了函数调用的额外开销。其缺点有:可能对其中的某个参量计算多次(包括副作用);如被多次调用的话,产生的代码多于函数。另外,无法取得宏定义的地址,因为它根本就没有地址。
#define max(a, b) ((a) > (b) ? (a) : (b))
宏定义中的圆括号保证参量和结果表达式都被正确地界定。
5、为了说明重定义和重检测规则,参看如下序列:
#define x 3
#define f(a) f(x * (a))
#undef x
#define x 2
#define g f
#define z z[0]
#define h g( ˜
#define m(a) a(w)
#define w 0,1
#define t(a) a
f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
g(x+(3,4)-w) | h 5) & m
(f)ˆm(m);
其结果为
f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
f(2 * (2+(3,4)-0,1)) | f(2 * ( ˜ 5)) & f(2 * (0,1))ˆm(0,1);
需要说明的是结果1中的最后一项t(1),预处理器并没有进一步将其替换为1,这是因为在对t(t(g)(0) + t)进行替换的时候,预处理器并没有对最后一个t进行替换。

6、为了说明字符串文字创建和标记连接规则,参看如下序列:
#define str(s) # s
#define xstr(s) str(s)
#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", /
x ## s, x ## t)
#define INCFILE(n) vers ## n /* from previous #include example */
#define glue(a, b) a ## b
#define xglue(a, b) glue(a, b)
#define HIGHLOW "hello"
#define LOW LOW ", world"
debug(1, 2);
fputs(str(strncmp("abc/0d", "abc", ’/4’) /* this goes away */
= = 0) str(: @/n), s);
#include xstr(INCFILE(2).h)
glue(HIGH, LOW);
xglue(HIGH, LOW)
其结果为
printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs("strncmp(/"abc//0d/", /"abc/", ’//4’) = = 0" ": @/n", s);
#include "vers2.h" (after macro replacement, before file access)
"hello";
"hello" ", world"
进一步进行字符串文字连接之后的结果为
printf("x1= %d, x2= %s", x1, x2);
fputs("strncmp(/"abc//0d/", /"abc/", ’//4’) = = 0: @/n", s);
#include "vers2.h" (after macro replacement, before file access)
"hello";
"hello, world"
宏定义中#和##标记周围的空白是可选的。

7、最后,示范一下重定义规则,以下序列是合法的:
#define OBJ_LIKE (1-1)
#define OBJ_LIKE /* white space */ (1-1) /* other */
#define FTN_LIKE(a) ( a )
#define FTN_LIKE( a )( /* note the white space */ /
a /* other stuff on this line
*/ )
但如下的重定义是非法的:
#define OBJ_LIKE (0) /* different token sequence */
#define OBJ_LIKE (1 - 1) /* different white space */
#define FTN_LIKE(b) ( a ) /* different parameter usage */
#define FTN_LIKE(b) ( b ) /* different parameter spelling */

相关资料链接:

 
在HP公司的Digital Unix中
DEC C Language Reference Manual
http://h30097.www3.hp.com......F_HTML/AQTLTBTE/TITLE.HTM

开放标准组织的 
http://www.open-std.org/jtc1/sc22/wg14/

SCO Unix
http://uw713doc.sco.com/e......rocessing_Directives.html
都有关于预处理的介绍
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息