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

cdm*_*kay 11 c c# java preprocessor

我已经是Java和VB.Net程序员已经有4年了,还有一个C#程序员已经有6个月了.我还使用了一堆动态语言,如Perl,Python,PHP和JavaScript.

我从来不需要预处理器.

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

Dim*_*ima 14

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

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

在C++中使用预处理器是对C的回归,并且通常不赞成.语言功能,例如常量,内联函数和模板,可以在C中使用预处理器的大多数情况下使用.

在C++中使用预处理器是可接受的甚至是必要的少数情况包括头文件的保护,防止多次包含#define相同的头,为C和C++使用相同的头,__ FILE__和__LINE__用于记录,还有一些用于记录.

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

  • 这不是预处理器在C++中使用的唯一时间.广泛(如果不是普遍的话)被认为是OK的其他用途包括在日志记录中输出`__FILE__`和`__LINE__`,Boost FOREACH宏,检查编译器功能支持(包括经典的`#ifdef __cplusplus // extern"C"{// #endif `),特定于平台的定义(例如,在MSVC中需要`__declspec`和在GCC中需要`__attribute__`),在包含<cassert>之前定义NDEBUG,... (2认同)

Gre*_*ill 11

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

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

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

#ifdef WIN32
    closesocket(s);
#else
    close(s);
#endif
Run Code Online (Sandbox Code Playgroud)

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

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

在C++的设计和演变中,Bjarne Stroustrup表示他希望在C++中删除对预处理器的依赖,但是没有成功.

  • "你没有看到预处理器的原因是......那些语言......没有." !优秀! (4认同)
  • C#确实有一个预处理器:http://en.csharp-online.net/CSharp_Preprocessor_Directives (2认同)

Nor*_*sey 9

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

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

如今语言的设计者认识到,文本包含没有办法运行的铁路,所以语言往往运行要么单独编译接口(ADA,MODULA,OCaml中),编译器生成的接口(哈斯克尔),或对保证接口的一致性动态系统(Java,Smalltalk).有了这样的机制,就不需要预处理器,也没有必要有很多理由(想想源代码分析调试).