宏定义与宏替换

一. 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<stdio.h>
#define n  2+2
int main()
{
  int a = 0;
  a = n * n;
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

2.使用带参数的宏出现的问题

#include<stdio.h>
#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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在vs带的编译器下,程序输出结果 i = 6 j =16 i = 8 j = 64

四. 结语 
本文主要讲了宏定义的用法以及要使用时要注意的东西,同时注意到宏替换在预处理阶段完成,只是进行文本替换。

五. 常用的宏定义

1 防止一个头文件被重复包含

#ifndef BODYDEF_H 
#define BODYDEF_H 
 //头文件内容 
#endif
  • 1
  • 2
  • 3
  • 4

2 得到指定地址上的一个字节或字

#define MEM_B( x ) ( *( (byte *) (x) ) ) 
#define MEM_W( x ) ( *( (word *) (x) ) )
  • 1
  • 2

3 得到一个field在结构体(struct)中的偏移量

#define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )
  • 1

4 得到一个结构体中field所占用的字节数

#define FSIZ( type, field ) sizeof( ((type *) 0)->field )
  • 1

5 得到一个变量的地址(word宽度)

#define B_PTR( var ) ( (byte *) (void *) &(var) ) 
#define W_PTR( var ) ( (word *) (void *) &(var) )
  • 1
  • 2

6 将一个字母转换为大写

#define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )
  • 1

7 判断字符是不是10进值的数字

#define DECCHK( c ) ((c) >= ''0'' && (c) <= ''9'')
  • 1

8 判断字符是不是16进值的数字

#define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||((c) >= ''A'' && (c) <= ''F'') ||((c) >= ''a'' && (c) <= ''f'') )
  • 1

9 防止溢出的一个方法

#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
  • 1

10 返回数组元素的个数

#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
  • 1

11 使用一些宏跟踪调试 
ANSI标准说明了五个预定义的宏名。它们是

_LINE_ /*(两个下划线),对应%d*/
_FILE_ /*对应%s*/
_DATE_ /*对应%s*/
_TIME_ /*对应%s*/
  • 1
  • 2
  • 3
  • 4

摘自:http://blog.chinaunix.net/uid-21372424-id-119797.html

posted on 2019-08-04 16:38  Nancy_Fighting  阅读(2656)  评论(0编辑  收藏  举报

导航