define PLAYSOUNDEFFECT(...) [[GameManager sharedGameManager] playSoundEffect:@#VA_ARGS]
这样的代码你能看懂吗?
看懂了你就是高手了 哈哈
(转载 http://blog.csdn.net/songrotek/article/details/8929963)
1 关于宏的定义
A macro
is a fragment of code which has been given a name. Whenever the name is used, it is replaced by the contents of the macro. There are two kinds of macros. They differ mostly in what they look like when they are used. Object-like
macros resemble data objects when used, function-like
macros resemble function calls.
有两种宏的类型,一种是类对象的宏,封装使用的数据对象,另一种是类函数的宏,封装函数的调用。在ObjC里面,那就是可以封装Method的使用,如文章一开始的代码
1.1 类对象的宏
最基本的使用:
define BUFFER_SIZE 1024
foo = (char *) malloc (BUFFER_SIZE);
foo = (char *) malloc (1024);
就是最基本的替换。
通常宏的名称都是用大写字母。
define NUMBERS 1, \
2, \
3
int x[] = { NUMBERS };
==> int x[] = { 1, 2, 3 };
在宏定义中,如果要换行,使用“"符号。然后经预处理后还是在同一行。
C预处理器是按顺序读取程序,因此宏定义生效在宏定义之后。
foo = X;
define X 4
bar = X;
ces
foo = X;
bar = 4;
宏调用时,预处理器在替换宏的内容时,会继续检测内容本身是否也是宏定义,如果是,会继续替换内容。
define TABLESIZE BUFSIZE
define BUFSIZE 1024
TABLESIZE
==> BUFSIZE
==> 1024
宏定义以最后生效的定义为准,因此下面的代码TABLESIZE对应37
define BUFSIZE 1020
define TABLESIZE BUFSIZE
undef BUFSIZE
define BUFSIZE 37
如果宏定义内容包含了名称,则预处理器会终止展开防止无限嵌套(infinite resursion)
1.2 类函数宏
define lang_init() c_init()
lang_init()
==> c_init()
类函数宏的名称后面加了"()"。
define lang_init () c_init()
lang_init()
==> () c_init()()
并且"()"必须紧随在名称后面否则就会认为是类对象宏。
1.3 宏参数
在类函数宏里面可以添加参数使得更像真正的函数
#define min(X, Y) ((X) < (Y) ? (X) : (Y))
x = min(a, b); ==> x = ((a) < (b) ? (a) : (b));
y = min(1, 2); ==> y = ((1) < (2) ? (1) : (2));
z = min(a + 28, p); ==> z = ((a + 28) < (p) ? (a + 28) : (*p));
基本的使用和函数的定义类似,当然宏里面都是实际参数,用逗号隔开。预处理时,先是将宏展开,然后将参数放进宏的主体中,再检查一遍完整的内容。
如何宏里面有字符串的内容,即使与参数名相同,也不会被替换。如下:
foo(bar) ==> bar, "x"
1.4 字符串化
使用”#“预处理操作符来实现将宏中的参数转化为字符串。例子如下:
#define WARN_IF(EXP) \
do { if (EXP) \
fprintf (stderr, "Warning: " #EXP "\n"); } \
while (0)
WARN_IF (x == 0);
==> do { if (x == 0)
fprintf (stderr, "Warning: " "x == 0" "\n"); } while (0);
这个字符串化会将参数中的所有字符都实现字符串化,包括引号。如果参数中间有很多空格,字符串化之后将会只用一个空格代替。
然后没有什么方法可以直接将参数转化成单一的字符char
define xstr(s) str(s)
define str(s) #s
define foo 4
str (foo)
==> "foo"
xstr (foo)
==> xstr (4)
==> str (4)
==> "4"
出现上面的结果是因为在使用str(s)时,s是字符串化,所以宏没有扩展开。而使用xstr(s)时s作为一个参数,因此先把宏完全扩展然后再放进参数。
1.5 连接
使用"##"操作符可以实现宏中token的连接。
struct command
{
char name;
void (function) (void);
};
struct command commands[] =
{
{ "quit", quit_command },
{ "help", help_command },
...
};
define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
...
};
如上,使参数NAME对应的字符与_command连接起来,而不进行其他转化。当然要注意连接后的字符必须是有意义的,否则只会出现错误或警告。然后C预处理器会将注释转化成空格,因此在宏中间,参数中间加入注释都是可以的。但不能将"##"放在宏的最后,否则会出现错误。1.6 多参数宏(Variadic Macros)
define eprintf(...) fprintf (stderr, VA_ARGS)
eprintf ("%s:%d: ", input_file, lineno)
==> fprintf (stderr, "%s:%d: ", input_file, lineno)
使用标识符_VA_ARGS来表示多个参数,在宏的名称中则使用(...)在C++中也可以使用如下的方式:
define eprintf(args...) fprintf (stderr, args)
结果是一样的。------------------------------------------------------------------------------------------------"##"的特殊用法:
#define eprintf(format, ...) fprintf (stderr, format, ##VA_ARGS)
eprintf ("success!\n")
==> fprintf(stderr, "success!\n");
将"##"放在","和参数之间,那么如果参数留空的话,那么"##"前面的","就会删掉,从而防止编译错误。1.7 取消或重新宏定义这个看下面的代码就明白:
#define FOO 4
x = FOO;
==> x = 4;
#undef FOO
x = FOO; ==> x = FOO;
These definitions are effectively the same:
#define FOUR (2 + 2)
#define FOUR (2 + 2)
#define FOUR (2 /* two */ + 2)
but these are not:
#define FOUR (2 + 2)
#define FOUR ( 2+2 )
#define FOUR (2 * 2)
#define FOUR(score,and,seven,years,ago) (2 + 2)
对于重定义,如果定义的宏不一样,那么编译器会给出警告并使用最新定义的宏。