宏(使用注意事项、主要用途)------c++程序设计原理与实践(进阶篇)

  使用宏的时候一定要小心:在c中没有真正有效的方法来避免使用宏,但宏带有严重的副作用,因为宏不遵守通常的c(或c++)作用域和类型规则——它只是一种文本替换

  宏的使用注意事项:

  • 所以宏名全部大写。
  • 不是宏的结构不要使用全部大写的名字。
  • 不要为宏取短的或“有趣”的名字,如max或min。
  • 期望其他人也遵守上面简单而常见的规范。

  宏的主要用途:

  • 定义“常量”。
  • 定义类似函数的结构。
  • “改进”语法。
  • 控制条件编译。
  • 其他不常见用途。

  我们认为宏被过度使用了,但在c++程序中有时很难避免使用宏(特别是当编写的程序需要移植到很老的编译器上或者有特殊限制的平台上,在c程序中还没有一种合理而完整的替代方法。)。

  下面介绍一些有关的宏技术。

类函数宏:

  下面是一个非常典型的类函数宏:

#define MAX(x,y)((x)>=(y)?(x):(y))

  我们为宏取名为全大写字母的MAX,以便与常用的函数名max区别开来,显然,它与函数还是有很大区别的:没有参数类型、没有语句块、没有返回值语句等。另外,宏定义中的那些括号是起什么作用呢?考虑如下代码:

int aa=MAX(1,2);
double dd=MAX(aa++,2);
char cc=MAX(dd,aa)+2;

  宏替换后,程序扩展为:

int aa=((1)>=(2)?(1):(2));
double dd=((aa++)>=(2)?(aa)++:(2));
char cc=((dd)>=(aa)?(dd):(aa))+2;

    在宏的定义中,使用任何参数时都应将其置于括号之中(当作表达式)。如果在宏定义中没有使用“那些括号”。最后一条语句会扩展为:

char cc=dd>=aa?dd:aa+2;

  也就是说,cc的值将和你根据其定义推断出的值不同。

  另一方面,对于第二条语句,使用再多括号耶解决不了问题。宏参数x被替换为aa++,由于x在MAX使用了两次,因此x进行了两次增1运算。注意,不要向宏传递可能引起副作用的参数。

如何取消定义宏:

#undef MAX(x,y);

  并不是所以宏参数都被用作表达式。例如:

#define ALLOC(T,n) ((T*)malloc(sizeof(T)*n));        //malloc  为c中的函数  类似c++的new

  这是来自实际程序中的例子,内存分配时sizeof中使用的类型与所需类型可能不匹配,这个宏对避免此类错误很有用:

double* p= malloc(sizeof(int)*10);        //可能错误

  如果还希望宏能捕获内存耗尽的错误:假如已经定义了error_var和error(),可以这样定义宏:

#define ALLOC(T,n) (errpr_var=(T*)malloc(sizeof(T)*n),\
                             (error_var==0)\
                             ?(error("memory allocation failure"),0)\
                             :error_var)

  行结尾出的 \ 并非输入错误,将一个较长的宏分成几行,就必须在非结尾行的最后加上 \ 。

语法宏:

  你可以定义这样一类宏,它们能使源程序形式上更符合你的偏好,例如:

#define forever for(;;)
#define CASE break;case
#define begin {
#define end }

  但不建议使用。

条件编译:

   假设某个头文件有两个版本,比如说一个是Linux版,另一个是Windows版。在程序中你如何选择使用哪个版本呢?常用方法如下:

#ifdef WINDOWS
    #lnclude"my_windows_header.h"
#else 
    #include"my_linux_header.h"
#endif

  现在,如果有人在编译之前定义了宏 WINDOWS,则效果为:

#lnclude"my_windows_header.h"

  否则,效果为:

#include"my_linux_header.h"

  #indef WINDOWS 并不关心 WINDOWS被定义成什么,它只关心WINDOWS是否被定义。

很多大型系统(包括所有操作系统)都会定义类似WINDOWS这样的宏,以供我们检验。

我们还可以这样来检查程序是在被c++编译器编译还是被c编译器编译:

#ifdef __cplusplus
    //in c++
#else 
    //in c
#endif

  还有一种类似的结构,通常被人们称为包含保护,常常用来防止头文件被保护多次:

#ifndef MY_WINDOWS_HEADER
#define MY_WINDOWS_HEADER
    //下面是头文件内容
#endif

  #ifndef检测宏是否未被定义,即它与#ifdef是相对的。逻辑上,用于源文件控制的宏与其他修改源码(宏替换)的宏有很大不同。他们只是使用了相同的下层语言机制。

 

c++程序设计原理与实践(进阶篇)

posted @ 2017-11-15 19:21  ff_d  阅读(825)  评论(0编辑  收藏  举报