我已经是Java和VB.Net程序员已经有4年了,还有一个C#程序员已经有6个月了。我还使用了一堆动态语言,如Perl,Python,PHP和JavaScript。
我从来不需要预处理器。
我的问题是:为什么你会在C,C ++和Objective-C中看到如此广泛使用预处理器,但很少(或从不)在Java,C#或Scala等语言中看到它?
答案 0 :(得分:13)
我不知道Objective-C,所以我的答案将是在C和C ++中对比预处理器的使用。
由于多种原因,预处理器最初是C所必需的。如果我没记错的话,原来C没有常数,所以需要#define来避免魔术数字。在1999之前,C没有内联函数,因此#define再次用于创建宏或“伪函数”以节省函数调用的开销,同时保持代码结构化。 C也没有运行时或编译时多态,因此条件编译需要#ifdefs。编译器通常不够聪明,无法优化无法访问的代码,因此,再次使用#ifdefs插入调试或诊断代码。
在C ++中使用预处理器是对C的回归,并且通常不赞成。语言功能,例如常量,内联函数和模板,可以在C中使用预处理器的大多数情况下使用。
在C ++中使用预处理器是可接受的甚至是必要的少数情况包括头文件的保护,以防止多次包含相同的头,#ifdef __cplusplus
使用相同的头对于C和C ++,__ FILE__和__LINE__用于记录,以及其他一些。
预处理器通常也用于特定于平台的定义,尽管Stephen Dewhurst的C++ Gotchas建议为平台特定定义分别包含目录,并在每个平台的单独构建配置中使用它们。
答案 1 :(得分:10)
您没有看到Java,C#或Scala中使用的预处理器的原因是这些语言显然没有。
C预处理器的一个常见用途是帮助提供特定于平台的代码。由于C(我在这里包括C ++和Objective-C)是一种需要直接与操作系统接口的低级语言,因此在可移植代码中必须有针对不同操作系统编译的代码的不同部分。您可以在成熟,高度可移植的代码库中找到类似这类内容的大量示例,例如zlib。
作为一个简单的例子,要关闭一个网络套接字,必须做这样的事情(在某种程度上,这肯定可以包含在一个函数中但它必须存在于某个地方):
#ifdef WIN32
closesocket(s);
#else
close(s);
#endif
在VM上运行的较新语言不需要特定于平台的不同代码段,可以针对单个可移植标准库进行编写。
预处理器还提供了一种在C语言中定义常量的方法,这些方法由较新语言中的其他更好的语言功能提供。
在C ++的设计和演变中,Bjarne Stroustrup表示他希望在C ++中删除对预处理器的依赖,但是没有成功。
答案 2 :(得分:9)
每种语言都需要单独编译的机制。理想情况下,语言将接口与实现区分开来,模块仅依赖于它导出的模块的接口。 (参见,例如,Ada,Clu,Modula等。)
C没有用于接口或实现的语言构造。因为不同的.c文件共享单个接口视图是至关重要的,所以编程规则演变为在.h文件中放置声明(即接口)并使用文本包含(#include
)共享这些声明/接口。原则上,#define
和#ifdef
可以免除,但#include
不能。{/ p>
如今语言设计人员认识到文本包含无法运行铁路,因此语言往往运行到单独编译的接口(Ada,Modula,OCaml),编译器生成的接口(Haskell)或动态系统保证接口一致性(Java,Smalltalk)。使用这样的机制,不需要预处理器,并且有很多理由没有预处理器(想想源代码分析和调试)。
答案 3 :(得分:4)
因为这些语言的设计和目标并不相同。
C是在预处理器的基础上构建的,它是一个功能强大的工具,它用于实现非常基本的东西(例如包含保护),开发人员可以使用它来通过宏优化代码或者可选地包含/排除某些代码块以及其他内容。 C ++继承了大多数C的习语,宏不再用于速度了(因为内联被引入)但它仍然用于很多事情,请参阅帖子What are preprocessor macros good for?
答案 4 :(得分:3)
因为Gosling和Heilsberg都了解滥用预处理所带来的危险和技术债务!
答案 5 :(得分:1)
现代语言将预处理器包含在语言本身中!对于C ++,预处理器仅用于模块管理和条件包含,例如非常有用。
我认为这是一个单独的工具,因为编译器不是我们今天所知的单一工具。我听说很老的C编译器用于生成令牌到文件,然后在不同的阶段进行剩余的编译。我能想到的主要原因是,与我们今天的情况相比,记忆和其他资源非常稀少。
答案 6 :(得分:1)
C和C ++中的预处理器有两个不同的函数
在构建过程中将文件拉到一起 - 语言 像Java等人。有自己的机制,如导入这样做
执行文本替换 - 在C中仍然需要这一点,但C ++可以更好地使用模板(大部分)使用模板
因此,C和C ++都需要第一个,但是C ++可以将其破解为第二个,尽管它在C ++中也很有用 - 请参阅今天早些时候的this question。
答案 7 :(得分:1)
我不同意在现代语言中cpp是不必要的似乎正在达成的共识。我有很多案例,我有相同程序的3个略有不同的版本,我希望能够为每个版本进行一系列更改。使用CPP,我可以将它们全部放在#if #else块中,我可以在编译行定义#if。在Java中,我需要创建某种静态全局并在编译时初始化它。我从来没有让它正常工作。
答案 8 :(得分:1)
预处理在Java世界中非常普遍。它用于弥补语言缺乏足够的内置抽象功能,否则会导致无限复制和粘贴的样板代码。
许多人没有意识到这一点的原因是,在Java世界中它被称为“代码生成”而不是“预处理”,因为“预处理器”听起来像讨厌的旧C,而“代码生成”听起来像一个专业工具,可以提高成熟的企业流程。尽管如此,它仍然需要预处理,即使您必须为不兼容的非标准专有工具支付大笔费用,而不是只使用内置于该语言中的设施。
答案 9 :(得分:0)
您可以确定现代语言是用C或C ++编写的,并且在该实现中本身就有宏。您需要它们来处理操作系统差异。动态/更高级别的语言包装,并隐藏底部某处的许多东西,你需要宏。
此外,宏有时用于速度。在动态语言中,速度并不是那么重要。
答案 10 :(得分:0)
你应该仔细看看Perl。 Perl支持source filters,它们基本上是用Perl编写的自定义Perl预处理器:)
答案 11 :(得分:0)
Java旨在避免使C ++难以使用的几个功能。
C#从Java中复制(或继承)大部分设计决策。
更高级别的编程语言避免了这种低级别的工件。