一、宏的定义与撤销
1、宏定义时尽量加上括号
#include <iostream>
#include<math.h>
using namespace std;
#define T1 3+4 //容易产生歧义
#define T2 (4+3)//添加括号后,语义清楚
int main()
{
int a =1;
int b =1;
a = 2*T1;//宏替换后 int a = 2*3+4
b = 2*T2;//宏替换后 int b = 2*(3+4)
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
return 0;
}
结果如下,所以宏定义的时候,为了清楚表达语义,尽量加上括号
a = 10
b = 14
2、宏的撤销
#define PI 3.14
#undef PI
3、引导号中的宏不会被替换
#define PI 3.14
printf("%s = %f","PI",PI);//输出 PT = 3.14
4、宏定义的宏名必须是合法的标识符
#define 0X abcd //error
5、宏定义中引号必须成对出现
#define TEST1 "z //error
#define TEST2 'z //error
二、带有参数的宏定义
//带有参数的宏定义
#define MAX(a,b) (a>b?a:b)
#define MIN(a,b) (a<b?a:b)
//使用
int sum = MAX(1,2) + MIN(3,4);//替换后为: (1>2?1:2) + (3<4?3:4)
//结果是sum = 2+3
MAX(1,2,3) //error,参数不同会报错
需要注意的是:C
(1)宏调用时参数的个数要与定义时相同。
三、跨行宏定义用反斜杠连接
//定义一个很长的宏,用反斜杠连接
#define SWAP(a,b) do{\
int tmp = 0;\
tmp = a;\
a = b;\
b = tmp;\
}while(0)
int main()
{
int a = 3;
int b =4;
cout<<"a = "<<a<<" b = "<<b<<endl;
SWAP(a,b);
cout<<"after SWAP "<<endl;
cout<<"a = "<<a<<" b = "<<b<<endl;
return 0;
}
结果如下
a = 3 b = 4
after SWAP
a = 4 b = 3
从结果可以看出,宏定义是在编译阶段的代码替换,不是程序执行的函数调用。所以这里可以直接交换a和b的值。
四、三个特殊符号:# , ## , #@
#include <iostream>
#include<math.h>
#include<typeinfo>
using namespace std;
//定义一个很长的宏,用反斜杠连接
#define CONNECT(a,b) a##b
#define TOCHAR(a) #@a
#define TOSTRING(a) #a
//a##b 表示连接
int main()
{
//a##b表示连接
int n = CONNECT(123, 456);
cout << "CONNECT(a,b) a##b" << endl;
cout << n << endl;
cout << typeid(n).name() << endl;
const char* str = CONNECT("abcd","efg");
cout << str << endl;
cout << typeid(str).name() << endl;
cout << endl;
//#@a(参数字符化操作,将宏参数字符化,取参数中的最后一个字符,参数超过四个字符报错)
char ch1 = TOCHAR(1);
char ch2 = TOCHAR(123);
cout << "TOCHAR(a) #@a" << endl;
cout << ch1 << endl;
cout << ch2 << endl;
cout << endl;
//#(字符串化操作,自动将宏参数字符串化)
const char* ch3 = TOSTRING(123456);
cout << "TOSTRING(a) #a" << endl;
cout << ch3 << endl;
system("pause");
return 0;
}
结果如下
CONNECT(a,b) a##b
123456
int
abcdefg
char const *
TOCHAR(a) #@a
1
3
TOSTRING(a) #a
123456
请按任意键继续. . .
五、常见的宏定义
1、防止头文件被重复包含
#ifndef _BODYDEF_H_
#define _BODYDEF_H_
//头文件内容
#endif
2、得到指定地址上的一个字节值
#include<stdio.h>
#include"atlstr.h"
//B表示字节byte
#define MEM_B(x) (*( (BYTE*) (x) ) )
//B表示为int
#define MEM_I(x) (*( (int*) (x) ) )
int main()
{
int bTest = 0x123456;
BYTE m = MEM_B(&bTest);
int n = MEM_I(&bTest);
printf("%x\n", m);
printf("%x\n", n);
system("pause");
return 0;
}
结果如下
56
123456
请按任意键继续. . .
3、得到一个field在结构体(struct)中的偏移量
#define OFFSETOF( type, field ) ( (size_t) &(( type *) 0)-> field )
4、得到一个结构体中field所占用的字节数
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )
5、等到一个变量的地址
#define B_PTR(var) ( (byte *) (void *) &(var) )
#define I_PTR(var) ( (int *) (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、防止溢出的一个方法 (判断+1之后会不会因为溢出变成最小)
#define INC_SAT(val) (val = ((val)+1 > (val)) ? (val)+1 : (val))
10、返回数组元素的个数
#define ARR_SIZE(a) ( sizeof((a)) / sizeof((a[0])) )