应聘嵌入式工程师笔试常考之宏定义的优缺点

时间:2022-01-12 08:05:11

1、宏定义的优点:
(1)   方便程序的修改
      使用简单宏定义可用宏代替一个在程序中经常使用的常量,这样在将该常量改变时,不用对整个程序进行修改,只修改宏定义的字符串即可,而且当常量比较长时,我们可以用较短的有意义的标识符来写程序,这样更方便一些。
(2) 提高程序的运行效率
      使用带参数的宏定义可完成函数调用的功能,又能减少系统开销,提高运行效率。正如C语言中所讲,函数的使用可以使程序更加模块化,便于组织,而且可重复利用,但在发生函数调用时,需要保留调用函数的现场,以便子函数执行结束后能返回继续执行,同样在子函数执行完后要恢复调用函数的现场,这都需要一定的时间,如果子函数执行的操作比较多,这种转换时间开销可以忽略,但如果子函数完成的功能比较少,甚至于只完成一点操作,如一个乘法语句的操作,则这部分转换开销就相对较大了,但使用带参数的宏定义就不会出现这个问题,因为它是在预处理阶段即进行了宏展开,在执行时不需要转换,即在当地执行。宏定义可完成简单的操作,但复杂的操作还是要由函数调用来完成,而且宏定义所占用的目标代码空间相对较大。所以在使用时要依据具体情况来决定是否使用宏定义。
2、宏定义的缺点:

宏定义有一些缺点:
(1) 无法对宏定义中的变量进行类型检查
此缺点,是相对于const变量来说的
[define与const的区别的简单总结]
define定义的变量,是Compile-Time时期的变量,系统在编译时候,就将其全部替换,而不会对其变量进行类型等属性检查,相对不是很安全,可能存在潜在的问题,而没有发现.
正因为其仅仅是编译时期替换,所以其定义的变量,是不会在运行时候分配内存的,不占用内存空间.
const定义的变量,是 Run-Time时期的变量,如果类型不匹配,系统在运行时候,就会发现并提示或报错,对应的,const变量在运行时期,也是一种变量,系统会为其分配内存.
 
(2) 边界效应
A.    未加括号带来的边界效应
由于宏定义的时候,其各个分量未加括号,而在使用宏定义的时候,传递的参数是变量的表达式,然后经过系统展开后,由于优先级的原因,导致其结果不是你所希望的.
[例子]
#define MUL(A,B) A*B
而在使用的时候,这样的调用:
int a=1,b=2,c=3,d=0;
d=MUL(a+b,c)
经过编译时候展开,就变成了
d=a+b*c
而不是我们所希望的
d=(a+b)*c
[解决办法]
其解决办法也很简单,就是给每个分量,都加上括号,就可以避免此类问题
即,在宏定义的时候,如此定义:
#define MUL(A,B) ((A)*(B))
 
B.    在define数据类型的时候, 未加括号带来的问题
在用define进行新的数据类型定义的时候,由于未加括号,会出现你所未预料到的结果.
此点其实就是上面说的边界效应,之所以将此点单独说一下,是由于此点不是普通计算结果的问题,而是数据类型的问题,问题相对更严重.
也是笔者在看《想成为嵌入式程序员应知道的0×10个基本问题》的时候,看到其作者提到的这个问题,此处就用其例子解释如下:
[例子]
#define dPS struct s *   //注意末尾无分号
当使用的时候,遇到:
dPS p1,p2;
的时候,经过编译时候替换扩展,就变成了
struct s* p1,p2;
而p2就不是我们所希望的s的指针类型了,而是一个普通的s数据结构类型的了.产生了边界效应.
[解决办法]
对应的解决办法也很简单,就是,遇到此类新数据类型定义的时候,还是用typedef
将上述宏定义改为:
typedef struct s * tPS; // 注意末尾有分号
而后的使用:
tPS p1,p2;
就正常了.
 
C.   特殊情况时候,加了括号也无法避免错误
在宏定义中出现++或—之类的操作符的时候,即使加括号,也无法避免其中的问题
[例子]
#define MIN(A,B) ((A)<(B)?(A):(B))
如果进行如此调用
int a=1,b=3,min=0;
min=MIN(a++,b);
 经过编译替换展开后,就成了
max=((a++)< (b)?(a++):(b))
计算出来的结果,就是
min=3,而不是我们所要的min=1了.
 
此类问题无法避免,除非程序员在调用宏的时候,自己多加注意,尽量避免此类调用.

***函数调用和宏函数的区别:

       函数和宏函数的区别就在于,宏函数占用了大量的空间(以空间换时间),而函数占用了时间。大家要知道的是,函数调用是要使用系统的栈来保存数据的,如果编译器里有栈检查选项,一般在函数的头会嵌入一些汇编语句对当前栈进行检查;同时,CPU也要在函数调用时保存和恢复当前的现场,进行压栈和弹栈操作,所以,函数调用需要一些CPU时间。而宏函数不存在这个问
题(预处理阶段已经完成宏替换)。宏函数仅仅作为预先写好的代码嵌入到当前程序,不会产生函数调用,所以仅仅是占用了空间,在频繁调用同一个宏函数的时候,该现象尤其突出。