关于c#:为什么预处理器的使用在C / C ++ / ObjC以外的语言中不常见?

Why is preprocessor usage less common in languages other than C/C++/ObjC?

我从事Java和VB .NET程序员已经4年了,C程序员已经有6个月了。我还使用了一些动态语言,如Perl、Python、PHP和JavaScript。

我从未需要过预处理器。

我的问题是:为什么你在C、C++和Objy-C中看到预处理器的广泛使用,但是很少使用(或者从来没有)用Java语言、C语言或斯卡拉语言?


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

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

使用C++中的预处理器是对C的一种回退,通常是不赞成的。语言特性,例如常量、内联函数和模板,可以在大多数情况下使用,在C中,您可以使用预处理器。

在C++中使用预处理器是可接受的,甚至是必要的几个情况下,包括头文件的守卫,以防止同一个报头被多次包含,EDCOX1(0)使用C和C++的相同标头,γ-FieleSy*和γyLyx*用于日志记录,以及其他一些。

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


在爪哇、C、斯卡拉中没有看到预处理器的原因是这些语言显然没有一个。

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

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

1
2
3
4
5
#ifdef WIN32
    closesocket(s);
#else
    close(s);
#endif

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

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

在C++的设计和进化中,Bjarne Stroustrup表示他想删除C++中预处理器的依赖性,但没有成功。


每种语言都需要一种独立的编译机制。理想情况下,语言将接口与实现区分开来,并且模块仅依赖于其导出的模块的接口。(请参见,例如ADA、CLU、MODULA等。)

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

现在,语言设计者认识到文本包含是不可能运行一条铁路的,所以语言倾向于运行到单独编译的接口(艾达,Modula,OCAML),到编译器生成的接口(Haskell),或者到保证接口一致性的动态系统(Java,SimalTalk)。有了这样的机制,就不需要预处理器,而且有很多理由不需要预处理器(想想源代码分析和调试)。


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

C是在预处理器的基础上构建的,作为一个强大的工具,它被用来实现非常基础的东西(比如包含保护),开发人员可以使用它来优化他们的代码,或者选择包括/排除某些代码块以及其他东西。C++继承了大多数C的成语,宏不再用于速度(因为内联被引入),但它仍然用于很多事情,请参阅POST什么是预处理器宏好?


因为戈斯林和海尔斯堡都明白错误的预处理带来的危险和技术债务!


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

许多人没有意识到这一点是正确的,在Java世界中,它被称为"代码生成"而不是"预处理",因为"预处理器"听起来像讨厌的老C,而"代码生成"听起来像是一种专业工具,它能有效地提高成熟的企业流程。尽管如此,它仍然是预处理的,即使你必须为一个不兼容的非标准专有工具支付一大笔钱,而不仅仅是使用语言中内置的工具。


我不同意目前的共识,即CPP在现代语言中是不必要的。在很多情况下,同一个程序有3个稍微不同的版本,我希望能够对每个版本进行大量更改。使用cpp,我可以将它们全部放在if-else块中,并且可以在编译行定义if。在爪哇中,我需要创建一些静态全局,并在编译时初始化它。我从来没有把它做好。


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

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

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

所以C和C++都需要第一个,但是C++可以把它放在第二个地方,尽管它在C++中也是有用的——从今天早些时候就可以看到这个问题。


现代语言本身就包含了预处理器!对于C++,预处理只需要用于模块管理和条件包含,例如非常有用。

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


Java是为了避免一些使C++难以使用的特性而设计的。

复制或继承大部分来自Java的设计决策。

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


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


可以肯定的是,现代语言是用C或C++编写的,而在实现本身中也有宏。您需要他们处理操作系统差异的一件事。动态/高级语言包装并隐藏了许多底层需要宏的东西。

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