c 中的 # #@ ##

source:http://www.cnblogs.com/morewindows/archive/2011/08/18/2144112.html

文中__FILE__与示例1可以参见《使用ANSI C and Microsoft C++中常用的预定义宏》

宏中的#的功能是将其后面的宏参数进行字符串化操作(Stringizing operator),简单说就是在它引用的宏变量的左右各加上一个双引号。

如定义好#define STRING(x#x之后,下面二条语句就等价。

       char *pChar = "hello";

       char *pChar = STRING(hello);

还有一个#@是加单引号(Charizing Operator

#define makechar(x)  #@x

       char ch = makechar(b);与char ch = 'b';等价。

 

但有小问题要注意,宏中遇到###时就不会再展开宏中嵌套的宏了。什么意思了?比如使用char *pChar =STRING(__FILE__);虽然__FILE__本身也是一个宏,但编译器不会展开它,所以pChar将指向"__FILE__"而不是你要想的形如"D:\XXX.cpp"的源文件名称。因此要加一个中间转换宏,先将__FILE__解析成"D:\XXX.cpp"字符串。

定义如下所示二个宏:

#define _STRING(x#x

#define STRING(x_STRING(x)

再调用下面语句将输出带""的源文件路径

       char* pChar = STRING(__FILE__);

       printf("%s %s\n"pChar, __FILE__);

可以比较下STRING(__FILE__)__FILE__的不同,前将带双引号,后一个没有双引号。

 

再讲下##的功能,它可以拼接符号(Token-pasting operator)。

MSDN上有个例子:

#define pastern ) printf"token"#n" = %d\n", token##n )

int token9 = 100;

再调用  paster(9);宏展开后token##n直接合并变成了token9。整个语句变成了

printf"token""9"" = %d", token9 );

C语言中字符串中的二个相连的双引号会被自动忽略,于是上句等同于

printf("token9 = %d", token9);。

即输出token9 = 100

 

 

有了上面的基础后再来看示例1

#define WIDEN2(x) L ## x

#define WIDEN(xWIDEN2(x)

#define __WFILE__ WIDEN(__FILE__)

wchar_t *pwsz = __WFILE__;

第一个宏中的L是将ANSI字符串转化成unicode字符串。如:wchar_t *pStr = L"hello";

再来看wchar_t *pwsz = __WFILE__;

__WFILE__被首先展开成WIDEN(__FILE__),再展开成WIDEN2("__FILE__表示的字符串"),再拼接成 L"__FILE__表示的字符串L"D:\XXX.cpp从而得到unicode字符串并取字符串地址赋值给pwsz指针。

 

VC_T()TEXT ()也是用的这种技术。

tchar.h头文件中可以找到:

#define _T(x)       __T(x)

#define __T(x)      L ## x

winnt.h头文件中可以找到

#define TEXT(quote) __TEXT(quote)   // r_winnt

#define __TEXT(quote) L##quote      // r_winnt

因此不难理解为什么第三条语句会出错error C2065: 'LszText' : undeclared identifier

       wprintf(TEXT("%s %s\n"), _T("hello"), TEXT("hello"));

       char szText[] = "hello";

       wprintf(TEXT("%s %s\n"), _T(szText), TEXT(szText));

而将"hello"定义成宏后就能正确运行。

#define SZTEXT "hello"

       wprintf(TEXT("%s %s\n"), _T(SZTEXT), TEXT(SZTEXT));

注:由于VC6.0默认是ANSI编码,因此要先设置成unicode编码,在project菜单中选择Setting,再在C/C++标签对话框中的Category中选择Preprocessor。再地Preprocessor definitions编辑框中将_MBCS去掉,加上_UNICODE,UNICODE

 

 

更多内容可以查考MSDN上对Preprocessor Operators的讲解。

在C语言中,预处理指令以`#`字符开头,用于在编译之前对源代码进行处理。其中,宏定义是通过`#define`指令来定义的,它允许程序员为常量、表达式或代码片段指定一个名称,这个名称被称为宏。当预处理器遇到宏时,它会将宏替换为其定义的值或代码,这一过程称为宏展开。 ### 宏定义的基本形式 宏定义的基本语法如下: ```c #define 宏名 替换文本 ``` 这里的“替换文本”可以是一个常量、表达式或者任意合法的C语言代码序列。例如,定义一个表示一年中有多少秒的宏: ```c #define SECONDS_PER_YEAR (60 * 60 * 24 * 365) ``` 在这个例子中,每当程序中出现`SECONDS_PER_YEAR`时,预处理器都会将其替换成`(60 * 60 * 24 * 365)`[^4]。 ### `#n` 形式的用法 实际上,在标准C语言的宏定义中并没有直接使用`#n`这样的形式。不过,我们可以从两个角度来理解这个问题:一是`#`操作符在宏定义中的特殊用途;二是数字字面量与宏定义结合使用的常见实践。 #### `#` 操作符 在宏定义中,`#`是一个特殊的操作符,用于字符串化(stringizing)操作。如果宏定义中的参数前有`#`,那么在宏展开时,该参数会被转换成字符串常量。例如: ```c #define PRINT_INT(x) printf(#x " = %d\n", x) ``` 在这个宏定义中,`#x`意味着`x`的值不会被求值,而是直接作为字符串插入到输出中。调用`PRINT_INT(123);`将会导致以下代码被生成: ```c printf("123" " = %d\n", 123); ``` 这相当于: ```c printf("123 = %d\n", 123); ``` 这样就实现了打印变量名及其值的功能[^1]。 #### 数字字面量与宏定义 虽然没有`#n`这种形式,但在实际编程实践中,我们经常需要定义一些数值型的宏,这些宏通常代表特定的数值常量。比如: ```c #define MAX_SIZE 100 ``` 这里`MAX_SIZE`就是一个数值宏常量,它代表了100这个整数。在程序中使用`MAX_SIZE`的地方,预处理器会自动将其替换为100。 此外,还可以利用宏定义创建更复杂的表达式,如: ```c #define SQUARE(x) ((x) * (x)) ``` 此宏接受一个参数`x`,并返回其平方。注意,在宏定义中使用括号是为了确保正确的运算顺序,避免因操作符优先级而导致错误。 综上所述,尽管C语言预处理指令中不存在直接的`#n`形式,但通过`#`操作符和数值宏定义,可以实现类似的功能和效果。这种机制不仅增强了代码的可读性和可维护性,而且也提供了灵活的方式来控制编译时的行为[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值