生成的二进制文件为什么如此庞大?

16

当我编译我的 C++ 程序时,生成的二进制文件为什么会这么大(通常比源代码文件大10倍以上)?相对于不需要编译即可运行的解释性语言,这有什么优势呢?


5
什么模式-调试/发布等?这可能会对二进制文件的大小产生重大影响-还有静态链接库是否会产生影响。 - Nim
你需要包含哪些头文件?每个被包含的模板代码都会被编译进你的可执行文件中,标准头文件中也包含了模板。 - josefx
1
解释性语言需要另外一些东西:它们需要解释器。你可以用 C 写一个“hello world”,需要几千字节的空间,或者你可以用 Python 写,只需要大约 100 字节……再加上一个 3MB 的解释器。没有免费的午餐 :) - jalf
4个回答

14

现代解释性语言通常将代码编译为某种形式的表示以实现更快的执行,虽然可能不会写入磁盘,但无法保证程序以更紧凑的形式呈现。一些解释器会进行完整的机器代码生成(例如Java JIT)。此外,存放在内存中的解释器本身可能很大。

几点说明:

  • 源代码中命令越复杂,执行它们所需的机器代码操作就越多。因此,高级语言特性往往具有更高的编译代码与源代码比率。这不一定是坏事:可以将其视为“我只需要说一点我想要做的事情,然后它会推断出所有必要的步骤”。编程的挑战在于确保它们是必要的-这需要良好的库和程序设计。
  • 编译器通常会故意决定交换一些可执行大小以获得更快的预期执行速度:内联与非内联代码是这种妥协的一部分,但对于小函数来说,两者都可能不一致地更紧凑。
  • 更复杂的运行时环境(例如添加对C++异常的支持)可能涉及一些额外的代码,在程序首次启动时运行以构建必要的语言特性环境。
  • 库功能可能不可比较。除了您很可能需要自己跟踪并非常明确地使用的附加库(例如XML、PDF解析、OpenGL)之外,语言通常悄悄地使用支持库来实现看似是语言特性和函数的功能。这些中的任何一个都可能非常大。
    • 例如,许多解释器只公开C库的printf()语句或类似语句,而对于输出格式化,C++具有ostream-一种更复杂、可扩展且类型安全的系统,具有(好或坏)函数调用间持久状态、查询和设置该状态的例程、额外的可定制缓冲区、可定制的字符类型和本地化等额外层级,以及通常会导致更小更大程序的许多小内联函数。最佳选择取决于您的应用程序和内存与性能目标。
  • 内置语言声明可能被编译成不同的形式:例如对一个整数表达式进行switch,并在1到1000之间随机分布100个case标签:一些编译器/语言可能会决定"压缩"这100个case,并进行二分查找匹配,而另一些则会使用稀疏数组来直接索引1000个元素(这会在可执行文件中浪费空间,但通常使代码更快)。因此,很难根据可执行文件的大小得出结论。
  • 通常,在程序变得越来越大和复杂时,内存使用和执行速度变得越来越重要。你不会看到像操作系统、企业级Web服务器或全功能商业文字处理器这样的系统使用解释性语言编写,因为它们没有可扩展性。


    7

    解释型语言假定有一个解释器可用,而编译程序在大多数情况下是独立的。


    扩展一下,这意味着解释型“程序”期望所有库等已经存在于系统中,因此您的“程序”将只有您的代码。像C++这样的语言将存储您在二进制文件中引用的代码,使其成为一个自包含的单元。 - William Mioch
    2
    @William:编译后的代码通常在运行时依赖于共享库/DLL,因此情况并不是那么明确。 - Tony Delroy
    @Tony:解释型语言也是如此。解释器通常依赖于相同的共享库/DLL,因此如果将所有依赖项加起来,解释型语言通常会依赖更多的代码。 - jalf
    @Tony:是的,你说得对。我已经有一段时间没有做非托管代码了,我在考虑静态链接库。 - William Mioch
    @jalf:肯定是这个趋势-只是说这不是完全黑白分明的。@William:不用担心。干杯。 - Tony Delroy

    1

    举个简单的例子:假设你有一个只有一行的程序

    print("hello world")
    

    那个“print”是做什么用的?毫无疑问,你正在请求其他代码执行一些工作。而且那些代码并不是免费的,需要运行的总和远远超过你编写的代码行数。在更现实的程序中,你会利用许多复杂的库来管理窗口和其他UI特性、网络、数据库等等。现在,无论这些代码是捆绑在你的应用程序中还是从DLL中加载或者存在于解释器中,它们都必须存在于某个地方。

    编译和解释之间有很多权衡,以及像Java的编译/字节码解释方法这样的中间解决方案。例如,你可以考虑以下问题:

    • 每次运行时解释源代码的运行时成本与运行编译后的代码
    • 解释器的可移植性优势-你需要为不同平台编译单独的应用程序版本。

    1
    通常,程序是用高级语言编写的,为了让CPU执行这些程序,必须将程序转换为机器码。这个转换过程由编译器解释器完成。 编译器只需进行一次转换,而解释器通常在每次执行程序时都要进行转换。 解释程序比编译程序运行速度慢得多,因为解释器必须分析每个语句并执行所需的操作,而编译代码只在编译期间确定上下文中执行操作的方式(这也是大型二进制文件存在的原因)。
    另一个解释器的缺点是它们必须作为附加软件存在于环境中才能运行源代码

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接