一. c /c++语言中使用宏的主要目的主要有 3 个:
1. 提高代码的可读性;
把用到的常量定义成有意义的名字;
2. 无需函数调用,运行效率高;
对于一些简单的操作,无需调用函数,虽然编程是强调模块化,但是函数调用时,需要保护现场和恢复现场。这些都需要耗时。对于复杂的操作来讲,这些耗时可以不计,但是对于简单的操作,则效率低下。利用宏来代替简单的操作,则可以提高程序的运行效率。
3. 可维护行好;
对于用得比较多的常量或者简单操作,一旦需要修改,则只需要修改宏定义处,不需要逐条修改。
二. 宏定义命令 : #define
1 . #define命令主要是将一个标识符替换为一个字符串,该标识符称为宏名,被替换的字符串被称为替换文本。
2. 用法:
主要有两种格式,一个是简单的宏定义,另一个是带参数的宏定义;
简单的宏定义: #define
例: #define pi 3.1415
带参数的宏定义:#define ()
例: #define A(x) x
三. 宏替换
当宏定义好后,在程序中使用宏名就称为宏替换。当程序进行编译时实际上经过了预处理,编译(生成中间代码,即从源程序翻译为中间语言,即汇编),汇编(将汇编语言翻译成机器代码,即二进制代码),链接(将目标文件生成 .exe文件)。宏替换就发生在预处理(也叫预编译)阶段,也就是说在编译之前(生成二进制文件之前)就已经完成了文本的替换工作。
关于预处理主要完成的工作是:
1.文件包含,将#include包含的文件找到,并在#include处进行展开;
2.条件编译,根据#if #ifdef 等编译命令及其后的条件,将源程序的一部分包含进来或排除在外,通常把排除在外的语句换成空行。
3.宏展开,将程序中所用到的宏展开成宏定义的替换文本。经过宏展开之后的程序与之前的源程序的只是简单的文本替换,并无计算功能。这是理解宏的要点。
四. 使用宏要注意的问题
1.使用简单宏出现的问题
#include
#define n 2+2
int main()
{
int a = 0;
a = n * n;
return 0;
}
2.使用带参数的宏出现的问题
#include
#define product(a) a*a
int main()
{
int i = 4;
int j = product(i++);
printf("i = %d\n",i);
printf("j = %d\n",j);
j = product(++i);
printf("i = %d\n",i);
printf("j = %d\n",j);
return 0;
}
在vs带的编译器下,程序输出结果 i = 6 j =16 i = 8 j = 64
四. 结语
本文主要讲了宏定义的用法以及要使用时要注意的东西,同时注意到宏替换在预处理阶段完成,只是进行文本替换。
五. 常用的宏定义
1 防止一个头文件被重复包含
#ifndef BODYDEF_H
#define BODYDEF_H
//头文件内容
#endif
2 得到指定地址上的一个字节或字
#define MEM_B( x ) ( *( (byte *) (x) ) )
#define MEM_W( x ) ( *( (word *) (x) ) )
3 得到一个field在结构体(struct)中的偏移量
#define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )
4 得到一个结构体中field所占用的字节数
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )
5 得到一个变量的地址(word宽度)
#define B_PTR( var ) ( (byte *) (void *) &(var) )
#define W_PTR( var ) ( (word *) (void *) &(var) )
6 将一个字母转换为大写
#define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )
7 判断字符是不是10进值的数字
#define DECCHK( c ) ((c) >= ''0'' && (c) <= ''9'')
8 判断字符是不是16进值的数字
#define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||((c) >= ''A'' && (c) <= ''F'') ||((c) >= ''a'' && (c) <= ''f'') )
9 防止溢出的一个方法
#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
10 返回数组元素的个数
#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
11 使用一些宏跟踪调试
ANSI标准说明了五个预定义的宏名。它们是
_LINE_ /*(两个下划线),对应%d*/
_FILE_ /*对应%s*/
_DATE_ /*对应%s*/
_TIME_ /*对应%s*/