1。 宏是一种替换,是C预处理器在编译前完成的。
2。 宏文本替换和typedef之间的区别:
比如typedef int x[10]和#define x int[10]之间的区别
(1) 可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做。
eg,
#define peach int
unsigned peach i; /* No problem, ^_^ */
(2) 连续声明几个变量时,用typedef定义的类型能够保证声明中所有的变量均为同一类型,而用#define定义的类型则无法保证。
eg,
#define int_ptr int *
int_ptr apple, cheese;
宏扩展后,变为: int * apple, cheese;
使得apple和Cheese成为不同的类型,一个是int型指针,另一个是int型
[ Reference from <<C Expert Programming>> page 68 - 69 ]
将宏用于类型定义这种用法有一个优点 -- 可移植性, 得到所有C编译器的支持。
3。 带参宏与函数,类似但本质不同。就速度而言,宏少了对函数进行调用的开销,会快一点,但因为宏只是进行文本替换,所以代码在频繁使用的地方,
使用宏,code size会增大。所以,写代码时,应该根据系统环境,如运行速度,memory size等的不同,在速度和code size之间找到一个平衡点。
4。 最好在宏定义中把每一个参数都用括号括起来,整个结果表达式也应该用括号括起来,以防止当宏用于一个更大一些的表达式中可能出现的问题。
eg,
#define abs(x) x>0?x:-x
在表达式 abs(a-b)中, 会被展开为 a-b>0?a-b:-a-b
上式无疑会得到一个错误的结果。
正确定义应该这样:
#define abs(x) (((x)>=0)?(x):-(x))
5。 避免使用有副作用的参数作为带参宏的参数,比如++, --,如果++i这个参数在宏中被求值两次
eg,
#define max(x,y) ((x)>(y)?(x):(y))
...
biggest = max(biggest, x[i++]);
展开后为:biggest = ((biggest)>(x[i++])?(biggest):(x[i++]))
如果biggest = 2, x[0] = 2, x[1] = 3, x[2] = 1, i = 1
那么:
i++将被求值两次,比较后i递增为2。如果i是一个函数的counter,
eg,
while (i < n)
biggest = max(biggest, x[i++]);
将导致错误。
在这时使用函数,代码就会work well了。
6。 宏不是语句
#define assert(e) if (!e) assert_error(__FILE__, __LINE__)
__FILE__和__LINE__是C预处理器中的宏,会被扩展为所在文件的文件名和所处代码行的行号。
如果这样用:
if (x > 0 && y > 0)
assert(x > y);
else
assert(y > x);
展开后为:
if (x > 0 && y > 0)
if (!(x > y)) assert_error("foo.c", 12);
else
if (!(y > x)) assert_error("foo.c", 16);
缩排一下:
if (x > 0 && y > 0)
if (!(x > y))
assert_error("foo.c", 12);
else
if (!(y > x))
assert_error("foo.c", 16);
[Reference from <<C Traps and Pitfalls>> page 101 - 102]
2。 宏文本替换和typedef之间的区别:
比如typedef int x[10]和#define x int[10]之间的区别
(1) 可以用其他类型说明符对宏类型名进行扩展,但对typedef所定义的类型名却不能这样做。
eg,
#define peach int
unsigned peach i; /* No problem, ^_^ */
typedef int banana;
unsigned banana i; /* Error, Invalid, :-) */
unsigned banana i; /* Error, Invalid, :-) */
(2) 连续声明几个变量时,用typedef定义的类型能够保证声明中所有的变量均为同一类型,而用#define定义的类型则无法保证。
eg,
#define int_ptr int *
int_ptr apple, cheese;
宏扩展后,变为: int * apple, cheese;
使得apple和Cheese成为不同的类型,一个是int型指针,另一个是int型
[ Reference from <<C Expert Programming>> page 68 - 69 ]
将宏用于类型定义这种用法有一个优点 -- 可移植性, 得到所有C编译器的支持。
3。 带参宏与函数,类似但本质不同。就速度而言,宏少了对函数进行调用的开销,会快一点,但因为宏只是进行文本替换,所以代码在频繁使用的地方,
使用宏,code size会增大。所以,写代码时,应该根据系统环境,如运行速度,memory size等的不同,在速度和code size之间找到一个平衡点。
4。 最好在宏定义中把每一个参数都用括号括起来,整个结果表达式也应该用括号括起来,以防止当宏用于一个更大一些的表达式中可能出现的问题。
eg,
#define abs(x) x>0?x:-x
在表达式 abs(a-b)中, 会被展开为 a-b>0?a-b:-a-b
上式无疑会得到一个错误的结果。
正确定义应该这样:
#define abs(x) (((x)>=0)?(x):-(x))
5。 避免使用有副作用的参数作为带参宏的参数,比如++, --,如果++i这个参数在宏中被求值两次
eg,
#define max(x,y) ((x)>(y)?(x):(y))
...
biggest = max(biggest, x[i++]);
展开后为:biggest = ((biggest)>(x[i++])?(biggest):(x[i++]))
如果biggest = 2, x[0] = 2, x[1] = 3, x[2] = 1, i = 1
那么:
i++将被求值两次,比较后i递增为2。如果i是一个函数的counter,
eg,
while (i < n)
biggest = max(biggest, x[i++]);
将导致错误。
在这时使用函数,代码就会work well了。
6。 宏不是语句
#define assert(e) if (!e) assert_error(__FILE__, __LINE__)
__FILE__和__LINE__是C预处理器中的宏,会被扩展为所在文件的文件名和所处代码行的行号。
如果这样用:
if (x > 0 && y > 0)
assert(x > y);
else
assert(y > x);
展开后为:
if (x > 0 && y > 0)
if (!(x > y)) assert_error("foo.c", 12);
else
if (!(y > x)) assert_error("foo.c", 16);
缩排一下:
if (x > 0 && y > 0)
if (!(x > y))
assert_error("foo.c", 12);
else
if (!(y > x))
assert_error("foo.c", 16);
[Reference from <<C Traps and Pitfalls>> page 101 - 102]
明显不是所希望的结果。