17
test.c:

int main()
{
    return 0;
}

I haven't used any flags (I am a newb to gcc) , just the command:

gcc test.c

I have used the latest TDM build of GCC on win32. The resulting executable is almost 23KB, way too big for an empty program.

How can I reduce the size of the executable?

4

10 回答 10

39

不要听从它的建议,但为了消遣,请阅读这个关于制作尽可能小的 ELF 二进制文件的“故事” 。

于 2009-08-22T13:48:17.807 回答
21

我怎样才能减小它的大小?

  • 不要这样做。你只是在浪费时间。
  • 使用 -s 标志去除符号(gcc -s)
于 2009-08-22T13:08:16.247 回答
12

默认情况下,一些标准库(例如 C 运行时)与您的可执行文件链接。查看钥匙--nostdlib --nostartfiles --nodefaultlib了解详情。此处描述的链接选项。

对于真正的程序,第二个选项是尝试优化选项,例如 -Os(优化大小)。

于 2009-08-22T13:08:08.603 回答
12

放弃。 在 x86 Linux 上,gcc 4.3.2 生成 5K 二进制文件。可是等等!那就是动态链接!静态链接的二进制文件超过半兆:516K。放松并学会忍受臃肿。

他们说 Modula-3 永远不会因为 200K hello world 二进制文件而去任何地方!


如果您想知道发生了什么,Gnu C 库的结构包括某些功能,无论您的程序是否依赖它们。这些功能包括诸如 malloc 和 free、dlopen、一些字符串处理以及一大堆似乎与语言环境和国际化有关的琐事尽管我找不到任何相关的手册页。

为需要最少服务的程序创建小型可执行文件并不是glibc的设计目标。公平地说,它也不是我曾经使用过的每个运行时系统(大约六个)的设计目标。

于 2009-08-22T18:59:12.767 回答
9

实际上,如果您的代码什么都不做,编译器仍然创建可执行文件是否公平?;-)

好吧,在 Windows 上,任何可执行文件仍然有大小,尽管它可能很小。使用旧的 MS-DOS 系统,一个完整的无操作应用程序只需几个字节。(我认为使用 21h 中断关闭程序需要四个字节。)然后,这些应用程序被直接加载到内存中。当 EXE 格式变得更流行时,情况发生了一些变化。现在可执行文件有关于进程本身的附加信息,例如代码和数据段的重定位以及一些校验和和版本信息。Windows 的引入为格式添加了另一个标题,告诉 MS-DOS 它无法执行可执行文件,因为它需要在 Windows 下运行。Windows 可以毫无问题地识别它。当然,可执行格式也扩展了资源信息,比如位图,

现在,无操作可执行文件的大小在 4 到 8 KB 之间,具体取决于您的编译器以及您用来减小其大小的每种方法。它的大小可以使 UPX 实际上产生更大的可执行文件!由于您在代码中添加了某些库,因此可能会在可执行文件中添加额外的字节。尤其是具有初始化数据或资源的库会增加大量字节。添加调试信息也会增加可执行文件的大小。

但是,尽管这一切都可以很好地减少大小,但您可能想知道继续担心应用程序的臃肿是否可行。现代硬盘会将文件分成几段,对于非常大的磁盘,差异会非常小。但是,保持尽可能小的尺寸所带来的麻烦会降低开发速度,除非您是熟悉这些优化的专家级开发人员。这些类型的优化不会提高性能,考虑到大多数系统的平均磁盘空间,我不明白为什么它是实用的。(不过,我确实以类似的方式优化了我自己的代码,但我对这些优化很有经验。)


EXE 标头感兴趣?它以字母 MZ 开头,代表“Mark Zbikowski”。第一部分是用于可执行文件的老式 MS-DOS 标头,用作 MS-DOS 的存根,表示该程序不是MS-DOS 可执行文件。(在二进制文件中,您可以找到文本“此程序无法在 DOS 模式下运行。”这基本上就是它所做的一切:显示该消息。接下来是 PE 标头,Windows 将识别并使用它而不是 MS-DOS标头。它以字母PE 开头,表示 Portable Executable. 在第二个标头之后将是可执行文件本身,分为几个代码和数据块。标头包含特殊的重新分配表,它告诉操作系统在哪里加载特定的块。如果你能把它控制在一个限度内,最终的可执行文件可以小于 4 KB,但 90% 将是标题信息,没有任何功能。

于 2009-08-22T13:44:09.590 回答
3

我喜欢多年前DJGPP FAQ解决这个问题的方式:

一般来说,通过查看“Hello”程序的大小来判断代码大小是没有意义的,因为此类程序主要由启动代码组成。...所有这些功能的大部分功能都浪费在“Hello”程序中。运行所有代码只是为了打印一个 15 字节的字符串并退出是没有意义的。

于 2009-08-22T13:48:33.127 回答
2

' size a.out' 告诉你代码、数据和 bss 段的大小是什么?大部分代码可能是启动代码(通常crt0.o在 Unix 机器上),由 o/s 调用并在调用main().

于 2009-08-24T22:28:05.863 回答
2

这个练习的目的是什么?

即使使用像 C 这样低级的语言,在调用 main 之前仍然需要进行很多设置。其中一些设置由加载程序处理(需要某些信息),一些由调用 main 的代码处理。然后可能有一点库代码,任何普通程序都必须拥有。至少,可能有对标准库的引用,如果它们在 dll 中的话。

检查空程序的二进制大小本身就是一项毫无价值的练习。它什么也没告诉你。如果您想了解有关代码大小的知识,请尝试编写非空(最好是非平凡)程序。比较使用标准库的程序和自己做所有事情的程序。

如果您真的想知道该二进制文件中发生了什么(以及为什么它如此之大),那么找出可执行格式获取二进制转储工具并将其拆开。

于 2009-08-22T13:46:33.423 回答
1

在二进制文件上运行 strip 以消除符号。使用 gcc 版本 3.4.4(cygming special)我从 10k 下降到 4K。

您可以尝试链接自定义运行时(调用 main 的部分)来设置运行时环境。所有程序都使用相同的程序来设置 gcc 附带的运行时环境,但对于您的可执行文件,您不需要数据或归零内存。这意味着您可以摆脱未使用的库函数,如 memset/memcpy 并减少 CRT0 大小。在寻找这方面的信息时,请查看嵌入式环境中的 GCC。嵌入式开发人员通常是唯一使用自定义运行时环境的人。

其余的是加载可执行文件的操作系统的开销。除非你手动调整,否则你不会在那里做太多事情?

于 2009-08-24T08:02:25.413 回答
0

使用 GCC,使用-Os而不是使用其他优化标志之一 (-O2-O3) 来编译您的程序。这告诉它优化大小而不是速度。顺便说一句,如果某些关键段恰好更适合,它有时可以使程序运行得比速度优化更快。另一方面,-O3实际上可以引起代码大小的增加。

可能还有一些链接器标志告诉它从最终二进制文件中删除未使用的代码。

于 2009-08-22T14:10:24.480 回答