今天读一个github上下载的C语言代码,读得时候就像在读天书,完全不像C。在此之前,我一直以为自己C语言掌握的还不错的。所以恶补了以下关于预处理的知识。
相信很多读者和我一样,只是会用C语言而已,只会循环跳跃闭着眼,但是学了宏定义之后就马上可以走上人生巅峰了。废话到此为止,下面开整:
引用经典里的一句话:预处理器的工作就是把一些文本转换成另外以下文本。--《C Primer Plus》
明示常量#define
会使用C的都知道这个简单的命令,我们也经常使用它定义一些常量,什么字符串最大长度了之类的,下面就从这个基本功能讲起:
- #define定义明示常量,它的主要作用就是将程序中所有出现定义名称的地方替换为设定的值。示例如下:
#include <stdio.h> #define TWO 2 //最简单的宏定义,将TWO定义为2,程序里所有TWO将被2替换 #define NIHAO "你好啊,小伙子! " //将NIHAO定义为一个字符串,所有NIHAO都会替换为对应字符串 #define FOUR TWO*TWO //在一个宏定义里可以引用别的定义 #define PX printf("x is %d. ",x)//可以将一个宏定义为一个函数,但是在使用PX之前,必须保证x存在/* * */ int main(int argc, char** argv) { printf("TWO= %d ",TWO); //预处理完成之后将被替换为 printf("TWO= %d ",2); printf(NIHAO); //预处理完成之后将被替换为 printf("你好啊,小伙子!"); printf("FOUR= %d ",FOUR); //预处理完成之后将被替换为 printf("FOUR= %d ",2*2); int x=1; PX; //预处理完成之后将被替换为 printf("x is %d. ",x); //虽然使用方便,但是只能输出x,而不能输出其他任何变量 return 0; }
/*
output:
TWO= 2
你好啊,小伙子!
FOUR= 4
x is 1.
*/ - 带有参数的宏定义,正如上述代码所示,不带参数的宏定义简单明了,但是功能单一。上述宏定义PX也只能输出x变量的值,而带参数的宏定义就可以解决这些问题,示例如下:
#include <stdio.h> #define SQUARE(x) x*x #define PR(x) printf("num is %d ",x) /* * */ int main(int argc, char** argv) { int b=2; int z=SQUARE(b); printf("square b is %d ",z); PR(SQUARE(b)); PR(b); return 0; }
/*
output:
square b is 4
num is 4
num is 2
*/读到这里是不是觉得宏定义无所不能了?然而,并不是的,下面是宏定义里面的各种陷阱:
#include <stdio.h> #define SQUARE(x) x*x #define PR(x) printf("num is %d ",x) /* * */ int main(int argc, char** argv) { int b=2; int z=SQUARE(b); printf("square b is %d ",z); PR(SQUARE(b)); PR(b); //陷阱一: 修改为#define SQUARE(x) (x)*(x) 可以避免 printf("square of b+2 is %d ",SQUARE(b+2)); //陷阱二: 修改为#define SQUARE(x) ((x)*(x)) 可以避免 printf("10/SQUARE(b) is %d ",10/SQUARE(b)); //陷阱三:宏定义参数不要使用自增 printf("SQUARE(++b) is %d ",SQUARE(++b)); /* output: square b is 4 num is 4 num is 2 square of b+2 is 8 10/SQUARE(b) is 10 SQUARE(++b) is 16 */ return 0; }
上述代码有三个陷阱,这些陷阱都是对原理不理解造成的,所谓预处理就是把一种文本处理为另外一种文本。
对于陷阱一:printf("square of b+2 is %d ",SQUARE(b+2));预处理结束之后SQUARE(b+2)会被处理为:b+2*b+2 ,b=2 所以结果为8,解决方法:将宏定义改为#define SQUARE(x) (x)*(x)对于陷阱二:printf("10/SQUARE(b) is %d ",10/SQUARE(b));预处理结束之后10/SQUARE(b)会被处理为: 10/2*2,所以结果是10,而不是我们预期的2,解决方法:将宏定义改为#define SQUARE(x) ((x)*(x))对于陷阱三:printf("SQUARE(++b) is %d ",SQUARE(++b));预处理结束之后SQUARE(++b) 会被处理为: ++b*++b ,就是3×4=12,而避免此类陷阱的方法比较粗暴,就是不要将自增用于宏定义参数!!。。 - 变参宏