c 语言宏的妙用(用法技巧)

Preprocessor Operators

Four preprocessor-specific operators are used in the context of the #define directive (see the following list for a summary of each). The stringizing, charizing, and token-pasting operators are discussed in the next three sections. For information on the defined operator, see The #if, #elif, #else, and #endif Directives.

Operator Action
Stringizing operator (#) Causes the corresponding actual argument to be enclosed in double quotation marks
Charizing operator (#@) Causes the corresponding argument to be enclosed in single quotation marks and to be treated as a character (Microsoft Specific)
Token-pasting operator (##) Allows tokens used as actual arguments to be concatenated to form other tokens
defined operator Simplifies the writing of compound expressions in certain macro directives
C/C++ Preprocessor Reference  

Stringizing Operator (#)

The number-sign or "stringizing" operator (#) converts macro parameters (after expansion) to string constants. It is used only with macros that take arguments. If it precedes a formal parameter in the macro definition, the actual argument passed by the macro invocation is enclosed in quotation marks and treated as a string literal. The string literal then replaces each occurrence of a combination of the stringizing operator and formal parameter within the macro definition.

White space preceding the first token of the actual argument and following the last token of the actual argument is ignored. Any white space between the tokens in the actual argument is reduced to a single white space in the resulting string literal. Thus, if a comment occurs between two tokens in the actual argument, it is reduced to a single white space. The resulting string literal is automatically concatenated with any adjacent string literals from which it is separated only by white space.

Further, if a character contained in the argument usually requires an escape sequence when used in a string literal (for example, the quotation mark (") or backslash (\) character), the necessary escape backslash is automatically inserted before the character. The following example shows a macro definition that includes the stringizing operator and a main function that invokes the macro:

#define stringer( x ) printf( #x "\n" ) int main() { stringer( In quotes in the printf function call\n ); stringer( "In quotes when printed to the screen"\n ); stringer( "This: \" prints an escaped double quote" ); }

Such invocations would be expanded during preprocessing, producing the following code:

int main() { printf( "In quotes in the printf function call\n" "\n" ); printf( "\"In quotes when printed to the screen\"\n" "\n" ); printf( "\"This: \\\" prints an escaped double quote\"" "\n" ); }

When the program is run, screen output for each line is as follows:

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

Microsoft Specific

The Microsoft C (versions 6.0 and earlier) extension to the ANSI C standard that previously expanded macro formal arguments appearing inside string literals and character constants is no longer supported. Code that relied on this extension should be rewritten using the stringizing (#) operator.

END Microsoft Specific

 

C/C++ Preprocessor Reference  

Charizing Operator (#@)

Microsoft Specific

The charizing operator can be used only with arguments of macros. If #@ precedes a formal parameter in the definition of the macro, the actual argument is enclosed in single quotation marks and treated as a character when the macro is expanded. For example:

#define makechar(x) #@x

causes the statement

a = makechar(b);

to be expanded to

a = 'b';

The single-quotation character cannot be used with the charizing operator.

END Microsoft Specific

C/C++ Preprocessor Reference  

Token-Pasting Operator (##)

The double-number-sign or "token-pasting" operator (##), which is sometimes called the "merging" operator, is used in both object-like and function-like macros. It permits separate tokens to be joined into a single token and therefore cannot be the first or last token in the macro definition.

If a formal parameter in a macro definition is preceded or followed by the token-pasting operator, the formal parameter is immediately replaced by the unexpanded actual argument. Macro expansion is not performed on the argument prior to replacement.

Then, each occurrence of the token-pasting operator in token-string is removed, and the tokens preceding and following it are concatenated. The resulting token must be a valid token. If it is, the token is scanned for possible replacement if it represents a macro name. The identifier represents the name by which the concatenated tokens will be known in the program before replacement. Each token represents a token defined elsewhere, either within the program or on the compiler command line. White space preceding or following the operator is optional.

This example illustrates use of both the stringizing and token-pasting operators in specifying program output:

#define paster( n ) printf( "token" #n " = %d", token##n ) int token9 = 9;

If a macro is called with a numeric argument like

paster( 9 );

the macro yields

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

which becomes

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

 宏中"#"和"##"的用法 
一:一般用法 
#:把宏参数变为一个字符串

##:把两个宏参数贴合在一起
用法: 
#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");  /* 输出为“int max: INT_MAX” %s的存在使得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];

 

 

转自:http://huanghl97.blog.163.com/blog/static/5938886020125591740883/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值