C语言中的函数型宏中的参数替换

C语言中的宏分为两种,对象型和函数型,两者的区别是,函数型带有参数,因此复杂一些。函数型宏参数的简单替换比较简单,稍微复杂一点的情况是在替换字符串中参数前面带有#或##这种前导符号的情况。关于涉及到#和##的替换规则,C语言标准有一大段论述,令人头疼。下面试图以示例简单说明一下。比如下面的例子,

#define SIZE(typename) sizeof(typename)

则宏“SIZE(int)”展开应该是“sizeof(int)”,为方便起见,我们将此展开记作:

SIZE(int)     |--->   sizeof(int)


【1】如果参数带有#前置符,即

#define SIZE(typename) sizeof(#typename)

SIZE(int)  |---> sizeof("int")

即“#"的作用是将形式参数替换为以“双引号包围起来的实际参数”构成的符号串。这样的话,当我们希望参数替换是发生在字符串中的时候,用这个参数可以派上用场。但是需要注意的是,如果参数在替换字符串中已经是被字符串包围,那么替换是不会发生的,比如

#define SIZE(typename) sizeof("typename") 
或
#define SIZE(typename) sizeof("#typename")

则展开情况分别是

SIZE(int)  |---> sizeof("typename")
或
SIZE(int)  |---> sizeof("#typename")
显然,没有发生任何替换。如果希望在一个字符串中间进行替换,怎么办呢?比如希望字符串 "The size of the typename is given"中的 typename被替换为你所需要的类型名字,可以这么处理:

#define SIZE(typename) "The size of the "#typename" is given"
如此,该宏展开就能得到所需的字符串了,因为C语言将 "The size of the ""int"" is given"视作与 "The size of the int is given"等价。#的作用是标识宏参数,并将其替换为C语言格式的字符串。换言之, 先参数替换,然后用双引号包围起来,视情况,还要在前面添加一个空格起分隔作用。


【2】如果希望替换不是发生在字符串,而是直接发生在程序文本上,那有怎么办呢?比如,以上面的宏SIZE(typename)为例,当typename赋予int时,我希望是直接调用一个函数sizeof_int(),这又该怎么办呢?按前面方法定义,

#define SIZE(typename) sizeof_typename()
或
#define SIZE(typename) sizeof_#typename()
则SIZE(int)展开后得到

sizeof_typename()
或
sizeof_ "int"()
前者根本不发生展开,后者展开根本不是所需。此时就可以使用“##”了,宏定义如下

#define SIZE(typename) sizeof_##typename()
则SIZE(int)展开后得到

sizeof_int()
“##”的作用实际上是将实际参数的字面名字(即程序源文件中标识该参数的字符串)与替换字符串中的其他部分连接起来。在上例中,实际参数int的字面名字是int,"##"的作用是将"int"与替换字符串中的其他部分,即"sizeof_"和"()",连接起来,换言之,先参数替换,然后执行字符串连接操作


【3】一些注意事项。

其一,当替换字符串中的参数处于字符串中的时候,即"typename"、"#typename"或者"##typename",替换都不会发生。

其二,在替换字符串中,参数是一个可以识别的“词”,即typename, #typename, ##typename在替换字符串中都要是一个可以区分开的词,typenamexx, #typenamexx,##typenamexx(x是字母或数字)都是不好的形式:typenamexx,根本不会发生替换;#typenamexx,不会替换,还报错,提示#后面跟的不是宏参数;##typenamexx,不会替换,且因为##是连接操作,会将typenamexx与替换字符串的其他部分连接起来。

其三,替换后所得字符串,需要符合词法的规定。比如该用##时用了#,#define SIZE(typename) sizeof_#typename(),替换可以发生,但是结果是,sizeof_ "int"(),并不符合词法,会报错;或者该用#或不该有前导符时,用了##,比如,#define SIZE(typename) sizeof(##typename),替换也可以发生,但是把左括号"("与"int"(以int为例)连接起来得到的"(int"不是一个合法的符合词法的token,所以也会报错。

附:上面实例都用gcc附带的cpp程序试验过,C++语言的规则应该是一样的。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值