为什么人们使用#ifdef进行function标记测试?

人们建议#ifdef进行大范围的条件编译 。 搜索#ifdef证实它的使用是普遍的。

然而#ifdef NAME (或等效的#if defined(NAME)和相关的#ifndef NAME (和#if !defined(NAME) )有一个严重的缺陷:

header.h

 #ifndef IS_SPECIAL #error You're not special enough #endif 

source.cpp

 #include "header.h" 

gcc -DIS_SPECIAL source.cpp

显然,将会通过

source1.cpp

 #define IS_SPECIAL 1 #include "header.h" 

但是,也一样

source0.cpp

 #define IS_SPECIAL 0 #include "header.h" 

这是完全错误的事情。 一些C ++编译器,传递一个以C模式处理的文件(由于扩展或命令行选项)有效地执行#define __cplusplus 0 。 我看到事情破裂了

 #ifdef __cplusplus extern "C" { #endif /* ... */ #ifdef __cplusplus } #endif 

C模式下处理,其中extern "C"是无效语法,因为__cplusplus实际上自动定义为0

另一方面,这适用于所有编译器:

 #if __cplusplus extern "C" { #endif /* ... */ #if __cplusplus } #endif 

为什么人们在这种情况下仍然使用#ifdef ? 他们是否完全没有意识到#if在未定义的名称上运行得非常好? 或者#if vs #ifdef是否存在条件编译的实际缺点?


显然, #ifdef确实有有效用途,例如为可配置参数提供默认值:

 #ifndef MAX_FILES #define MAX_FILES 64 #endif 

我只讨论旗帜测试的情况。

为什么人们在这种情况下仍然使用#ifdef?

个人意见:从命令行控制起来稍微容易一些。 我更喜欢-DOPTION over -DOPTION=1

此外,名称的存在显然是二进制的。 我不必处理{0,非零,未定义}。

他们是否完全没有意识到#if在未定义的名称上运行得非常好?

我不知道。 这有什么语义? 假定未定义的名称是0吗? 我是否想要向那些几乎不了解预处理器的人解释一下?

或者#if vs #ifdef是否存在条件编译的实际缺点?

对我来说,名称存在的#ifdef/#ifndef的二元性质是一个明确的好处。 另外,我对这两种构造的主要用法是包含警戒。 使用#ifndef这种模式最干净。

我不能说为什么一般人更喜欢#ifdef不是#if ,但我至少可以说我为什么这样做。 基于刚才的内省(自从你问过 – 我以前从未明确考虑过它),有两个原因:

1)我更喜欢我的宏(我试图谨慎使用)尽可能地使用最直接的语义,并相应地尽可能地“免费”。 我假设宏,如果它们有任何类型,都是“类型自由函数”(注意:这里我更喜欢模板,但有时候一切……)或基本上只是布尔标志。 因此,即使为宏指定值1 ,也会为我拉伸它。 (例如,如果你有#define _cplusplus 2 ,它应该是什么意思?它应该以不同于1 ?)

2)最后一点关于它们是“标志”的事实是我主要将这些用于我在命令行(或在IDE中)指定的条件编译标志 。 实际上,在我的软件团队中,当我们编写C ++时,基本上“禁止”将宏用于其他任何事情。 即使在条件编译的情况下,如果我们可以通过其他方式解决问题(例如通过模块化),我们也会尝试避免它们。

这两个原因都与同样的基本假设有关,即尽可能避免使用宏(在C ++中),因此不需要类型的复杂性或不透明的语义。 如果你没有做出这个假设(并且在用C语言编程时它不太常见,我知道),那么这会改变一些事情,我想你对#if的观点可能会有更多的影响。