C语言参考手册阅读笔记:预处理指令之#,##,可变参数


---------------------------------------------------------------
 3.3.8 将记号转换为字符串 [C语言参考手册,p38(r54)]
---------------------------------------------------------------
    标准C语言中有一种机制可以将宏参数(扩展之后)转换为字符串型常量。在此之前,编程人员要利用许多C语言预处理器中的漏洞以不同方式达到相同结果。
    在标准C语言中,宏定义中出现的#记号被当作一元“字符串化”运算符,后面为宏正式参数名。宏扩展期间,#和正式参数名换成相应的包含在字符串引号当中的实际参数。生成字符串时,记号参数表中的每个空白序列换成一个空格符,任何嵌人引号和反斜杠前面加上一个反斜杠以保留其在字符串中的含义。参数开头和末尾的空白符忽略,因此空参数扩展为空字符串""(即使逗号之间有空白符)。

例:考虑TEST宏的标准c语言定义

#define TEST(a,b) printf(#a "<" #b "=%d/n", (a)<(b))
语句TEST(0,0xFFFF);TEST('/n', 10);展开如下:
printf("0" "<" "0xFFFF" "=%d/n", (0)<(0xFFFF));
printf("'//n'" "<" "10 "=%d/n", ('/n')<(10));

---------------------------------------------------------------
 3.3.9 宏扩展中的记号合并 [C语言参考手册,p38(r54)]
---------------------------------------------------------------
    标准C语言中合并记号形成新记号时,由宏定义中的合并运算符##控制。重新扫描更多宏之前,宏替换表中任何运算符##中间的两个记号合并成一个记号。因此必须有这种记号:##不能放在替换表开头或末尾。如果合并之后得不到有效记号,则结果是未定义的。

例子:
#define TEMP(i) temp ## i
TEMP(1) = TEMP(2 + k) + x;
预处理之后变成:
temp1 = temp2 + k + x;

    上例中,扩展TEMP()+x时可能出现一个奇妙的情形。宏定义是有效的,但##右边没有要组合的记号(除非遇到+,但这不是我们所要的)。要解决这个问题,应把正式参数i看成是专为##扩展的特殊的“空”记号。这样,TEMP()+x扩展的结果将如我们预料的那样为temp+x。
    不能用记号拼接产生通用字符名。
    和宏参数转换成字符串时一样(见3.3.8节),编程人员可以利用许多非标准C语言实现中的漏洞获得这种合并功能。尽管C语言的原始定义明确地把宏体描述成记号序列,而不是字符序列,但许多语言编译器把宏体当作字符序列一样扩展和重新扫描。这在编译器处理注释语句时更加明显,编译器将注释语句完全删除而不是换成空格,因此一些巧妙编写的程序就可以利用这个特性。
例:考虑下列例子:
#define INC        ++
#define TAB        internal_table
#define INCTAB    table_of_increments
#define CONC(x,y)    x/**/y
CONC(INC,TAB)

    标准C语言将CONC体解释为两个记号x和y,用空格分开(注释语句变成空格)。调用CONC(INC,TAB)扩展为两个记号INC TAB。但有些非标准C语言实现直接删除注释语句,然后重新扫描宏体中的记号,这样就把CONC(INC,TAB)扩展为一个记号INCTAB;

 -----------------------------------------------
 步骤    标准C语言扩展        可能的非标准扩展
 -----------------------------------------------
 1    CONC(INC,TAB)        CONC(INC,TAB)
 2    INC/**/TAB                  INC/**/TAB
 3    INC TAB                      INCTAB
 4    ++ internal_table      table_of_increments


测试代码:

#define XVARNAME(i)        x##i
#define PXVARNAME(n)    printf("x"#n" = %d/n",x##n)

int k = 0;
int b = 0;
int XVARNAME(k)=12;
    PXVARNAME(k + 1);                         //xk + 1 = 13
    PXVARNAME(k);                                //xk = 12
    b = XVARNAME(k) + 0;
    printf("b = %d, k =  %d/r/n", b, k);    //b = 12, k = 0
    b = XVARNAME(k + 10) + 0;
    printf("b = %d, k =  %d/r/n", b, k);    //b = 22, k = 0


---------------------------------------------------------------
 3.3.10  宏中的可变参数表 [C语言参考手册,p38(r54)]
 notice: Visual C++ 直至8.0版才开始支持C99的可变宏定义
---------------------------------------------------------------
#ifdef DEBUG
#define myprintf(...) fprintf(stderr, __VA_ARGS__)
#else
#define myprintf(...) fprintf(__VA_ARGS__)
#endif

特别地:
#define make_em_str(...) #__VA_ARGS__
下列调用
make_em_str(a,b,c,d)
展开成字符串
"a,b,c,d"


-------------------------------------------------------------------------------------------------------------------------
题外话:
讨厌得益网的积分增加方法,但是,那里的有些东东确实不错,如果您愿意,帮忙点一下:
http://www.netyi.net
--------------------------------------------------------------------------------------------------------------------------

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值