为什么我们需要在C++头文件中使用 "#if defined Identifier"?

Why we need to use "#if defined Identifier" in our C++ header files?

本文关键字:#if defined Identifier 文件 我们 C++ 为什么      更新时间:2023-10-16

我有一个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 

我想知道:

  1. 为什么我们需要在头文件中使用这几行
  2. 头文件是写入它们的最佳位置吗?还是图书馆是更好的文件?(我指的是这个.h文件使用的.cpp文件)
  3. 处理器在哪里搜索,以查看这些标识符(在我们的示例中为ARRAY_SIZEBUFFER_SIZE)是否已经定义?(在主程序中?如果是,在包括库之前的行中还是在整个主程序中。)

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

编辑:为了完整起见,我应该补充一点,还有编译器完成的预处理器定义,这些定义提供了什么处理器架构(32位x86处理器的__i386__,ARM处理器的__ARM__,64位x86处理器为__x86_64__),通常还提供了标识处理器模型更多细节的标志,例如这些扩展的__SSE____MMX__,操作系统(例如,__LINUX____WINDOWS__,它们可能也有允许您执行类似#if __WINDOWS__ > 500的操作的数值)以及什么编译器(如__MSVC____gnuc__),以及几十个其他或多或少神秘的编译器,但本问题中代码片段中的编译器不是其中之一。

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

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

我们为什么需要这个

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

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

我认为,如果你不知道为什么要使用哈希定义,那么最好使用cpp-const,因为你不太可能遇到问题,而且它们更容易维护。

也就是说,如果你想在不同的操作系统/不同的编译之间更改TABLE_SIZE,那么hash定义你的使用非常好。

  1. #if是预处理器指令,defined检查在本例中是否定义了某个宏ARRAY_SIZE。如果尚未定义ARRAY_SIZE,预处理器将剥离#If和#elif之间的代码,因此TABLE_SIZE将不引用未定义的宏ARRAY_SIZE(如果去掉if,代码将不会在宏定义中发出错误信号,但无论TABLE_SIZE出现在代码中的哪个位置,它都会被ARRAY_SIZE替换,并且最终会在其他地方出现一些相当神秘的错误消息)。

    因此,#if是进行编译时检查和根据代码所在的平台对代码进行更改编译或取决于某个参数。

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

  3. 预处理器一直搜索到它已经处理的内容,这意味着如果包含ARRAY_SIZE定义的文件已经包含在您的头之前,那么ARRAY_SIZE将在该点定义,即,正如您在包含库之前的行中所说的那样。

其基本逻辑是,对于TABLE_SIZE:

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

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

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