预处理命令“ # 运算符”和“ ## 运算符” "#@"运算符

来源:互联网 发布:大学生必读书籍 知乎 编辑:程序博客网 时间:2024/05/29 08:14
预处理还需要运算符?有没有搞错?^_^, 没有搞错,预处理是有运算符,而且还不止一个:
     # (单井号)    —— 字符串化运算符。
     ## (双井号 )—— 连接运算符
     #@                —— 字符化运算符。


接下来我们会分别说明一下他们的用法.

一、字符串化运算符 —— #

用于创建字符串,#运算符后面应该跟一个形参(中间可以有空格或Tab),例如:
    #define STR(s) #s  
    puts(STR(Here is a Demo)); // 相当于puts("Here is a Demo"); 

常用实例,我们在调试代码的时候有时需要打印一些字符串的值,如下:
 #include<stdio.h>  
 #define Dump_Str(s) printf("%s = %s\n",#s,s);     
 int main()
 {
    const char * pchName = "Gui xue";
    Dump_Str(pchName);
 }

二、连接运算符 —— ##

 用于将两个Token连接成一个Token。这里提到一个需要概念Token ,先说明一下什么是Token。

 人与人之间的沟通,通过说话,而每句话便是由单词组合在一起,形成特定的语义。这里的单词便可理解成 Token。

C语言编译器相当于一个翻译,要懂两种语言——C语言和机器语言;它的工作是将C语言翻译成机器语言。首先它应该读懂C语言中的“句子”,而对整个“句子”的理解,是建立在对每个“单词”理解的基础上的,所以首先我们要把句子分成多个单词——分词。

词法分析便是将C语言的“句子”按照词法规则拆分成 Token 序列。例如:
    #define __CONCAT(x,y)   x ## y  
    int  n1 =15;  
    int  n2 =200;  
    __CONCAT(n,1); // n1  
    __CONCAT(n,2); // n2 

常见用法:glib库中
    stdint.h (sysdeps\generic):150:    #define __INT64_C(c)    c ## L
    stdint.h (sysdeps\generic):151:    #define __UINT64_C(c)    c ## UL
将某个常量后自动加上 L或UL,达到数据类型强制转换的目的。

个人总结:
1、##最好定义成类似:
    #define STR_LINK2(A,B)  A##""##B
这样A和B可以分别是另外的宏,否则编译会出错,说AB没定义。

2、改预处理不支持嵌套。

 
 

三、字符化运算符—— #@ 

用于创建一个字符,类似 ## ,注: 非 ANSI-C中的特性,GCC不支持,VC可以; 使用实例如下:
#include<stdio.h>    
#define Dump_Char(c)  #@c
int main()

     printf("%c\n",Dump_Char(g)); //g
     printf("%c\n",Dump_Char(guix)); //x   可以输入 4个长度的字符,但只输出最后一位
     printf("%c\n",Dump_Char(  guix  )); //x  默认去除前后空格,保留中间空格
     printf("%c\n",Dump_Char(guixu));//error C2015: too many characters in constant 

}


1. 利用宏参数创建字符串:# 运算符
 
    在类函数宏(function-like macro)的替换部分中,“#”符号用作一个预处理运算符,它可以把语言符号(token)转化为字符串。例如,如果 x 是一个宏参量,那么 #x 可以把参数名转化为相应的字符串。该过程称为字符串化。
 
    说明:类函数宏就是带参数的宏。类函数宏的定义中,用圆括号括起来一个或多个参数,随后这些参数出现在替换部分。
#include <stdio.h>    
#define PSQR(x) printf("The square of " #x " is %d. /r/n", (x) * (x))    
    
int main(void)   
{   
    int y = 5;    
       
    PSQR(y);   
    PSQR(2 + 4);    
    
    return 0;    
}   
    
// 输出:   
The square of y is 25.     // 用 "y" 代替 #x    
The square of 2 + 4 is 36. // 用 "2 + 4" 代替 #x  
#include <stdio.h> 
#define PSQR(x) printf("The square of " #x " is %d. /r/n", (x) * (x)) 
 
int main(void)
{
    int y = 5; 
    
    PSQR(y);
    PSQR(2 + 4); 
 
    return 0; 
}
 
// 输出:
The square of y is 25.     // 用 "y" 代替 #x 
The square of 2 + 4 is 36. // 用 "2 + 4" 代替 #x
#define STRING2(x) #x   
#define STRING(x) STRING2(x)   
    
#define WQ wangqi    
    
#pragma message(STRING2(WQ)) // WQ(字符串)   
#pragma message(STRING(WQ))  // wangqi(字符串)  
#define STRING2(x) #x
#define STRING(x) STRING2(x)
 
#define WQ wangqi 
 
#pragma message(STRING2(WQ)) // WQ(字符串)
#pragma message(STRING(WQ))  // wangqi(字符串) 
2. 预处理器的粘合剂:## 运算符
 
    和 # 运算符一样,## 运算符可以用于类函数宏的替换部分。另外,## 运算符还可用于类对象宏(object-like macro)的替换部分。这个运算符把两个语言符号组合成单个语言符号。例如,可以定义如下宏:
#define XNAME(n) x ## n  
#define XNAME(n) x ## n
    宏调用 XNAME(4) 会展开成 x4 。
 
    说明:类对象宏就是用来代表值的宏。如,#define PI 3.141593 中的PI。
#include <stdio.h>    
#define XNAME(n) x ## n    
#define PRINT_XN(n) printf("x" #n " = %d/r/n", x ## n);    
    
int main(void)   
{   
    int XNAME(1) = 14; // 变为 int x1 = 14;    
    int XNAME(2) = 20; // 变为 int x2 = 20;    
    PRINT_XN(1)        // 变为 printf("x1 = %d/r/n", x1);    
    PRINT_XN(2)        // 变为 printf("x2 = %d/r/n", x2);    
    
    return 0;    
}   
    
// 输出:   
x1 = 14    
x2 = 20   
#include <stdio.h> 
#define XNAME(n) x ## n 
#define PRINT_XN(n) printf("x" #n " = %d/r/n", x ## n); 
 
int main(void)
{
    int XNAME(1) = 14; // 变为 int x1 = 14; 
    int XNAME(2) = 20; // 变为 int x2 = 20; 
    PRINT_XN(1)        // 变为 printf("x1 = %d/r/n", x1); 
    PRINT_XN(2)        // 变为 printf("x2 = %d/r/n", x2); 
 
    return 0; 
}
 
// 输出:
x1 = 14 
x2 = 20 
#define __T(x)      L ## x   
#define _T(x)       __T(x)   
#define _TEXT(x)    __T(x)   
        
#define WQ "wangqi"    
        
#pragma message(__T(WQ)) // LWQ (标识符)   
wcout << _T(WQ);         // wangqi(宽字节字符串)  
#define __T(x)      L ## x
#define _T(x)       __T(x)
#define _TEXT(x)    __T(x)
     
#define WQ "wangqi" 
     
#pragma message(__T(WQ)) // LWQ (标识符)
wcout << _T(WQ);         // wangqi(宽字节字符串) 
3. 语言符号 
 
    从技术方面看,系统把宏的主体当作语言符号(token)类型字符串,而不是字符型字符串。C 预处理器中的语言符号是宏定义主体中的单独的“词(word)”。用空白字符把这些词分开。例如:
#define FOUR 2*2  
#define FOUR 2*2
    这个定义中有一个语言符号:即序列 2*2 。但是:
#define SIX 2 * 3  
#define SIX 2 * 3
    这个定义中有三个语言符号:2、* 和 3 。
 
    在处理主体中的多个空格时,字符型字符串和语言符号型字符串采用不同方法。考虑下面的定义:
#define EIGHT 4    *    8  
#define EIGHT 4    *    8
    把主体解释为字符型字符串时,预处理器用 4    *    8 替换 EIGHT 。也就是说,额外的空格也当作替换文本的一部分。但是,当把主体解释为语言符号类型时,预处理器用由单个空格分隔的三个语言符号,即 4 * 8 来替换 EIGHT 。 换句话说,用字符型字符串的观点看,空格也是主体的一部分;而用语言符号字符串的观点看,空格只是分隔主体中语言符号的符号。在实际应用中,有些 C 编译器把宏主体当作字符串而非语言符号。在比这个实例更复杂的情况下,字符与语言符号之间的差异才有实际意义。
 
    顺便提一下,C 编译器处理语言符号的方式比预处理器的处理方式更加复杂。编译器能理解 C 的规则,不需要用空格来分隔语言符号。例如,C 编译器把 2*2 当作三个语言符号。原因是 C 编译器认为每个 2 都是一个常量,而 * 是一个运算符。
 
    摘自:《C Primer Plus(第五版)中文版》第16章 C预处理器和C 

   转载自:http://blog.csdn.net/g5dsk/archive/2010/01/12/5179484.aspx

0 0
原创粉丝点击