基本语法
例子如下
#define机制包括了一个规定,允许把参数替换到文本中,这种实现通常称为宏(macro)或定义宏(define macro)。
下面是宏的声明方式
其中的parament-list是一个由逗号隔开的符号表,它们可能出现在stuff中。
注意:
参数列表的左括号必须与name紧邻,如果两者之间有任何空白存在,参数列表就会被解释为stuff的一部分。
现在举一个例子:
这个宏接收一个参数x,如果在上述声明之后,你把SQYUARE(5)置于程序中,处理器就会用下面的这个表达式来替换上面的表达式:5*5.
但这里我们打一个警告:
因为这个宏是存在一定问题的。
观察下面的这段代码:
乍一看,你可能觉得这段代码会打印出36,但事实上它会打印出11,这是为什么呢?
因为替换文本时,参数x被替换成a+1,所以这条语句实际上变成了:
这样就比较清晰了,由替换产生的表达式并没有按照预想的次序求值。
如果我们想达到我们的目标,只需要向x添加小括号就行了。
这样预处理之后就达到了我们的目标
但我们要注意,这里还有一个宏定义会让它出现问题:
这种情况下会打印什么值呢?貌似好像是100,但实际上是55.
让我们看看替换后的表达式是什么
这里出现错误是因为乘法运算的优先级优于加法运算,所以出现了55.这个问题的解决方法就是再加上一对小括号就可以了。
提示:所以用于对数值表达式进行求值的宏定义都应该用这种方式进行打括号,避免在使用宏的时候由于参数中的参数符或邻近操作符之间产生不可预料的相互作用 。
当宏参数在宏的定义中出现超过一次了,如果参数带有副作用,那么你在使用这个参数的时候就可能遇到危险,导致不可预测的后果,副作用就是表达式求值的时候出现永久性效果。
例如:
MAX宏可以证明具有副作用的参数所引起的问题
上面的代码会输出什么值呢?
经过前两个的举例,我们可以想到经过替换之后x=6,y=10,z=9.
经过以上的注意事项,我们可以更加深入的来了解宏的替换规则。
在程序中扩展#define定义符号和宏时,需要涉及几个步骤:
1.在调用宏时,首先对参数进行检查,看看是否包含任何由#define定义的符号。如果是,它们首先被替换。
2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被他们的宏所替换。
3.最后,再次对结果文件进行扫描,看看它是否包含任何由#define定义的符号。如果是,就重复上述处理过程。
1.宏参数和#define定义中可以出现其它#define定义的符号。但是对于宏,不能出现递归。
2.当预处理器搜索#define定义的符号时,字符串常量的内容并不被搜索。
宏通常被用于执行简单的计算。
比如在两个数中找出较大的数时,写成如下的宏更具优势。
我们思考一下,为什么我们不用函数而是用宏呢?
其实原因有二:
1.用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。所以宏比函数在程序的规模和速度方面更胜一筹。
2.更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之这个宏可以适用于整型、长整型、浮点型等可以用于>来比较的类型。宏是类型无关的。
但是不能就说宏一定比函数好啊
跟函数相比,宏也有自己的劣势:
1.每次使用宏的时候,一份宏定义的代码将插入到程序中,除非宏比较短,否则可能大幅度增加程序的长度。
2.宏是没有办法调试的。
3.宏由于类型无关,也就不够严谨。(双刃剑)
4.宏可能会带来运算符优先级的问题,容易导致程序出错。
宏有的时候呢可以做到函数做不到的事情。比如:宏的参数可以出现类型,但是函数做不到。
比如下面这段代码:
上述的代码函数就做不到。