預處理命令“ # 運算符”和“ ## 運算符” "#@"運算符

預處理還需要運算符?有沒有搞錯?^_^, 沒有搞錯,預處理是有運算符,而且還不止一個:
     # (單井號)    —— 字符串化運算符。
     ## (雙井號 )—— 連接運算符
     #@                —— 字符化運算符。


接下來我們會分別說明一下他們的用法.

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

用於創建字符串,#運算符後面應該跟一個形參(中間可以有空格或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


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章