C++预处理命令,包含宏定义、文件包含、条件编译等

C++ #if #endif #define #ifdef #ifndef #if defined #if !defined详解

先了解一下 预处理命令:

在编译之前进行得处理,C语言得预处理主要有三个方面得内容:1.宏定义;2.文件包含;3.条件编译。预处理命令是以“#”符号开头的。

常见符号意义

#if                //编译预处理中的条件命令,相当于C++中的if语句
#define            //定义一个预处理宏
#undef             //取消宏定义
#ifdef             //判断某个宏是否被定义
#ifndef            //判断某个宏是否未被定义,与#ifdef相反
#elif              //当#if, #ifdef, #ifndef或前面的#elif条件不满足时,执行当前#elif之后的语句,相当于C++中的else-if
#else              //与#if, #ifdef, #ifndef对应, 当这些条件都不满足时,执行#else之后的语句,相当于C++中的else
#endif             //#if, #ifdef, #ifndef这些条件命令的结束标志,成对使用
defined            //与#if, #elif配合使用,判断某个宏是否被定义

1、宏定义

可以对类型重命名,例如:

#define PI 3.141592654

NOTE:

符号常量虽然可以提高代码的可读性和可维护性,如果要修改它的值,只需定义修改一次,然后重新编译即可。但是有可能会与程序在其他地方使用的变量冲突,建议使用const关键字代替#define,例如:

#define n_def 5
const int n_con = 7;
void test()
{
    int n_def;
    int n_con;
}

预处理器将把int n_def替换为int 5;从而导致了编译错误,而test()中定义的n_con是本地变量。

宏可以带参数(也就是宏函数),#define 标识符(x1,x2…) 替换列表,注意标识符和(之间不能有空格,例如:

#define DeletePtrArr(p)  if ((p)!=NULL) { delete[] (p);(p)=NULL;}
#define DeletePtr(p) {if(NULL!=p){delete p; p = NULL;}}
#define max(Var1,Var2) (Var1>Var2? Var1:Var2)

NOTE:

使用宏函数的时候,效率很高,这也是使用其的一个主要原因。但是也存在一些缺点:仅仅做预处理器符号表的简单替换,因此它不能进行参数的有效性检验,另外它的返回值不能被强制转换为可转换的合适的类型,宏定义时还要注意书写(参数要括起来),否则容易出现歧义,宏定义只是在形式上类似函数;宏不能访问对象的私有成员;宏定义任何时候都是执行严格的参数替换策略,容易产生二义性,例如在使用++、–等操作符的时候可能会出现问题。

为了解决宏定义的以上问题,c++中引入了内联函数(在函数前面加上inline),即引入inline关键字,在能使用宏定义的地方都能够使用inline函数;inline函数也没有调用的开销,它会进行数据类型检查, 因为inline函数是真正的函数;inline函数还可以使用类的保护成员以及私有成员,因为inline函数可以作为类的成员函数;另外inline函数在运行时可以调试,而宏定义不能。一般在c++中建议用内联函数代替宏

举个例子1:(参考:https://blog.csdn.net/lyrebing/article/details/24018971)

#include <iostream>
using namespace std ;
#define Max( a , b ) ( a > b ? a : b);  //宏定义,在此处#define之后加了; 一般不要加;下面解释
inline int fun( int a , int b )		    //内联函数
{
 return ( a > b ? a : b ) ;
}
int main()
{
 int a , b ;
 a = 3 ;
 b = 0 ;
 Max( ++a , b );
 cout << a << endl ;
 a = 3 ;
 b = 5 ;
 Max( ++a , b );
 cout << a << endl ;
 a = 3 ;
 b = 0 ;
 fun( ++a , b ) ;
 cout << a << endl ;
 return 0 ;
}

以上输出的结果为: 5 4 4

还应注意与typedef的区别

type是在编译时处理的,它是在自己的作用域内给一个已经存在的类型一个别名,例如:

typedef unsigned char Byte;

而对于以下:

#define ptr int *
ptr a, b; //相当于int * a, b; 只是简单的宏替换

typedef int* ptr;
ptr a, b; //a, b 都为指向int的指针,typedef为int* 引入了一个别名

以上两个看似效果相同,但是差别很大,第一个只是简单的进行了替换,相当于语句int * a, b;表示定义了一个整型指针a和整型变量b。

另外注意到typedef语句最后有“;”,而define不加分号,因为#define不是语句,如果加了分号会连分号一块置换,对于上述例子1中的#define Max( a , b ) ( a > b ? a : b); 最后出现了分号,是因为在这里相当于宏定义了一个函数,在替换的时候也会一起替换,为了后面语句的可执行性,当然此处的;省略也不会发生错误,此外在主程序中Max(++a,b)之后不加;也是可以执行的。
2、文件包含

#include "file"
#include <file>

使用双引号,系统首先到当前目录下查找被包含的文件,如果没找到,再到系统指定的"包含文件目录"(由用户在配置环境时设置)去找;而使用尖括号时,系统会直接到系统指定的"包含文件目录"去查找。通常使用双引号比较保险。

3、条件编译

常量表达式:

#define CONDITION
#if CONDITION
  Printf(“Value of i:%d\n”, i);
#endif

#if 常量表达式,常量表达式为0时,预处理器会删除#if 和#endif中间的代码,并且#if 会把没有定义过的标准符视做为0,如果没有定义CONDITION, 则测试#if CONDITION会失败,但#if !CONDITION会成功,即如果常量表达式为一个没有定义的宏,它的值会被视作0。

取消已经定义的宏:

#if defined VALUE      //检验VALUE是否被定义
#undef VALUE           //如果已经被定义,解除定义
#define VALUE 77     //重新定义VALUE为77
#endif

检验是否被定义,还可以写成:

#ifndef VALUE               //如果VALUE没有被定义
#define VALUE 77            //定义VALUE 为77
#endif

防止重复inclue:

方式1:

#if !defined __MYHEADER_H__
#define __MYHEADER_H__
#endif

方式2:

#ifndef __MYHEADER_H__
#define __MYHEADER_H__
#endif

方式3:(#pragma once)

#pragma once

#ifndef的方式依赖于宏名字不能冲突,这不光可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件不会被不小心同时包含。当然缺点就是如果不同头文件的宏名不小心“撞车”,可能就会导致头文件明明存在,编译器却硬说找不到声明的状况

#pragma once则由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值