预处理
预处理是程序编译前的一个预先处理的动作,编译一共有4个步骤:C原文件 --> 预处理 --> 编译 --> 汇编 -> 链接 --> 可执行文件。
预处理的工作主要是展开我们C源码中的#开头的语句,去掉了所有的注释,在严格的意义来说,这些语句它并不属于C语言的语法范畴。
以下是预处理的一些指令:
1.头文件 : #include
2.宏定义 : #define
3.取消宏 : #undef
4.条件编译 : #if #ifdef #ifndef #else #elif #enif
5.显示错误 : #error
6.修改当前文件的名字行号 :#line
7.向编译器中发送特定的指令 : #progma
可以在编译程序的时候添加一个-E的选项,让编译器在预处理之后停下来不要继续往下走,macro.i是源程序将#展开的内容。
gcc macro.c -o macro.i -E
宏的概念
宏(macro)实际上以一段字串,在源码中只是用来直接替换目标位置,一般宏都使用全大写字母(这只是一个习惯)。
#define PI 3.141592
#define BUF_SIZE 64
int main (int argc, char const *argv[])
{
printf("PI:lf " , PI);
int a = 100 + PI ;
printf("a:%d
", a);
}
输出:PI:3.141592 a:103
注意:
1.PI就是一个宏,在我们的源代码中如果出现宏的使用,则通过预处理之后会直接被替换,只是直接的字符替换而已,不会考虑语法或运算关系。
无参宏
无参宏意味着我们在使用的时候不需要传递参数
#define BUF_SIZE 64
int main (int argc, char const *argv[])
{
int arr[BUF_SIZE]; //定义了一个int型数组,大小为64个
}
注意:
1.宏他的本指就值直接的替换,没有任何的语法检查;
2.使用宏的情况下如果代码有跟新迭代时,只需要修改宏的一处,整个代码中所有用到宏的地方都会被修改;
3.宏的名字可以提高代码的可读性。
带参宏
带参宏 在使用的时候需要携带参数来使用。
#define MAX(a,b) a>b ? a : b
int main (int argc, char const *argv[])
{
printf("%d " , MAX(100,998) );
printf("%d
" , 100>998 ? 100 : 998 );
}
输出:998 998
注意:
1.使用带参宏的时候也是直接替换。
2.只是存粹的文本替换,没有任何的语法检查/判断,也没有任何的运算
3.宏在预处理之后已经被替换,代码实际运行时是不需要额外的时间开销,只会浪费一点内存的空间。
带参宏的副作用
由于宏只是存粹的文本替换,中间不涉及任何的计算与语法检查,类型匹配,所以用起来会比用函数麻烦很多。
#define MAX(a,b) a>b ? a : b
int main (int argc, char const *argv[])
{
int x = 100;
int y = 200;
printf("MAX:%d
" , MAX(x,y == 200 ? 888:999 ) );
}
从以上的代码中, 不管表达式 y == 200 ? 888:999 的值都是大于 x ,但是却出来最大值为x 、
观察一下替换后的结果:
printf("MAX:%d
" , x>y == 200 ? 888:999 ? x : y == 200 ? 888:999 ); //注意从右向左运算,?的优先级大于:
所以应该修改长如下:
#define MAX(a,b) (a)>(b) ? (a) : (b)
printf("MAX:%d
" , (x)>(y == 200 ? 888:999) ? (x) : (y == 200 ? 888:999) );
使用括号对宏当中的每一项括起来,提高优先级,这样替换之后逻辑不会出现问题。