欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

条件编译#if、##ifdef、#ifndef 及#define和const区别

程序员文章站 2024-03-23 11:49:22
...


1、#ifndef的作用

  • 1、头文件中使用,防止头文件被多重调用。
    被重复引用是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成。
    比如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include “a.h” 和#include "c.h"此时就会造成c.h重复引用。

  • 2、作为测试使用,省去注释代码的麻烦。
    VS/VC 有两种编译模式,Debug 和 Release。
    使用 Debug 模式,这样便于程序的调试;而最终发布的程序,使用 Release 模式,这样编译器会进行很多优化,提高程序运行效率,删除冗余信息。

#include <stdio.h>
#include <stdlib.h>
int main(){
    #ifdef _DEBUG
        printf("正在使用 Debug 模式编译程序...\n");
    #else
        printf("正在使用 Release 模式编译程序...\n");
    #endif

    system("pause");
    return 0;
}
  • 3、作为不同角色或者场景的判断使用。
    如跨平台,在 Windows 和 Linux 下都能运行程序示例
#include <stdio.h>
int main(){
    #if _WIN32
        printf("This is Windows!\n");
    #else
        printf("Unknown platform!\n");
    #endif
   
    #if __linux__
        printf("This is Linux!\n");
    #endif

    return 0;
}

2、头文件被重复引用会怎么样?

  • 影响编译过程效率
    对于大工程,头文件重复被引用会导致编译工作量加大,效率低下。
  • 引起错误
    在头文件中定义了全局变量(虽然这种方式不被推荐,但确实是C规范允许的)这种会引起重复定义。
    如下示例,在vc中链接时就出现了变量 i 重复定义的错误,而在c中成功编译。
    条件编译#if、##ifdef、#ifndef 及#define和const区别
#ifndef AAA
#define AAA
...
int i;
...
#endif

问题分析:
(1).当你第一个使用这个头的.cpp文件生成.obj的时候,int i 在里面定义了当另外一个使用这个的.cpp再次[单独]生成.obj的时候,int i 又被定义然后两个obj被另外一个.cpp也include 这个头的,连接在一起,就会出现重复定义.
(2).把源程序文件扩展名改成.c后,VC按照C语言的语法对源程序进行编译,而不是C++。在C语言中,若是遇到多个int i,则自动认为其中一个是定义,其他的是声明。
(3).C语言和C++语言连接结果不同,可能(猜测)时在进行编译的时候,C++语言将全局
变量默认为强符号,所以连接出错。C语言则依照是否初始化进行强弱的判断的。(参考)

解决方案:
1).把源程序文件扩展名改成.c。

<x.c>
int i;

(2).推荐解决方案:
.h中只声明 extern int i;在.cpp中定义

<x.h>
#ifndef __X_H__
#define __X_H__
extern int i;
#endif //__X_H__

3、头文件中都要加入#ifndef/#define/#endif ?

  • 不是,但是不管怎样,用#ifndef xxx #define xxx #endif或者其他方式避免头文件重复包含,只有好处没有坏处。个人觉得培养一个好的编程习惯是学习编程的一个重要分支。

4、#ifndef/#define/#endif 具体含义

  • #ifndef A_H意思是"if not define a.h" 如果不存在a.h
    接着的语句应该#define A_H 就引入a.h
    最后一句应该写#endif 否则不需要引入

#ifdef identifier
#ifndef identifier

这些指令等效于:

#if defined identifier
#if !defined identifier

5、#ifndef/#define/#endif 示例

#ifndef _STDIO_H_   //如:头文件stdio.h,命名规则一般是头文件名全大写,前后加下划线
#define _STDIO_H_ 
...... 
#endif 

6、 #if,#ifdef, #ifndef 的比较

  • #if 后面跟的是“整型常量表达式”,而 #ifdef 和 #ifndef 后面跟的只能是一个宏名。
  • #if 命令要求判断条件为“整型常量表达式”,也就是说,表达式中不能包含变量,而且结果必须是整数;而 if 后面的表达式没有限制,只要符合语法就行
#include <stdio.h>
#define NUM1 10
#define NUM2 20
int main(){
    #if (defined NUM1 && defined NUM2)
        //代码A
        printf("NUM1: %d, NUM2: %d\n", NUM1, NUM2);
    #else
        //代码B
        printf("Error\n");
    #endif
    return 0;
}

7、const 与 #define的比较

  • 宏定义 #define
    指令都是以#开始的,我们来看一下简单的宏定义(对象式宏)
  #define MAX(x,y) ((x)>(y)?(x):(y))
  #define ISLEAP(y) ((y)%4==0&&(y)%100!=0||(y)%400==0)
  #define ISSMALL(m) ((m)==4||(m)==6||(m)==9||(m)==11)
  #define NORMAL(m) (ISSMALL(m)?30:31)
  #define DAYS(y,m) ((m)==2?28+ISLEAP(y):NORMAL(m))

用于调试的示例:

#if (comdition)
  {//语句##;}
#endif
  //如果(comdition)为真, 也就是逻辑1的话,编译下面的语句,如果(comdition)为假,即逻辑0,则不编译下面的语句。例子如下:
  #define DEBUG 1
  #if DEBUG
  Printf(“Value of i:%d\n”, i);
  Printf(“Value of j:%d\n”, j);
  #endif
  • const 与 #define比较
  • 角度1:定义常量:
    const 定义的常数是变量 也带类型, #define 定义的只是个常数 不带类型。
  • 角度2:空间占用
    宏定义不分配内存,变量定义分配内存,
    const常量会在内存中分配(可以是堆中也可以是栈中)
#define PI 3.14     //预处理后 占用代码段空间
const float PI=3.14;    //本质上还是一个 float,占用数据段空间
  • 角度3:起作用的阶段
    define是在编译的预处理阶段起作用,而const是在 编译、运行的时候起作用
  • 角度4:起作用的方式
    define只是简单的字符串替换,没有类型检查。而const有对应的数据类型,是要进行判断的,可以避免一些低级的错误
#define N 2+3 //我们预想的N值是5,我们这样使用N
double a = N/2;  //我们预想的a的值是2.5,可实际上a的值是3.5
  • 角度5:代码调试
    const常量可以进行调试的,define是不能进行调试的,因为在预编译阶段就已经替换掉
  • 角度6:重定义
    const不能重定义,而#define可以通过#undef取消某个符号的定义,再重新定义
  • 角度7:防止头文件重复引用
    define可以用来防止头文件重复引用
  • 角度8:复杂功能
    使用define会使得代码看起来非常简单,而const无法实现该功能
    例如,MFC在实现六大核心机制中,大量使用了define
    1、MFC程序的初始化
    2、运行时类型识别(RTTI)
    3、动态创建
    4、永久保存
    5、消息映射
    6、消息传递

比如,在实现RTTI功能的时候,定义了如下宏,代码如下:

#define DECLARE_DYNCREATE(class_name) \

    DECLARE_DYNAMIC(class_name)\

    static CObject* PASCALCreateObject();

感谢博主分享

相关标签: c++ c++