宏定义#define的使用

1.定义

  1. 定义:
    将一个指定的标示符(宏名)代表一个字符串 /
    用一个短的名字代表一个长的字符串

  2. 格式:

#define 标识符 字符串
ex:
#define PI 3.1415926

在编译预处理时,将程序中在该命令以后出现的所有PI都用3.1415926代替

2.#

假如希望在字符串中包含宏参数,ANSI C允许这样作,在类函数宏的替换部分,#符号用作一个预处理运算符,它可以把语言符号转化程字符串。
例如,如果x是一个宏参量,那么#x可以把参数名转化成相应的字符串。该过程称为字符串化(stringizing)。

#include <iostream>
#define SQUARE(x) printf("square of " #x " is %d\n", (x) * (x))
using namespace std;

int main()
{
    int y = 4;
    SQUARE(4); // square of 4 is 16
    SQUARE(2+4); // square of 2+4 is 36
}

第一次调用宏时使用“y”代替#x;第二次调用时用“2+4"代#x。

3.##

##还可以用于类对象宏的替换部分。
这个运算符把两个语言符号组合成单个语言符号。

#include <stdio.h>

#define XN(n) x##n
#define PXN(n) printf("x" #n " = %d\n", x##n)

using namespace std;

int main()
{
    int XN(1) = 10; //x1
    PXN(1); //print("x1 = %d\n", x1);
}

输出结果:
x1=10

4.多行定义 /

在软件开发过程中,经常有一些常用或者通用的功能或者代码段,这些功能既可以写成函数,也可以封装成为宏定义。那么究竟是用函数好,还是宏定义好?这就要求我们对二者进行合理的取舍。

宏定义中允许包含两行以上命令的情形,此时必须在最右边加上”\”且该行”\”后不能再有任何字符,连注释部分都不能有,下面的每行最后的一定要是”\”,”\”后面加一个空格都会报错,更不能跟注释。

我们来看一个例子,比较两个数或者表达式大小,首先我们把它写成宏定义:

#include <stdio.h>
#include <iostream>

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

using namespace std;

int main()
{
    cout << MAX(6, 2);
}

很显然,我们不会选择用函数来完成这个任务,原因有两个:首先,函数调用会带来额外的开销,它需要开辟一片栈空间,记录返回地址,将形参压栈,从函数返回还要释放堆栈。这种开销不仅会降低代码效率,而且代码量也会大大增加,而使用宏定义则在代码规模和速度方面都比函数更胜一筹;其次,函数的参数必须被声明为一种特定的类型,所以它只能在类型合适的表达式上使用,我们如果要比较两个浮点型的大小,就不得不再写一个专门针对浮点型的比较函数。反之,上面的那个宏定义可以用于整形、长整形、单浮点型、双浮点型以及其他任何可以用“>”操作符比较值大小的类型,也就是说,宏是与类型无关的。

和使用函数相比,使用宏的不利之处在于每次使用宏时,一份宏定义代码的拷贝都会插入到程序中。除非宏非常短,否则使用宏会大幅度增加程序的长度。

还有一些任务根本无法用函数实现,但是用宏定义却很好实现
比如参数类型没法作为参数传递给函数,但是可以把参数类型传递给带参的宏。
例子:

#define MALLOC(n, type) \
  ( (type *) malloc((n)* sizeof(type)))

利用这个宏,我们就可以为任何类型分配一段我们指定的空间大小,并返回指向这段空间的指针。我们可以观察一下这个宏确切的工作过程:

int *ptr; 
ptr = MALLOC ( 5, int );

将这宏展开以后的结果:

ptr = (int ) malloc ( (5) sizeof(int) );

例子2:

#include <iostream>
#include <string.h>

#define F1(n)      \
    if (n == 1)    \
    {              \
        cout << 1; \
    }

using namespace std;

int main()
{
    F1(1);
}

说明:

1、使用#define声明多行宏函数与声明单行宏函数没有本质区别
2、多行声明时,回车换行前要加上字符‘\’,即“[enter]”,注意字符‘\’后要紧跟回车键,中间不能有空格或其他字符。

5.条件编译

1) 常见格式1

#ifdef 表示符号
    程序段1
#else
    程序段2
#endif

作用

  1. 如指定的标示符已经被#define命令定义过,则在编译阶段之编译程序段1,否则编译2
  2. #endif用来限定#ifdef命令的范围,#else部分可以没有。

2) 常见格式2

//#if 后面跟的也是用 #define的定义去判断(tbd 不知道对不对)
#if 表达式
    程序段1
#else
    程序段2
#endif

作用

  1. 指定的表达式值为真时就编译程序段1,否则编译程序短2

3) ex:

#include <iostream>
#define F1 1

#ifdef F1
#define F2 1
#else
#define F2 2
#endif
using namespace std;
int main()
{
    cout << F2; //输出1
}
#define j 111

#if j == 111
int i  =222;
#else
int i  =444;
#endif

6.带参数的宏

格式

#define 宏名(参数表) 字符串

ex:

#define S(a,b) a*b
area = S(3,2); // area = 3*2;

7.VA_ARGS 可变宏参数

#include <iostream>
#define DEBUG(...) printf(__VA_ARGS__)
#define ERROR(fmt, ...) printf(fmt, __VA_ARGS__)

using namespace std;

int main()
{
    int y = 99;
    //宏展开: printf("%d\n", y)
    DEBUG("%d\n", y);
    //宏展开: printf("%d,%d,%d\n", y, 1, 2)
    DEBUG("%d,%d,%d\n", y, 1, 2);

    ERROR("%d\n", y);

    ERROR("%d,%d,%f\n", 1, 2, 3.0);
}

8. #ifndef—#define—#endif的作用

#ifndef—#endif,#define是预处理命令,编译器在正式编译之前执行的命令。
#ifndef—#endif是配套使用的条件编译指令,
即如果#ifndef后的宏没有定义,则编译#ifndef—#endif之间的代码,否则不编译。

所以,假定头文件名为:tree.h。在头文件开始,

#ifndef __TREE_H__ ; //习惯上以头文件名为宏名

#define __TREE_H__;
...
#endif __TREE_H__

第一次以用该头文件时,没有定义宏 TREE_H,则进行编译并定义宏__TREE_H__。当其他文件中再次引用该头文件时,因为已经定义了宏__TREE_H__,所以不执行中间的代码,从而可以防止重复引用该头文件

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值