1. 程式人生 > >c語言中#和##的用法

c語言中#和##的用法

報錯 用法 == 預處理器 tor () name 文件 就是

一、一般用法
我們使用#把宏參數變為一個字符串,用##把兩個宏參數貼合在一起.
用法:
#include<cstdio>
#include<climits>
using namespace std;

#define STR(s) #s
#define CONS(a,b) int(a##e##b)

int main()
{
printf(STR(vck)); // 輸出字符串"vck"
printf("%d\n", CONS(2,3)); // 2e3 輸出:2000
return 0;
}

二、當宏參數是另一個宏的時候
需要註意的是凡宏定義裏有用‘#‘或‘##‘的地方宏參數是不會再展開.

1, 非‘#‘和‘##‘的情況
#define TOW (2)
#define MUL(a,b) (a*b)

printf("%d*%d=%d\n", TOW, TOW, MUL(TOW,TOW));
這行的宏會被展開為:
printf("%d*%d=%d\n", (2), (2), ((2)*(2)));
MUL裏的參數TOW會被展開為(2).

2, 當有‘#‘或‘##‘的時候
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)

printf("int max: %s\n", STR(INT_MAX)); // INT_MAX #include<climits>
這行會被展開為:
printf("int max: %s\n", "INT_MAX");

printf("%s\n", CONS(A, A)); // compile error
這一行則是:
printf("%s\n", int(AeA));

INT_MAX和A都不會再被展開, 然而解決這個問題的方法很簡單. 加多一層中間轉換宏.
加這層宏的用意是把所有宏的參數在這層裏全部展開, 那麽在轉換宏裏的那一個宏(_STR)就能得到正確的宏參數.

#define A (2)
#define _STR(s) #s
#define STR(s) _STR(s) // 轉換宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 轉換宏

printf("int max: %s\n", STR(INT_MAX)); // INT_MAX,int型的最大值,為一個變量 #include<climits>
輸出為: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然後再轉換成字符串;

printf("%d\n", CONS(A, A));
輸出為:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))

三、‘#‘和‘##‘的一些應用特例
1、合並匿名變量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示該行行號;
第一層:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二層: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三層: --> static int _anonymous70;
即每次只能解開當前層的宏,所以__LINE__在第二層才能被解開;

2、填充結構
#define FILL(a) {a, #a}

enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;

MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相當於:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};

3、記錄文件名


#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);

4、得到一個數值類型所對應的字符串緩沖大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
這裏相當於:
char buf[11];

【alps_008】:
基本看了一遍,樓主的情況屬於一般用法:

“#把宏參數變為一個字符串,用##把兩個宏參數貼合在一起”



#include<stdio.h>
#include<string.h>
#define STRCPY(a,b) strcpy(a##_p,#b) //把第一個參數後邊加上字符_p,把第二個參數變成字符串

int main()
{
  char var1_p[20];
  char var2_p[30];
  strcpy(var1_p,"aaaa");
  strcpy(var2_p,"bbbb");
STRCPY(var1,var2); //等於strcpy(var1_p,"var2");
  STRCPY(var2,var1); //等於strcpy(var2_p,"var1");
  printf("%s\n",var1_p);
  printf("%s\n",var2_p);
  return 0;
}

【jeffer007】:
Token-Pasting Operator (##)


// preprocessor_token_pasting.cpp
#include <stdio.h>
#define paster( n ) printf_s( "token" #n " = %d", token##n )
int token9 = 9;

int main()
{
paster(9);
}

Output
token9 = 9

Stringizing Operator (#)
// stringizer.cpp
#include <stdio.h>
#define stringer( x ) printf( #x "\n" )
int main() {
stringer( In quotes in the printf function call );
stringer( "In quotes when printed to the screen" );
stringer( "This: \" prints an escaped double quote" );
}

Output
In quotes in the printf function call
"In quotes when printed to the screen"
"This: \" prints an escaped double quote"

5 先看下面三條語句:

#define Conn(x,y) x##y
#define ToChar(x) #@x
#define ToString(x) #x
  • 1
  • 2
  • 3

(1). ## 連接操作符

##表示連接(token pasting, or token concatenation,merge two tokens into one while expanding macros)。x##y表示什麽?表示x連接y,舉例說:

int n = Conn(123,456);
     ==> int n=123456;
char* str = Conn("asdf", "adf");
     ==> char* str = "asdfadf";
  • 1
  • 2
  • 3
  • 4

怎麽樣,很神奇吧!

需要註意的是,##的左右符號必須能夠組成一個有意義的符號,否則預處理器會報錯。

(2). #@ 字符化操作符

#@x只能用於有傳入參數的宏定義中,且必須置於宏定義體中的參數名前。作用是將傳的單字符參數名轉換成字符,以一對單引用括起來其實就是給x加上單引號,結果返回是一個const char
舉例說:

char a = ToChar(1);
     ==> char a=‘1‘;
  • 1
  • 2

做個越界試驗

char a = ToChar(123);
     ==> char a=‘3‘;
  • 1
  • 2

但是如果你的參數超過四個字符,編譯器就給給你報錯了!error C2015: too many characters in constant :P

(3). # 字符串化操作符

#表示字符串化操作符(stringification)。其作用是:將宏定義中的傳入參數名轉換成用一對雙引號括起來參數名字符串。其只能用於有傳入參數的宏定義中,且必須置於宏定義體中的參數名前。說白了,他是給x加雙引號:

char* str = ToString(123132);
     ==> char* str="123132";
  • 1
  • 2

如果你想要對展開後的宏參數進行字符串化,則需要使用兩層宏。

#define xstr(s) str(s)
#define str(s) #s
#define foo 4
str (foo)
     ==> "foo"
xstr (foo)
     ==> xstr (4)
     ==> str (4)
     ==> "4"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

s參數在str宏中被字符串化,所以它不是優先被宏展開。然而s參數是xstr宏的一個普通參數,在被傳遞到str宏之前已經被宏展開。

(4). \ 行繼續操作

\ 行繼續操作當定義的宏不能用一行表達完整時,可以用”\”(反斜線)表示下一行繼續此宏的定義。

註意:最後一行不要加續行符啊.

VC的預處理器在編譯之前會自動將\與換行回車去掉(寫成多行時,反斜杠後不能有空格,否則編譯器(ARM或VC)會報錯!),這樣一來既不影響閱讀,又不影響邏輯,皆大歡喜.

(5). __VA_ARGS__

__VA_ARGS__宏用來接受不定數量的參數。例如:

#define eprintf(...) fprintf (stderr, __VA_ARGS__)

eprintf ("%s:%d: ", input_file, lineno)
    ==>  fprintf (stderr, "%s:%d: ", input_file, lineno)
  • 1
  • 2
  • 3
  • 4
  • 5

當__VA_ARGS__宏前面##時,可以省略參數輸入。例如:

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

eprintf ("success!\n")
    ==> fprintf(stderr, "success!\n");

c語言中#和##的用法