设为首页 加入收藏

TOP

C语言宏定义##连接符和#符的使用(二)
2017-07-29 10:23:42 】 浏览:5468
Tags:语言 定义 连接 符和 符的 使用
而它将会被通过替换变成: fprintf(stderr,"Error!/n",); 很明显,这里仍然会产生编译错误(非本例的某些情况下不会产生编译错误)。除了这种方式外,c99和GNU CPP都支持下面的宏定义方式: #define myprintf(templt, ...) fprintf(stderr,templt, ##__VAR_ARGS__) 这时,##这个连接符号充当的作用就是当__VAR_ARGS__为空的时候,消除前面的那个逗号。那么此时的翻译过程如下: myprintf(templt); 被转化为: fprintf(stderr,templt); 这样如果templt合法,将不会产生编译错误。 这里列出了一些宏使用中容易出错的地方,以及合适的使用方式。 错误的嵌套-Misnesting 宏的定义不一定要有完整的、配对的括号,但是为了避免出错并且提高可读性,最好避免这样使用。 由操作符优先级引起的问题-Operator Precedence Problem 由于宏只是简单的替换,宏的参数如果是复合结构,那么通过替换之后可能由于各个参数之间的操作符优先级高于单个参数内部各部分之间相互作用的操作符优先级,如果我们不用括号保护各个宏参数,可能会产生预想不到的情形。比如: #define ceil_div(x, y) (x + y - 1) / y 那么 a = ceil_div( b & c, sizeof(int) ); 将被转化为: a = ( b & c + sizeof(int) - 1) / sizeof(int); // 由于+/-的优先级高于&的优先级,那么上面式子等同于: a = ( b & (c + sizeof(int) - 1)) / sizeof(int); 这显然不是调用者的初衷。为了避免这种情况发生,应当多写几个括号: #define ceil_div(x, y) (((x) + (y) - 1) / (y)) 消除多余的分号-Semicolon Swallowing 通常情况下,为了使函数模样的宏在表面上看起来像一个通常的C语言调用一样,通常情况下我们在宏的后面加上一个分号,比如下面的带参宏: MY_MACRO(x); 但是如果是下面的情况: #define MY_MACRO(x) { /* line 1 */ /* line 2 */ /* line 3 */ } //... if (condition()) MY_MACRO(a); else {...} 这样会由于多出的那个分号产生编译错误。为了避免这种情况出现同时保持MY_MACRO(x);的这种写法,我们需要把宏定义为这种形式: #define MY_MACRO(x) do { /* line 1 */ /* line 2 */ /* line 3 */ } while(0) 这样只要保证总是使用分号,就不会有任何问题。 Duplication of Side Effects 这里的Side Effect是指宏在展开的时候对其参数可能进行多次eva luation(也就是取值),但是如果这个宏参数是一个函数,那么就有可能被调用多次从而达到不一致的结果,甚至会发生更严重的错误。比如: #define min(X,Y) ((X) > (Y) ? (Y) : (X)) //... c = min(a,foo(b)); 这时foo()函数就被调用了两次。为了解决这个潜在的问题,我们应当这样写min(X,Y)这个宏: #define min(X,Y) ({ typeof (X) x_ = (X); typeof (Y) y_ = (Y); (x_ < y_) ? x_ : y_; }) ({...})的作用是将内部的几条语句中最后一条的值返回,它也允许在内部声明变量(因为它通过大括号组成了一个局部Scope)。
宏中的#的功能是将其后面的宏参数进行字符串化操作(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 paster( n ) 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(x) WIDEN2(x)

#define __WFILE__ WIDEN(__FILE__)

wchar_t *pwsz = __WFILE__;

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

再来看wchar_t *pwsz = __WFILE__;

_
首页 上一页 1 2 3 下一页 尾页 2/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇数据结构顺序表的“增删改查”功.. 下一篇PID闭环自动控制直流电机速度

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目