小谈Token“##”的作用以及与"#"的区别~~
2010-09-28 16:42
405 查看
今下午调试修改过的程序,用cl.exe在32位命令行窗口下编译,提示有两个错误:
第一个是const char[1]不能转化为BSTR,第二个是const char[1]不能转化为OLECHAR *类型
根据提示所标明的错误行,找到第二个错误产生的地方:
SysAllocString(XSTR(""));
幸好之前啃过库里的若干代码,记得这个XSTR的出处,于是打开apglobal.h,查到的XSTR在非MAC系统下的定义为
#define XSTR(x) L##x
L符号我知道,这两个##是什么东西,起什么作用呢?谷歌一番,找到这篇说明:
(刚好作者说从CSDN摘下来的,算是取之于彼用之于彼,哈哈)
#define f(a,b) a##b
#define d(a) #a
#define s(a) d(a)
void main( void )
{
puts(d(f(a,b)));
puts(s(f(a,b)));
}
输出结果:
f(a,b)
ab
分析: ##把两个符号连起来
#a指把a当成符号,就是把#后面的看成字符串
# 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令
#define to_string( s ) # s
将会使编译器把以下命令
cout < < to_string( Hello World! ) < < endl;
理解为
cout < < "Hello World!" < < endl;
使用##连结##前后的内容. 例如, 命令
#define concatenate( x, y ) x ## y
...
int xy = 10;
...
将会使编译器把
cout < < concatenate( x, y ) < < endl;
解释为
cout < < xy < < endl;
理所当然,将会在标准输出处显示'10'.
puts(d(f(a,b))); ----> 因为d宏中的参数是另外一个宏,且带##,所以作为参数的宏不展开,相当于
puts(#f(a,b));----->puts("f(a,b)");
puts(s(f(a,b))); ----> 因为s宏中的参数是另外一个宏,但不带##,所以作为参数的宏先展开,相当于
puts(s(ab));----->puts(d(ab));---->puts(#ab);---->puts("ab");
#define f(a,b) a##b
#define d(a) #a --》 以"#"开头的,直接替换,不展开:immediately replaced by the unexpanded actual argument
#define s(a) d(a) --》 非以"#"开头的,先展开,再替换,也就是一般的情况
所以就两种情况:
1,不以"#"开头的,先展开参数a,然后是替换代码:puts(s(f(a,b)));-->puts(s(ab))-->puts(d(ab))-->puts("ab")
2,以"#"开头的,直接替换,不展开:puts(d(f(a,b)))-->puts("f(a,b)")
#include <stdio.h>
#define DIRECT_LITERAL(a) #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
int main(void)
{
puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
return 0;
}
这其实从编译角度的展开归约也可以理解啊。
以上代码第一种情况,当预编译器看到DIRECT_LITERAL后查到它是宏定义,定义为#a,此时后面的参数部分就会以#a的形式生成到源文件中。也就是说,预编译后的源文件中,替代第一条语句的就是:
puts("INDIRECT_LITERAL(a + b)");输出则是INDIRECT_LITERAL(a + b)
而对于第二条语句,当预编译器看到INDIRECT_LITERAL后查到它是宏定义,定义为DIRECT_LITERAL(a),这时先把它作为DIRECT_LITERAL(DIRECT_LITERAL(a + b))的形式暂存起来,你也可以理解为这个状态是语法树的当中一个叶结点。然后再分析后面的DIRECT_LITERAL后面的参数部分,即:DIRECT_LITERAL(a + b),同样,预编译器会将它归约为"a + b"的形式。这样对于里面的DIRECT_LITERAL(a + b)的形式就完全确定下来了,那么这个值就可以充当叶子结点,即它底下不会再有结点。然后再回到刚才那个状态,DIRECT_LITERAL("a + b")最后就是"/"a + b/""。所以这里输出是"a + b"。
值得注意的是#a是将参数a转为字符串形式。所以像DIRECT_LITERAL(a)的展开形式是字符串常量"a"
那么DIRECT_LITERAL("a")展开就是"/"a/""。
#include <stdio.h>
#define DIRECT_LITERAL(a) #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
#define DIRECT_CAT(a, b) a##b
#define INDIRECT_CAT(a, b) DIRECT_CAT(a, b)
int main(void)
{
puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_CAT(INDIRECT_CAT(a, b), INDIRECT_CAT(c, d))));
puts(INDIRECT_LITERAL(INDIRECT_CAT(DIRECT_CAT(a, b), DIRECT_CAT(c, d))));
return 0;
}
本人后记:含义并不复杂,就是有点绕,为避免一知半解,手动输入上面那段代码编译生成了一下,看看结果和自己的理解是否符合,经过验证对比,明白多了。另外,调试期间发现Windbg在调试控制台程序时比VS有优势,可以直接打开.exe调试并直接看到运行结果,算是一个意外收获喔
没想到写技术日志也挺费时间的,下次争取贴上程序截图,以求更加直观,呵呵
第一个是const char[1]不能转化为BSTR,第二个是const char[1]不能转化为OLECHAR *类型
根据提示所标明的错误行,找到第二个错误产生的地方:
SysAllocString(XSTR(""));
幸好之前啃过库里的若干代码,记得这个XSTR的出处,于是打开apglobal.h,查到的XSTR在非MAC系统下的定义为
#define XSTR(x) L##x
L符号我知道,这两个##是什么东西,起什么作用呢?谷歌一番,找到这篇说明:
(刚好作者说从CSDN摘下来的,算是取之于彼用之于彼,哈哈)
#define f(a,b) a##b
#define d(a) #a
#define s(a) d(a)
void main( void )
{
puts(d(f(a,b)));
puts(s(f(a,b)));
}
输出结果:
f(a,b)
ab
分析: ##把两个符号连起来
#a指把a当成符号,就是把#后面的看成字符串
# 和 ## 操作符是和#define宏使用的. 使用# 使在#后的首个参数返回为一个带引号的字符串. 例如, 命令
#define to_string( s ) # s
将会使编译器把以下命令
cout < < to_string( Hello World! ) < < endl;
理解为
cout < < "Hello World!" < < endl;
使用##连结##前后的内容. 例如, 命令
#define concatenate( x, y ) x ## y
...
int xy = 10;
...
将会使编译器把
cout < < concatenate( x, y ) < < endl;
解释为
cout < < xy < < endl;
理所当然,将会在标准输出处显示'10'.
puts(d(f(a,b))); ----> 因为d宏中的参数是另外一个宏,且带##,所以作为参数的宏不展开,相当于
puts(#f(a,b));----->puts("f(a,b)");
puts(s(f(a,b))); ----> 因为s宏中的参数是另外一个宏,但不带##,所以作为参数的宏先展开,相当于
puts(s(ab));----->puts(d(ab));---->puts(#ab);---->puts("ab");
#define f(a,b) a##b
#define d(a) #a --》 以"#"开头的,直接替换,不展开:immediately replaced by the unexpanded actual argument
#define s(a) d(a) --》 非以"#"开头的,先展开,再替换,也就是一般的情况
所以就两种情况:
1,不以"#"开头的,先展开参数a,然后是替换代码:puts(s(f(a,b)));-->puts(s(ab))-->puts(d(ab))-->puts("ab")
2,以"#"开头的,直接替换,不展开:puts(d(f(a,b)))-->puts("f(a,b)")
#include <stdio.h>
#define DIRECT_LITERAL(a) #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
int main(void)
{
puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
return 0;
}
这其实从编译角度的展开归约也可以理解啊。
以上代码第一种情况,当预编译器看到DIRECT_LITERAL后查到它是宏定义,定义为#a,此时后面的参数部分就会以#a的形式生成到源文件中。也就是说,预编译后的源文件中,替代第一条语句的就是:
puts("INDIRECT_LITERAL(a + b)");输出则是INDIRECT_LITERAL(a + b)
而对于第二条语句,当预编译器看到INDIRECT_LITERAL后查到它是宏定义,定义为DIRECT_LITERAL(a),这时先把它作为DIRECT_LITERAL(DIRECT_LITERAL(a + b))的形式暂存起来,你也可以理解为这个状态是语法树的当中一个叶结点。然后再分析后面的DIRECT_LITERAL后面的参数部分,即:DIRECT_LITERAL(a + b),同样,预编译器会将它归约为"a + b"的形式。这样对于里面的DIRECT_LITERAL(a + b)的形式就完全确定下来了,那么这个值就可以充当叶子结点,即它底下不会再有结点。然后再回到刚才那个状态,DIRECT_LITERAL("a + b")最后就是"/"a + b/""。所以这里输出是"a + b"。
值得注意的是#a是将参数a转为字符串形式。所以像DIRECT_LITERAL(a)的展开形式是字符串常量"a"
那么DIRECT_LITERAL("a")展开就是"/"a/""。
#include <stdio.h>
#define DIRECT_LITERAL(a) #a
#define INDIRECT_LITERAL(a) DIRECT_LITERAL(a)
#define DIRECT_CAT(a, b) a##b
#define INDIRECT_CAT(a, b) DIRECT_CAT(a, b)
int main(void)
{
puts(DIRECT_LITERAL(INDIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_LITERAL(a + b)));
puts(INDIRECT_LITERAL(DIRECT_CAT(INDIRECT_CAT(a, b), INDIRECT_CAT(c, d))));
puts(INDIRECT_LITERAL(INDIRECT_CAT(DIRECT_CAT(a, b), DIRECT_CAT(c, d))));
return 0;
}
本人后记:含义并不复杂,就是有点绕,为避免一知半解,手动输入上面那段代码编译生成了一下,看看结果和自己的理解是否符合,经过验证对比,明白多了。另外,调试期间发现Windbg在调试控制台程序时比VS有优势,可以直接打开.exe调试并直接看到运行结果,算是一个意外收获喔
没想到写技术日志也挺费时间的,下次争取贴上程序截图,以求更加直观,呵呵
相关文章推荐
- Android 中 &quot;@+id&quot;的作用是什么?以及其与android:id的区别是什么?
- 论#import和#include 以及#import "stdio.h"和#import<stdio.h>的区别
- 与'>以及DataBinder.Eval(Container, DataItem,"id")的区别
- 总结:FORM中使用onSubmit="return false"防止表单自动提交,以及submit和button提交表单的区别
- JAVA中isEmpty和null以及""的区别
- null,""以及" "的区别
- js中的"=="和equals()以及is()三者的区别
- 创建unique时,约束和索引有何区别。唯一约束和唯一索引区别,选项"忽略重复键"作用
- string.empty,""以及null的区别
- Java中的String str="abc"; String str=new String("abc");和String str = new String();的区别以及==与equals()的不同。
- a href="" 和 href="#" 以及 href="javascript:void(0)"时 以及其中的 onclick 的区别
- JAVA中isEmpty和null以及""的区别
- android 基础 @SuppressLint("NewApi")和@TargetApi()的作用与区别
- Java中String类的isEmpty方法、null以及""的区别
- js中的"=="和equals()以及is()三者的区别
- FORM中使用onSubmit="return false"防止表单自动提交,以及submit和button提交表单的区别
- "Bundle versions string, short" 跟 "Bundle version" 的区别及作用
- JAVA中isEmpty和null以及""的区别
- #include <iostream> 和#include <iostream.h> 以及include " "的区别
- FORM中使用onSubmit="return false"防止表单自动提交,以及submit和button提交表单的区别