【问题标题】:Conditional compilation in C and DelphiC 和 Delphi 中的条件编译
【发布时间】:2012-02-14 19:11:23
【问题描述】:

下一个模式在 C 代码中很常见:

#ifndef SOMETHING
#define SOMETHING
#endif

Delphi 代码中也可以使用该模式:

{$IFNDEF SOMETHING}
{$DEFINE SOMETHING}
{$ENDIF}

但这并不常见——我从来没有见过。如果 Delphi 代码需要条件定义,它只定义它而不使用IFNDEF 检查。

为什么会这样? C和Delphi的条件编译有什么区别,前者需要ifndef检查,后者不需要?

【问题讨论】:

  • 我相信 Delphi 的代码库比 C 小得多。

标签: c delphi conditional-compilation ifndef


【解决方案1】:

这是因为这在 C 中不仅常见而且是强制性的:

#include <something.h>

虽然这在 Delphi 中很少使用。而使用的时候,其实是用来设置那些{$DEFINE}的:

{$INCLUDE 'something.inc'}

这很重要,因为 DEFINES 仅在编译一个对象时有效(可能是 .PAS 文件或 .C 文件)。 Delphi 使用uses 子句包含其他单元,而C 使用include 包含它的标题。在C 中,标头本身可能包含其他标头。您询问的模式用于防止递归地重新包含相同的标头。

为了让母题一目了然,这里有一个可以在 C 中使用的示例,以及在 Delphi 中的等价物。假设我们设置了 3 个文件,其中A 需要同时包含BC,而B 只需包含C。 “C”文件如下所示:

// ----------------------- A.h
#ifndef A
#define A

#include "B.h"
#include "C.h"

// Stuff that goes in A

#endif

// ------------------------ B.h
#ifndef B
#define B

#include "C.h"

// Stuff that goes in B

#endif

// ----------------------- C.h
#ifndef C
#define C

// Stuff that goes in C

#endif

如果没有C.h 中的条件定义,C.h 文件最终会在A.h 中包含两次。这就是代码在 Delphi 中的样子:

// --------------------- A.pas
unit A;

interface

uses B, C;

implementation

end.

// --------------------- B.pas
unit B

interface

uses C;

implementation

end.

// --------------------- C.pas

unit C

interface

implementation

end.

Delphi/Pascal 版本不需要保护“C”不被包含在“A”中,因为它不使用{$INCLUDE} 来实现这个目标,它使用uses 语句。编译器将从B.dcu 文件和C.dcu 文件中获取导出的符号,而不会包含来自C.dcu 的符号两次。


在 C 代码中看到更多预编译器指令的其他原因:

  • 预编译器比 Delphi 的要强大得多。 Delphi 代码中的{$DEFINE} 仅处理条件编译,而 C 变体可用于条件编译和作为单词替换的一种形式。
  • 对标头强制使用#include 意味着您可以拥有一个定义宏的标头。或者,您可以通过在实际 #include &lt;header.h&gt; 之前指定一些 #define 语句来配置标头

【讨论】:

  • 彻底的答案,但你的例子不是 :-) 如果你添加从 C 到 A 的依赖,动机会更有意义。
  • 很好地解释了如何使用#ifndef 防止标头递归,但这似乎不是C 中#ifndef 的唯一用途。我见过很多次#ifndef 没有后续#include 使用.
  • 从 C 到 A 的依赖可能更有趣,因为没有 IFDEF 会导致无限递归循环。但是我会遇到 Delphi 示例的问题,因为 Delphi 不允许循环依赖。而且我可能会遇到设计纯粹主义者的麻烦,因为循环依赖确实是一个糟糕的设计。
  • @Serg,在我的示例中,C.h 文件没有任何 INCLUDE,但如果没有 IFNDEF 保护,它将在 A.h 中包含两次。
  • 我确信#ifndef 不仅用于防止标头递归,尽管这可能是#ifndef 最重要的用途。我也相信#ifndef 在 C 中的重要性与头文件(*.h)的广泛使用有关,而它们的 Delphi 类似物(*.inc 文件)很少使用且用途非常有限。
【解决方案2】:

这种模式在 C 代码中并不“常见”(.c 或 .cpp 源文件)。在 C/C++ headers (.h) 文件中很常见:

#ifndef SOMETHING
#define SOMETHING
#endif

原因是为了防止 SAME 标头无意中在同一个翻译单元中包含 MULTIPLE TIMES。

例如,假设模块“a.c”使用标题“b.h”。和“b.h”#include 的“c.h”。这意味着“a.c”明确使用“b”,也隐含使用“c”。到目前为止,一切顺利。

现在假设“c.h”使用“b.h”。 “#ifndef/#define/#endif”的东西防止“b”第二次被#include'd(一次在“a”中被“a”,第二次在“a”中来自“c”)。

这在 Delphi 中都是不必要的。 Delphi "$ifdef" 仅用于条件编译; Delphi“单元”处理潜在的递归和/或循环依赖。

【讨论】:

  • 如果不是代码,标题中的内容是什么?对我来说,C 头文件仍然是 C 代码。
  • 不要对“代码”这个词吹毛求疵。重要的一点是“接口”(.h)和“实现”.cpp”之间的区别。C/C++ 头文件使用#ifndef 预处理器约定来减轻递归/循环定义。由于 Pascal/Delphi 使用单位的方式,这不需要使用预处理器。
  • PS:关于 Pascal/Delphi(以及 Delphi 和其他语言)的真正优点之一是“接口”与“实现”的清晰、明确的分离。
  • 我不是在狡辩,接口和实现之间是有区别的,但我认为这两者都属于“代码”。
猜你喜欢
  • 1970-01-01
  • 2016-06-12
  • 1970-01-01
  • 2014-11-26
  • 2010-12-30
  • 2018-07-13
  • 1970-01-01
  • 2016-12-21
  • 1970-01-01
相关资源
最近更新 更多