为啥我们需要在 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_SIZE
和BUFFER_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.h
。 lib.h
包含在 main.cpp
文件中。问题是当我在 Windows 中编译这个程序时,我的 main.cpp
文件中间的行 defined (TABLE_SIZE BUFFER_SIZE)
返回 true
,但是当我在 Linux 中使用 G++
编译这个相同的程序时,这一行返回 @987654355 @!文件之间没有区别,但结果不同。你有什么想法吗?
请问您也可以将之前的评论添加到您的答案中吗?
如果不知道在这些头文件之前包含哪些头文件,我不知道可以定义什么。在 lib.h
之前的标题中搜索 TABLE_SIZE
或 BUFFER_SIZE
将是关键。这听起来不像系统标头提供的东西,但你永远不会真正知道。不确定您要添加我评论的哪一部分 - 我已经添加了一个编辑来解释“它不向前看,只向后看”部分。我认为头文件的工作原理不在这个问题范围内(当然,这对于理解编译过程至关重要)【参考方案2】:
#if
/ #elif
/ #else
用于在预处理器阶段实现简单的逻辑。在此示例中,它们用于根据是否定义了其他一些值来正确设置 TABLE_SIZE
的值。
我们为什么需要这个?
一些值依赖于其他一些值,如本例所示。这些其他值可能根本没有定义——这就是为什么我们也需要考虑到这一点。因为TABLE_SIZE
、ARRAY_SIZE
等都是define
d,编译时改了,不修改代码。这是使用define
s 而不是常规变量的优势之一。使用gcc
时,可以这样设置define
s:
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.h
。 lib.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包含的是啥文件?