11

我从事 Java 和 VB.Net 程序员大约 4 年,从事 C# 程序员大约 6 个月。我还使用了许多动态语言,例如 Perl、Python、PHP 和 JavaScript。

我从来不需要预处理器。

我的问题是:为什么您会在 C、C++ 和 Objective-C 中看到如此广泛的预处理器使用,但很少(或从未)在 Java、C# 或 Scala 等语言中看到它?

4

12 回答 12

14

我不知道 Objective-C,所以我的答案是对比 C 和 C++ 中预处理器的使用。

由于几个原因,预处理器最初对于 C 来说是必需的。如果我没记错的话,最初 C 没有常​​量,所以#define需要避免幻数。在 1999 年之前,C 没有内联函数,因此再次#define用于创建宏或“伪函数”以节省函数调用的开销,同时保持代码结构化。C 也没有运行时或编译时多态性,因此#ifdef条件编译需要 s。编译器通常不够聪明,无法优化无法访问的代码,因此再次#ifdef使用 s 插入调试或诊断代码。

在 C++ 中使用预处理器是对 C 的倒退,并且通常不被接受。常量、内联函数和模板等语言特性可用于在 C 中使用预处理器的大多数情况下。

在 C++ 中使用预处理器是可以接受甚至是必要的少数情况包括对头文件的保护,以防止相同的头被多次包含,#ifdef __cplusplus在 C 和 C++ 中使用相同的头,__FILE__以及__LINE__记录,以及其他一些。

预处理器也经常用于特定于平台的定义,尽管Stephen Dewhurst 的C++ Gotchas建议为特定于平台的定义使用单独的包含目录,并在每个平台的单独构建配置中使用它们。

于 2009-08-12T22:19:51.343 回答
11

您看不到 Java、C# 或 Scala 中使用的预处理器的原因是这些语言显然没有预处理器。

C 预处理器的常见用途之一是帮助提供特定于平台的代码。由于 C(我在这里包括 C++ 和 Objective-C)是一种需要直接与操作系统交互的低级语言,因此在可移植代码中,必须为不同的操作系统编译不同的代码部分。您可以在诸如zlib之类的成熟、高度可移植的代码库中找到此类事情的大量示例。

举一个简单的例子,要关闭一个网络套接字,必须做这样的事情(在某种程度上,这当然可以包装在一个函数中,但它必须存在于某个地方):

#ifdef WIN32
    closesocket(s);
#else
    close(s);
#endif

在 VM 上运行的较新语言不需要特定于平台的不同代码部分,并且可以针对单个可移植标准库编写。

预处理器还提供了一种在 C 中定义常量的方法,这些常量由较新语言中的其他更好的语言特性提供。

在 The Design and Evolution of C++ 中,Bjarne Stroustrup 表示他想去除 C++ 中对预处理器的依赖,但没有成功。

于 2009-08-12T22:15:04.957 回答
9

每种语言都需要一种单独编译的机制。理想情况下,语言将接口与实现区分开来,一个模块只依赖于它导出的模块的接口。(例如,参见 Ada、Clu、Modula 等。)

C 没有用于接口或实现的语言结构。因为不同的 .c 文件共享接口的单一视图是至关重要的,所以编程学科演变为将声明(即接口)放入 .h 文件并使用文本包含 ( #include) 共享这些声明/接口。原则上,#define可以#ifdef省略,但#include不能。

如今,语言设计者认识到文本包含不是运行铁路的方法,因此语言倾向于运行于单独编译的接口(Ada、Modula、OCaml)、编译器生成的接口(Haskell)或保证接口一致性的动态系统(Java,Smalltalk)。有了这样的机制,就不需要预处理器了,而且有很多理由不需要预处理器(想想源代码分析调试)。

于 2009-08-12T23:21:38.553 回答
4

因为这些语言的设计和目的是不一样的。

C 在构建时将预处理器视为一个强大的工具,它用于实现非常基本的东西(例如包含保护),开发人员能够使用它通过宏优化代码或选择性地包含/排除某些块代码除了其他的东西。C++ 继承了 C 的大部分习惯用法,宏不再用于提高速度(因为引入了内联),但它仍然用于很多事情,请参阅帖子What are preprocessor macros good for?

于 2009-08-12T22:18:19.787 回答
3

因为 Gosling 和 Heilsberg 都了解滥用预处理所带来的风险和技术债务!

于 2009-08-12T22:17:29.400 回答
3

预处理在 Java 世界中非常非常普遍。它用于弥补语言缺乏足够的内置抽象设施,否则会导致无休止的复制和粘贴样板代码。

许多人没有意识到这是真的的原因是,在 Java 世界中它被称为“代码生成”而不是“预处理”,因为“预处理器”听起来像讨厌的旧 C,而“代码生成”听起来像一个专业工具高效成熟的企业流程。但是,它仍然是预处理,即使您必须为不兼容的非标准专有工具支付巨额费用,而不仅仅是使用语言内置的工具。

于 2010-02-24T23:12:41.630 回答
2

我不同意在现代语言中不需要 cpp 的共识。我有很多情况下,我有同一个程序的 3 个稍微不同的版本,我希望能够为每个版本进行大量更改。使用 CPP,我可以将它们全部放在 #if #else 块中,并且可以在编译行定义 #if。在 Java 中,我需要创建某种静态全局变量并在编译时对其进行初始化。我从来没有让它正常工作。

于 2009-08-12T22:35:53.107 回答
1

现代语言在语言本身中包含预处理器!对于 C++,预处理器仅用于模块管理和条件包含等,这是非常有用的。

我相信它是一个单独的工具,因为编译器并不是我们今天所知道的单一工具。我听说非常古老的 C 编译器用于生成文件的标记,然后在不同的阶段进行其余的编译。我能想到的主要原因是内存和其他资源与我们今天拥有的相比非常稀缺。

于 2009-08-12T22:04:51.827 回答
1

C和C++中的预处理器有两个不同的功能

  • 在构建过程中将文件拉到一起 - 像 Java 等语言。有自己的机制,比如 import 来做到这一点

  • 执行文本替换 - 这在 C 中在一定程度上仍然需要,但 C++ 可以(在大多数情况下)使用模板更好地做到这一点

因此,C 和 C++ 都需要它作为第一个,但 C++ 可以将它用于第二个,尽管它甚至在 C++ 中也很有用 - 请参阅今天早些时候的这个问题

于 2009-08-12T22:13:49.827 回答
0

您可以确定现代语言是用 C 或 C++ 编写的,并且在该实现本身中存在宏。您需要它们来处理操作系统差异的一件事。动态/高级语言包装并隐藏了底部某处需要宏的许多东西。

此外,宏有时用于提高速度。在动态语言中,速度并不那么重要。

于 2009-08-12T22:11:27.507 回答
0

你应该更仔细地看一下 Perl。Perl 支持源过滤器,它们基本上是用 Perl 编写的自定义 Perl 预处理器 :)

于 2009-08-12T22:19:25.463 回答
0

Java 旨在避免使 C++ 难以使用的几个特性。

C# 从 Java 复制(或继承)大部分设计决策。

高级编程语言避免了这种低级工件。

于 2009-08-12T22:29:03.743 回答