【问题标题】:Is there a reason even my tiniest .c files always compile to at least 128-kilobyte executables?即使是我最小的 .c 文件也总是编译为至少 128 KB 的可执行文件,有什么原因吗?
【发布时间】:2016-01-13 03:08:47
【问题描述】:

我在 Windows 8.1 64 位上使用 Dev-C++,它使用 GCC 编译。

我注意到我的所有 .c 文件总是编译为至少 128 KB 的 .exe 文件,无论源文件多么小。即使是简单的“你好,世界!”是128kb。正如我所料,具有更多代码行的源文件增加了可执行文件的大小,但所有文件开始时至少 128kb,好像这是某种最小大小。

我知道 .exe 实际上并没有这样的最小大小; .kkrieger 是一个完整的第一人称射击游戏,具有 3D 图形和声音,所有这些都可以放在一个 96kb 的可执行文件中。

为了弄清楚这一点,我在 Notepad++ 中打开了我的 hello_world.exe。我想,也许我的编译器添加了一个恰好为 128kb 的冗长标头。

不幸的是,我对可执行文件知之甚少,无法理解它,尽管我确实发现诸如“地址 %p 没有图像部分 VirtualQuery failed for %d bytes at address %p”之类的字符串隐藏在其中.exe 中常见的乱码。

当然,这不是一个严重的问题,但我想知道为什么会这样。

为什么会出现这个 128kb 的最小值?这是否与我的 64 位操作系统有关,或者可能与我的编译器的怪癖有关?

【问题讨论】:

  • 大概是ELF或者COFF或者Mach-O文件布局。
  • 无论原始源代码是什么,每种文件格式都会有一些始终存在的内容。此外,您可能正在查看包含调试符号的二进制文件。如果是这种情况,那么您可以将其剥离以使其更小。 “带有 3D 图形和声音,都适合一个 96kb 的可执行文件”。您查看的 exe 中不太可能包含所有代码。它将使用可能非常大的 dll(共享库)。
  • @kaylum - 实际上,不管你信不信,.kkrieger 实际上是静态链接的。 .kkrieger 使用的唯一外部 DLL 是来自 windows 的wgl 相关函数(Win32 中的所有图形都需要)。它只是广泛地使用程序生成。纹理是从可执行文件中压缩的,产生打包成几个字节的整个纹理网格。声音只是一个不断输入 MIDI 数据的 DSP 输出流,这意味着它们可以将整个声音引擎打包成几个字节的内联汇编。很整洁的东西,对吧?
  • @MasonWatmough 哇,是的,很酷。对于 OP 了解用于产生小二进制结果的长度也是很好的信息。

标签: c compilation executable


【解决方案1】:

简短回答:视情况而定。

长答案:这取决于您拥有的操作系​​统以及它如何处理可执行文件。

大多数(如果不是全部)编程语言编译器不会将其分解为绝对的、原始的 x86/ARM/其他架构的机器代码。相反,在他们将您的源代码打包成 .o(对象)文件后,他们会将 .o 及其库“链接”在一起,从而形成标准的可执行格式。这些“可执行格式”本质上是特定于系统的文件格式,其中包含低级、非常接近机器代码的指令,操作系统以这样的方式解释这些低级指令到 CPU 的形式机器码指令。

例如,我将讨论 Linux 设备最常用的两种可执行格式:ELF 和 ELF64(我会让您自己弄清楚同名的区别是什么)。 ELF 代表可执行和可链接格式。在每个 ELF 编译的程序中,文件以一个 4 字节的“幻数”开始,它只是一个十六进制的 0x7F,后跟 ASCII 中的字符串“ELF”。下一个字节设置为 1 或 2,表示程序分别用于 32 位或 64 位体系结构。在那之后,另一个字节表示程序的endianness. 之后,还有几个字节表示架构是什么,依此类推,直到 64 位标头总共达到 64 个字节。

但是,64 字节甚至不接近您所说的 128K。那是因为(除了 windows .exe 格式通常要复杂得多),这里的 C++ 标准库有问题。例如,让我们看一下 C++ iostream 库的一个常见用法:

#include <iostream>
int main()
{
    std::cout<<"Hello, World!"<<std::endl;
    return 0;
}

这个程序可能会在 Windows 系统上编译成一个非常大的可执行文件,因为在你将iostream 添加到你的程序的那一刻,它会将整个 C++ 标准库添加到其中,极大地增加了你的可执行文件的大小。

那么,我们该如何解决这个问题呢?简单的: 使用 C++ 的 C 标准库实现!

#include <cstdio>
int main()
{
    printf("Hello, World!\n");
    return 0;
}

只需使用原始的 C 标准库即可将您的大小从几百 KB 减少到最多几个。发生这种情况的原因仅仅是因为 GCC/G++ 出于某种奇怪的原因非常喜欢将程序与整个标准 C++ 库链接。

但是,有时您绝对需要使用 C++ 特定的库。在这种情况下,许多链接器都有某种命令行选项,基本上告诉链接器“嘿,我只使用 STDCPP 库中的 2 个函数,你不需要整个东西”。在 Linux 链接器 ld 上,这是命令行选项 -nodefaultlibs。不过,我不完全确定这在 Windows 上是什么。当然,这可以非常快速地中断大量的调用,例如在进行大量标准 C++ 调用的程序中。

所以,最后,我会更担心简单地重写您的程序以使用常规 C 函数而不是新奇的 C++ 函数,尽管它们很神奇。如果你担心尺寸的话。

【讨论】:

  • 另外,您可能需要添加-fno-exceptions 以防止链接器/编译器翻转。
猜你喜欢
  • 2019-09-22
  • 1970-01-01
  • 1970-01-01
  • 2010-10-07
  • 1970-01-01
  • 2015-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多