为什么简单的程序会占用这么多存储空间space?
Why do simple programs take up so much storage space?
我用 C 语言创建了一个简单的 hello world 程序:
#include <stdio.h>
int main() {
printf("Hello World!\n");
return 0;
}
之后,我使用 gcc 在 Mac 上编译它并使用 xxd 转储它。每行 16 个字节(8 个字),编译后的程序总共有 3073 行或 49 424 个字节。在所有这些字节中,只有 1 904 个字节组成了程序,而其余 47 520 个字节都是零。
考虑到只有大约 3.9% 的字节不为零,这是浪费 space 的明显例子。有什么办法可以在这里优化可执行文件的大小吗? (顺便说一下,我已经尝试使用 -Os
编译器选项但没有得到任何结果。)
编辑:我通过计算 hexdump 的行数得到这些数字,但在包含实际指令的行中也有零。我没有计算这些字节,因为它们可能对程序的执行至关重要。 (就像字符串 Hello World!
的空终止符一样)我只计算了完整的零块。
生成的文件不仅仅包含程序所需的代码和数据。它还包含正确加载和执行程序所需的元数据。一般来说,这个元数据可能被拆分成多个section,每个section需要在一定的边界对齐。为了做到这一点,可以添加一些零来填充。对更大的程序进行相同的检查将导致更少的 space 被元数据和填充占用。
可执行文件也可能被制作成可以快速加载到内存中。特别是文本和数据段可能与 OS 的页面大小对齐。这通常是 4KB 或 2 的更大次方。在您称某些东西为 space 的浪费之前,请确保您了解 link 编辑和加载、分段和 Executable and Linkable Format (小精灵).
gcc on MacOS 生成 object 和 Mach-O 文件格式的可执行文件。该文件被分成多个段,每个段都有一些对齐要求以使加载更有效(因此你得到所有的零填充)。我拿了你的代码并用 gcc 在我的 Mac 上构建它,给我一个 8432 字节的可执行文件大小。是的,xxd
给了我一堆零。这是 headers:
部分的 objdump
输出
$ objdump -section-headers hello
hello: file format Mach-O 64-bit x86-64
Sections:
Idx Name Size Address Type
0 __text 0000002a 0000000100000f50 TEXT
1 __stubs 00000006 0000000100000f7a TEXT
2 __stub_helper 0000001a 0000000100000f80 TEXT
3 __cstring 0000000f 0000000100000f9a DATA
4 __unwind_info 00000048 0000000100000fac DATA
5 __nl_symbol_ptr 00000010 0000000100001000 DATA
6 __la_symbol_ptr 00000008 0000000100001010 DATA
__text
包含程序的机器代码,__cstring
包含文字 "Hello World!\n"
,每个部分都有一堆元数据。
这种结构对于像你这样的简单程序来说显然是矫枉过正,但像你这样的简单程序并不常见。 Object 和可执行文件格式必须能够支持动态加载、符号重定位和其他需要复杂结构的东西。任何编译的程序都有最低级别的复杂性(以及因此的大小)。
因此,“小”程序的可执行文件比您认为的基于源代码的要大,但请注意,其中不仅仅是您的源代码。
我用 C 语言创建了一个简单的 hello world 程序:
#include <stdio.h>
int main() {
printf("Hello World!\n");
return 0;
}
之后,我使用 gcc 在 Mac 上编译它并使用 xxd 转储它。每行 16 个字节(8 个字),编译后的程序总共有 3073 行或 49 424 个字节。在所有这些字节中,只有 1 904 个字节组成了程序,而其余 47 520 个字节都是零。
考虑到只有大约 3.9% 的字节不为零,这是浪费 space 的明显例子。有什么办法可以在这里优化可执行文件的大小吗? (顺便说一下,我已经尝试使用 -Os
编译器选项但没有得到任何结果。)
编辑:我通过计算 hexdump 的行数得到这些数字,但在包含实际指令的行中也有零。我没有计算这些字节,因为它们可能对程序的执行至关重要。 (就像字符串 Hello World!
的空终止符一样)我只计算了完整的零块。
生成的文件不仅仅包含程序所需的代码和数据。它还包含正确加载和执行程序所需的元数据。一般来说,这个元数据可能被拆分成多个section,每个section需要在一定的边界对齐。为了做到这一点,可以添加一些零来填充。对更大的程序进行相同的检查将导致更少的 space 被元数据和填充占用。
可执行文件也可能被制作成可以快速加载到内存中。特别是文本和数据段可能与 OS 的页面大小对齐。这通常是 4KB 或 2 的更大次方。在您称某些东西为 space 的浪费之前,请确保您了解 link 编辑和加载、分段和 Executable and Linkable Format (小精灵).
gcc on MacOS 生成 object 和 Mach-O 文件格式的可执行文件。该文件被分成多个段,每个段都有一些对齐要求以使加载更有效(因此你得到所有的零填充)。我拿了你的代码并用 gcc 在我的 Mac 上构建它,给我一个 8432 字节的可执行文件大小。是的,xxd
给了我一堆零。这是 headers:
objdump
输出
$ objdump -section-headers hello
hello: file format Mach-O 64-bit x86-64
Sections:
Idx Name Size Address Type
0 __text 0000002a 0000000100000f50 TEXT
1 __stubs 00000006 0000000100000f7a TEXT
2 __stub_helper 0000001a 0000000100000f80 TEXT
3 __cstring 0000000f 0000000100000f9a DATA
4 __unwind_info 00000048 0000000100000fac DATA
5 __nl_symbol_ptr 00000010 0000000100001000 DATA
6 __la_symbol_ptr 00000008 0000000100001010 DATA
__text
包含程序的机器代码,__cstring
包含文字 "Hello World!\n"
,每个部分都有一堆元数据。
这种结构对于像你这样的简单程序来说显然是矫枉过正,但像你这样的简单程序并不常见。 Object 和可执行文件格式必须能够支持动态加载、符号重定位和其他需要复杂结构的东西。任何编译的程序都有最低级别的复杂性(以及因此的大小)。
因此,“小”程序的可执行文件比您认为的基于源代码的要大,但请注意,其中不仅仅是您的源代码。