目录
#define 定义标识符:
例如:
#define MAX 100
#define STR "abcdef"
#define INT int
我们可以通过#define去定义一个标识符,去进行文本的替换。
在定义标识符的时候一般不会在末尾加上;容易引发一些问题:
因为#define 机制上实现的是一段文本的替换,如果在定义的时候在末尾加上 ; 的话会被识别为两条语句(原语句和一条空语句)
例如:
#define 定义宏:
#define 机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏或定义宏。
但是本质上还是属于替换掉一段文本,遵循的规则是先替换,而后进行算术运算,例如:
#define ADD(x,y) x+y
int main()
{
int A = 2;
int B = 3;
int C = 4 * ADD(A, B);
printf("%d\n", C);
//这里最后打印出来的是11,而不是20
//遵循先替换后运算的过程,替换后为: 4 * 2 + 3
return 0;
}
所以我们在使用宏的时候尽量多的去添加对应的括号,避免在使用宏时由于参数中 的操作符或邻近操作符之间不可预料的相互作用。
这里的宏设置就应该改为:#define Add(x,y) ((x)+(y))
#define 的替换规则总结:
1. 在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的值所替换。
3. 最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
对于宏的替换,有两个注意的点:
1. 宏参数和#define 定义中可以出现其他#define定义的符号。但是对于宏,不能出现递归。
2. 当预处理器搜索#define定义的符号的时候,字符串常量的内容并不被搜索。
部分会带有副作用的宏参数:
当宏参数在宏的定义中出现超过一次的时候,如果参数带有副作用(自增/自减),那么在使用这个宏的时候就可能会影响最终得到的结果比如:x++ / x+1,这两个算式都会使变量x增加1,而在宏的使用中例如如下代码:
#define MAX(a, b) ( (a) > (b) ? (a) : (b) )
int main()
{
int a = 2;
int b = 4;
int c = MAX(a++, b++);
printf("a = %d b = %d c = %d\n", a, b, c);
return 0;
}
这里最终的运算结果为:a = 3,b = 6, c = 5。
这里的参数替换完之后为: int c = ((a++) > (b++) ? (a++) : (b++));
这里从运算的角度来讲 b++ 会出现两次,而 a++ 出现一次,b++ 为后自增,所以得出的结果
即为:a = 3,b = 6, c = 5。
宏与函数的对比:
宏通常被应用于执行简单的运算,而函数则多是用于复杂的运算。
因为使用的语法比较相似,一般从语言本身上很难能区分开二者,所以在命名上会有一个习惯,宏名全部大写,函数名不要全部大写。
详细对比:
属 性 | #define 定义宏 | 函数 |
代 码 长 度 | 每次使用时,宏代码都会被插入到程序中。除了非常小的宏之外,程序的长度会大幅度增长 | 函数代码只出现于一个地方;每次使用这个函数时,都调用那个地方的同一份代码 |
执行速度 | 相对于函数更快 | 存在函数的调用和返回的额外开 销,所以相对慢一些 |
操作符优先级 | 宏参数的求值是在所有周围表达式的上下文环境里, 除非加上括号,否则邻近操作符的优先级可能会改变最终结果,所以建议宏在书写的时候多些括号。 | 函数参数只在函数调用的时候求值一次,它的结果值传递给函数。表达式的求值结果更容易预测。 |
带有副作用的参数 | 参数可能被替换到宏体中的多个位置,所以带有副作用的参数求值可能会更改最终结果。 | 函数参数只在传参的时候求值一 次,结果更容易控制。 |
参数类型 | 宏的参数与类型无关,只要对参数的操作是合法的, 它就可以使用于任何参数类型。 | 函数的参数是与类型有关的,如果参数的类型不同,就需要不同的函数,即使他们执行的任务是相同的。 |
调试 | 宏是不方便调试的 | 函数是可以逐语句调试的 |
递归 | 宏是不能递归的 | 函数是可以递归的 |
#if / #endif (条件编译):
在编译一个程序的时候我们可以通过条件编译指令选择性的将它进行编译或者放弃编译。
例如:
单条件编译:
int main()
{
#if 1 //如果表达式为真,则被包括的代码参与编译
printf("Hello World!");
#endif
#if 0 //若为假,则被包括的代码不参与编译
printf("Hello Hello!");
#endif
return 0;
}
多分支条件编译:
建立在单条件编译基础上多出 #elif / #else
int main()
{
#if 1==1 //为真,参与编译
printf("ONE\n");
#elif 2==1 //为假,不参与编译
printf("TWO\n");
#elif 3==1 //为假,不参与编译
printf("THREE\n'");
#else //全部只会选择一种执行,所以也不参与编译
printf("FOUR\n");
//如果条件有多个满足,会优先执行最早成立条件的那个
#endif
return 0;
}
判断是否被定义:
#define MAX 100
int main()
{
#if defined(MAX) //若MAX被定义,则被包括的代码参与编译
printf("Hello World!\n");
#endif
//可以替换成以下模式,本质上相同
//#if defined(MAX) -> #ifdef MAX #if !defined(MAX) -> #ifndef MAX
//也有与之相反的:
#ifndef MAX //若MAX被未定义,则被包括的代码参与编译
printf("Hello Woeld!\n");
#endif
return 0;
}
嵌套指令:
这些条件编译指令也是可以嵌套使用的,例如:
#define MAX 100
#define MAXA 1
int main()
{
#if defined(MAX) //查看MAX是否被定义
#ifdef MAXA //如果被定义,则查看MAXA是否被定义
printf("ONE\n");//已被定义,所以输出
#endif
#ifdef MAXB //MAXB未被定义
printf("TWO\n");
#endif
#elif defined(MIN) //MIN也未被定义
#ifdef MINA
printf("THREE\n");
#endif
#endif
return 0;
}