为啥我们需要在 C++ 头文件中使用“#if defined Identifier”?

Posted

技术标签:

【中文标题】为啥我们需要在 C++ 头文件中使用“#if defined Identifier”?【英文标题】:Why we need to use "#if defined Identifier" in our C++ header files?为什么我们需要在 C++ 头文件中使用“#if defined Identifier”? 【发布时间】:2015-04-20 07:28:07 【问题描述】:

我有一个C++ 库头文件,它以以下代码行开头:

#if defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
#elif !defined BUFFER_SIZE
#define TABLE_SIZE 128
#else
#define TABLE_SIZE BUFFER_SIZE
#endif 

我想知道:

    为什么我们需要在头文件中使用这一行? 头文件是写它们的最佳位置吗?或者图书馆是一个更好的文件? (我的意思是这个.h 文件使用的.cpp 文件) 处理器在哪里搜索,看看这个标识符(ARRAY_SIZEBUFFER_SIZE,在我们的例子中)是否已经定义? (在主程序中?如果是,在包含库之前的行中还是在整个主程序中?)

【问题讨论】:

参考gcc.gnu.org/onlinedocs/cpp/Defined.html 至于为什么这是必要的:您发布的行将 TABLE_SIZE 设置为某个值,为什么这是必要的取决于您使用的头文件 /library。至于您的其他问题,我建议您阅读有关 c/c++ 中的预处理器宏的书或教程 @MikeMB 这是否检查TABLE_SIZE 是否在我的main.cpp 文件中定义?主文件中的声明点(在#include 这个库之前或之后)在此方法的响应中是否重要(defined())? 【参考方案1】:

预处理器值由发送到编译器的命令行上的-D 选项定义,或在前面的代码中由#define 定义(包括您发布的代码部分之前包含的头文件)。 defined 预处理器功能仅在给定名称已定义时返回 true(或解释为 true),或在未定义时返回 false。请注意,给定的名称必须由#define 定义,例如const int x = 4; 不适用于#if defined(x)。编辑:请注意defined 从不“向前看”。把它想象成老式的纸带或穿孔卡片,你只能知道你已经看到了什么,而不是“未来”的磁带/卡片中的内容。

编辑:为了完整起见,我应该补充一点,还有编译器完成的预处理器定义 - 这些提供诸如处理器架构之类的东西(__i386__ 用于 32 位 x86 处理器,__ARM__ 用于 ARM 处理器, __x86_64__ 用于 64 位 x86 处理器 - 并且经常标记以识别处理器型号的更多详细信息,例如 __SSE____MMX__ 用于这些扩展),操作系统(__LINUX____WINDOWS__ 例如 -这些也可能具有允许您执行诸如#if __WINDOWS__ > 500) 之类的数字值以及诸如__MSVC____gnuc__ 之类的编译器,以及其他几十个或多或少神秘的值-但代码中的那些这个问题中的片段不是其中之一。

如果不知道您正在查看哪个头文件,就无法解释为什么需要这样做。

【讨论】:

.... 或通过前面代码中的#define(包括您发布的代码部分之前包含的头文件) 如果我这样做,defined() 方法会返回什么在main.cpp 文件中定义我的标识符(我将这个库包含在其中)?定义是否重要?(在#include "theLib.h" 之前的行或之后的行中) @Jean:我进行了编辑以澄清 - 它只会“向后”看,而不是“向前”。因此,在此之前看到的任何内容 [并将其视为 #include 的意思是“复制命名文件的全部内容并将其粘贴到而不是 #include] 这是主要问题:我的电脑中有一个main.cpp、一个lib.cpp 和一个lib.hlib.h 包含在 main.cpp 文件中。问题是当我在 Windows 中编译这个程序时,我的 main.cpp 文件中间的行 defined (TABLE_SIZE BUFFER_SIZE) 返回 true,但是当我在 Linux 中使用 G++ 编译这个相同的程序时,这一行返回 @987654355 @!文件之间没有区别,但结果不同。你有什么想法吗? 请问您也可以将之前的评论添加到您的答案中吗? 如果不知道在这些头文件之前包含哪些头文件,我不知道可以定义什么。在 lib.h 之前的标题中搜索 TABLE_SIZEBUFFER_SIZE 将是关键。这听起来不像系统标头提供的东西,但你永远不会真正知道。不确定您要添加我评论的哪一部分 - 我已经添加了一个编辑来解释“它不向前看,只向后看”部分。我认为头文件的工作原理不在这个问题范围内(当然,这对于理解编译过程至关重要)【参考方案2】:

#if / #elif / #else 用于在预处理器阶段实现简单的逻辑。在此示例中,它们用于根据是否定义了其他一些值来正确设置 TABLE_SIZE 的值。

我们为什么需要这个?

一些值依赖于其他一些值,如本例所示。这些其他值可能根本没有定义——这就是为什么我们也需要考虑到这一点。因为TABLE_SIZEARRAY_SIZE等都是defined,编译时改了,不修改代码。这是使用defines 而不是常规变量的优势之一。使用gcc时,可以这样设置defines:

gcc -o test test.c -DBUFFER_SIZE=128

此示例将BUFFER_SIZE 的值设置为128(除非它再次在源代码中重新定义)。

此外,它还可用于创建可移植代码,其中一些选项(甚至部分代码)因各种操作系统或架构而异。

预处理器指令应该放在哪里?

通常,它们被放置在头文件中。这种方式更容易管理它们 - 您在所有文件中设置了相同的宏,其中包括特定的标题。无论如何,这只是一个约定。从技术上讲,预处理器指令可以放置在代码中的任何位置(但在使用它们之前),无论是在头文件中还是在源文件中。

宏的作用域是什么?

Proprocessor 仅执行简单的文本替换(以及一些更复杂事物的宏扩展,例如可变长度参数)。宏可以在定义后使用,直到未定义为止。考虑这个例子:

/* Here, TEST is not defined. */

#define TEST 123

/* TEST is defined here, and can be used. */

#undef TEST

/* TEST is undefined again, and cannot be used. */

编译库或应用程序时,可以创建多个翻译单元。其中这些作为源文件,处理了所有#include 指令。考虑这个例子:

header1.h:

#define TEST 123

header2.h:

#undef TEST
#define ABC 456

test.c:

#include "header1.h" /* Defines TEST */

void test1() 
    printf("%d", TEST); /* OK, TEST has been defined in `header1.h`, which is included above. */


#include "header2.h" /* Undefines TEST, defines ABC */

void test2() 
    printf("%d", ABC); /* OK, ABC has been defined in `header2.h`, which is included above. */
    /* printf("%d", TEST); */ /* Can't do that - TEST has been undefined! */

有时检查预处理器的功能可能很有用。对于gcc,那就是:

gcc -E test.c -o test.preprocessed

【讨论】:

这是主要问题:我的电脑中有一个main.cpp、一个lib.cpp 和一个lib.hlib.h 包含在 main.cpp 文件中。问题是当我在 Windows 中编译这个程序时,main.cpp 文件中间的行 defined (TABLE_SIZE BUFFER_SIZE) 返回 true,但是当我在 Linux 中使用 G++ 编译这个相同的程序时,这一行返回 @987654353 @!文件之间没有区别,但结果不同。你有什么想法吗? 我不确定将defined 与多个参数一起使用是否有效(我从未见过这样使用它)。你试过检查#defined (TABLE_SIZE) && defined (BUFFER_SIZE)吗?除此之外,也许它取决于操作系统。这些文件是开源的吗?你能给我们他们的链接吗? 我会尝试并在源代码中进行一些编辑,如果问题没有解决,我会将其作为新问题发布。 (关于依赖于操作系统的问题,我已经考虑过了。我还要检查相等的编译器吗?)【参考方案3】:

我认为,如果您不知道为什么要使用散列定义,最好使用 cpp const,因为您不太可能遇到问题,而且它们更易于维护。

也就是说,如果您希望能够在不同的操作系统/不同的编译之间更改您的 TABLE_SIZE,那么哈希定义了您的使用非常好。

【讨论】:

【参考方案4】:

    #if 是一个预处理指令,defined 检查是否在这种情况下定义了某个宏 ARRAY_SIZE。如果ARRAY_SIZE 尚未定义,#if 和#elif 之间的代码将被预处理器剥离,因此TABLE_SIZE 将不会引用未定义的宏ARRAY_SIZE(如果您将 if 排除在外,您的代码将不会在宏定义处发出错误信号,但 TABLE_SIZE 将被替换为 ARRAY_SIZE,无论它出现在您的代码中,您最终会在其他地方收到一些相当神秘的错误消息。

    因此#if 是一个有用的结构,可以进行编译时检查和 根据代码所在的平台更改代码 编译或取决于某些参数。

    声明通常在头文件 (.h) 中,而定义在 .cpp 文件中。因此#if主要用在头文件中,但可以出现在任何地方

    预处理器搜索直到它已处理的内容,这意味着如果包含ARRAY_SIZE 定义的文件已包含在您的标题之前,则ARRAY_SIZE 将在该点定义,即如您在行中所说那是在包含库之前。

【讨论】:

【参考方案5】:

基本逻辑是,对于TABLE_SIZE

如果定义了ARRAY_SIZE,请使用它。 否则,如果定义了BUFFER_SIZE,则使用它。 否则,请使用128

至于你为什么需要它,这完全取决于你正在查看的代码。

它们在头文件中是否更好取决于您将如何使用它。通常,如果您想在许多不同的源文件中使用它,您会在头文件中定义类似的内容。如果仅在 一个 源文件中需要它,那么您可以在此处轻松完成。

【讨论】:

以上是关于为啥我们需要在 C++ 头文件中使用“#if defined Identifier”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在头文件中声明并在文件中定义会给出多个定义错误?

C++程序在使用GDAL时为啥必须包含头文件:gdal_priv.h?gdal_priv.h包含的是啥文件?

devc++格式为啥那么丑

C++,大佬们看下这里报错是为啥?可是这代码能AC啊?!

尽管注释掉了所需的头文件,为啥这个 C++ 程序仍能编译和运行? [复制]

为啥我们需要 libstdc++.so?