购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.3.2 预处理

预处理中的伪指令通常以#开头,主要包括:宏定义、条件编译、头文件包含和其他符号。在复杂的设备中,为避免运行时的冲突,多个模块程序要合理地使用预处理程序。其中头文件包含如#include "FileName.h"(自定义文件)或#include <FileName.h>(系统头文件),开发人员在定义头文件时包含这些文件要用(""),且需要标明路径,通常用相对路径或在Makefile文件中定义好,否则编译器找不到该文件会出错返回。

宏定义程序中用一个标识符来表示特定字符串或数字,称为“宏”。宏定义有两种:有参数的宏定义(简称“带参宏定义”)和无参数的宏定义(简称“无参宏定义”)。下面分别讨论注意事项。

1.无参宏定义

(1)宏名在源程序中若用双引号括起来,则不视为一个宏而视为一个字符串,预处理程序不作宏替换,举例如下。

#define NOT 0
main ()
{
    printf ("NOT\n");
}

上例中定义宏名NOT表示0,但在printf语句中NOT被看成字符串处理。

(2)宏定义作为一种简单的代换,预处理程序并不作任何检查,但会在编译阶段检查,因此定义宏时需要考虑一些发生异常情形时的处理方法。

(3)作用域。在函数之外的宏定义,作用域为宏初始定义起到本文件的源程序结束。终止作用域可使用#undef命令,当被多个文件包含时,作用域就在所引用的文件中。

(4)宏定义嵌套。在宏定义中可以使用前文已定义的宏,由预处理程序进行嵌套层代换。

#define PIE 3.14
#define S PIE*y*y

调用printf ("%f", S);

在宏替换后变为:printf ("%f",3.14*y*y);

(5)宏定义与typedef的区别

宏定义是简单的字符串替换,在预处理阶段完成;typedef 是在编译时处理的,不是进行简单的代换,而是对类型说明符重新命名。新名称代表和原类型说明有同样的功能。

#define INT32 int*
typedef (int*) INT;

这两者在形式上相似,在实际使用中却不相同。

INT32x,y;在宏代换后变成:int *x,y;这里x是指向整型的指针变量,而y是整型变量。

INTx,y;这里x,y都是指向整型的指针变量。

2.带参宏定义

(1)使用空格时要恰当。如定义较大数的宏#define MAX (a,b) (a>b)?a:b就不能写成如下形式。

#define MAX (a,b) (a>b)?a:b

上例中,预处理器认为MAX是无参宏定义。

(2)宏定义中类型定义要注意匹配。因为形式参数不分配内存单元,宏调用中的实际参数(以下简称实参)有具体的值,不是参数传递而是符号替换。

(3)字符串内的形式参数(以下简称形参)通常要用括号括起来,以避免出错。

#define SEQU (y) y*y
main ()
{
    int a,sequ;
    scanf ("%d",&a);
    sequ =SEQU (a+1);
    printf ("sequ =%d\n", sequ);
}

上例中没有使用括号,如果用下例中的两个宏定义,运行结果有很大差异,读者可以自行测试一下。

#define SEQU (y) (y)* (y)
#define SEQU (y) ((y)* (y))

(4)带参宏和带参函数形式相似,即使两者的运行结果相同,但本质仍然不同。同一表达式用函数调用会涉及参数入栈/出栈、分配内存等,耗费的时间也不一样。所以常用的做法是将简短的函数(如10行以内)用宏或者内联函数来替代。

(5)宏定义可用来定义多个语句,类似于函数功能,在调用宏时,这些语句可替换到源程序中按顺序运行。

#define COMPUTE (a,b,c,d) a=l*w;b=l*h;c=w*h;d=w*l*h;
main ()
{
    int l=3,w=4,h=5,a,b,c, d;
    COMPUTE (a,b,c,d);
    printf ("a=%d\nb=%d\nc=%d\nd=%d\n",a,b,c,d);
}

上例中用宏名COMPUTE表示4个赋值语句。在宏调用时,把4个语句展开并用实际参数替代形式参数,将计算结果送入实际参数中。 0gSuU8pzzKst2YiJ8n7z7NxeDOuhM4slAGxQG+cm/mdOv0bA4IeifM0JNxfVOIW8

#define LIST_FIND(head,cmpfn,type,args)       \
 ({                        \
    const struct list_head*__i=(head);       \
    do{                     \
      __i=__i->next;          \
      if(__i==(head)){          \
          __i=NULL;          \
          break;              \
      }                 \
    }while(!cmpfn((const type)__i,args));   \
    (type)__i;                 \
})
点击中间区域
呼出菜单
上一章
目录
下一章
×