#ifndef 仅适用于“声明”部分?

Posted

技术标签:

【中文标题】#ifndef 仅适用于“声明”部分?【英文标题】:#ifndef works only for "declaration" parts? 【发布时间】:2016-08-01 12:50:34 【问题描述】:

我有这样的 IGlobal.h 文件:

#ifndef _IGLOBALS_
#define _IGLOBALS_

#define _USE_MATH_DEFINES

#define PLUG_NUM_CHANNELS 2
#define PLUG_NUM_PRESETS 1
#define PLUG_FPS 100
#define PLUG_PPQ 96
#define KNOB_NUM_FRAMES 128
#define PIANOROLL_MIN_NOTES 2
#define PIANOROLL_MAX_NOTES 8
#define PIANOROLL_MIN_VELOCITY 40
#define PIANOROLL_MAX_VELOCITY 127
#define PIANOROLL_PATTERN_LENGTH PLUG_PPQ * 4
#define LFO_NUM_SAMPLES 882
#define WAVEPLAYER_WIDTH 441
#define WAVEPLAYER_HEIGHT 136
#define WAVEPLAYER_PATH_LENGTH 256
#define ENVELOPE_MIN_VALUE 0.0001

#include <algorithm>
#include <random>
#include <chrono>
#include <math.h>
#include <cmath>
#include "IPlug_include_in_plug_hdr.h"

using namespace std;

// bitmaps
IBitmap gBitmapKnobGeneral;

#endif // !_IGLOBALS_ 

我经常从项目中的 .h/.cpp 文件中包含它。 IBitmap 是一个结构体。

当我构建(编译)时,它说:

LNK1169 one or more multiply defined symbols found
LNK2005 "struct IBitmap gBitmapKnobGeneral" ?gBitmapKnobGeneral@@3UIBitmap@@A) already defined in ICustomControls.obj

事实上,我每次包含 IGlobal.h 时都会得到它。 #ifndef 不应该放弃这个问题吗?或者编译器自动只为declarations 而不是definitions

【问题讨论】:

#endif 在哪里?或者这只是文件的开头?请您添加整个错误消息吗? IPlug_include_in_plug_hdr.h 包含保护吗?顺便说一句,我认为#define PIANOROLL_PATTERN_LENGTH PLUG_PPQ * 4 可能会导致一些糟糕的预处理器问题。最好加上括号。 一般来说,你会在头文件中声明一个变量为extern,然后在一个cpp文件中定义它。 @RetiredNinja:是的,通常!但是如果我不这样做,编译器会自动执行吗? 为什么你有#include &lt;math.h&gt;,然后是#include &lt;cmath&gt;?检查Math constants ***.com/questions/8020113/c-include-guards 【参考方案1】:

回答 #ifndef 适用于整个文件的问题。

问题是当你写 IBitmap gBitmapKnobGeneral;您将在每个包含标头的源文件中创建一个实例。 我假设您要做的是创建一个全局实例。 在这种情况下,你会想写。

  extern IBitmap gBitmapKnobGeneral;

在 IGlobal.h 文件中,然后在其中一个源文件中创建一个实例,例如 IGlobal.cpp 与

 IBitmap gBitmapKnobGeneral;

【讨论】:

当然。但是由于有 ifndef,它不应该重新编译这个文件。相反,由于有“定义”,它确实如此。智能编译器? 我想最好的说法是,对于您编译的每个源文件(单元),定义都被重置。因此,当您编译第二个源文件时,即使您有 ifndef,它也会创建一个名为 gBitmapKnobGeneral 的全局变量。然后,当链接器尝试合并从两个源文件创建的 obj 文件时,就会出现问题。【参考方案2】:

在 C++ 中,头文件没有被编译,它们被“粘贴”到源 (cpp) 文件中。每个源文件都有自己独立的一组#defines - 如果你把它放到一个 cpp 文件中

#define A 1

这个变成另一个

#ifndef A
#error A is not defined
#endif

您收到错误“A 未定义”。在您的情况下,每个包含标头 IGlobal.h 的源文件都会通过您的标头一次(#ifdef 保护阻止它多次解析)并定义 IBitmap gBitmapKnobGeneral; 您的程序最终会得到变量的多个定义。要了解如何解决问题,请参阅其他答案和 cmets。

【讨论】:

"您的程序以变量的多个定义结束。"如果(如您所说)它包含一次标题,为什么会这样?不清楚。【参考方案3】:

#ifndef 检查之前是否定义了标识符,如果没有,则它包含#ifndef 和对应的#endif/#else 之间的代码。 如果您之前定义了此标识符,那么代码在预处理后将不存在,因此我将检查问题是否不在包含的文件中或 IGlobal.h 中定义的符号中 - 可能是这些“常量”之一(例如 PLUG_NUM_CHANNELS)也在别处。

编辑: 看看#pragma once - 在我看来,它是更干净的解决方案,可以省去很多标识符的麻烦。

【讨论】:

或者在定义包含保护之前使用#undef _IGLOBALS_ 也许,但我们不知道问题出在哪里——它可能在其他地方(也许是IPlug_include_in_plug_hdr.h)。话虽如此,我会将全局变量提取到单独的文件和命名空间中——我认为这将是更清洁的解决方案。 不。它说LNK2005 "struct IBitmap gBitmapKnobGeneral" (?gBitmapKnobGeneral@@3UIBitmap@@A) already defined in ICustomControls.obj 所以我认为你应该使用重写行 IBitmap gBitmapKnobGeneral;extern struct IBitmap gBitmapKnobGeneral; 我知道!但为什么!? #ifndef 应该忽略它,因为它不再执行了

以上是关于#ifndef 仅适用于“声明”部分?的主要内容,如果未能解决你的问题,请参考以下文章

ifndef系列

#ifndef, #define, #endif的作用

#ifndef #define #endif 的用法

#pragma once与#ifndef的区别

#pragma once与 #ifndef的区别

#pragma once