即使是最小的 .c 文件也总是编译成至少 128 KB 的可执行文件,这是有原因的吗?
Is there a reason even my tiniest .c files always compile to at least 128-kilobyte executables?
我正在使用 Dev-C++,它使用 GCC 在 Windows 8.1,64 位上编译。
我注意到我所有的 .c 文件总是被编译成至少 128 KB 的 .exe 文件,无论源代码有多小。即使是一个简单的 "Hello, world!" 也是 128kb。正如我所料,具有更多代码行的源文件增加了可执行文件的大小,但所有文件开始时至少有 128kb,好像这是某种最小大小。
我知道 .exe 实际上没有这样的最小大小; .kkrieger 是一个完整的 first-person 射击游戏,具有 3d 图形和声音,所有这些都适合一个 96kb 的可执行文件。
为了一探究竟,我在 Notepad++ 中打开了 hello_world.exe。我想也许我的编译器添加了一个冗长的 header,恰好是 128kb。
不幸的是,我对可执行文件的了解还不够多,无法理解它,尽管我确实发现像 "Address %p has no image-section VirtualQuery failed for %d bytes at address %p" 这样的字符串隐藏在 .exe 中常见的乱码中。
当然,这不是一个严重的问题,但我想知道为什么会这样。
为什么会出现这个 128kb 的最小值?这是否与我的 64 位 OS 有关,或者可能与我的编译器的怪癖有关?
简答:视情况而定。
长答案:这取决于您拥有的操作系统及其处理可执行文件的方式。
大多数(如果不是全部)编程语言的编译器不会将其分解为绝对的、原始的 x86/ARM/other 体系结构的机器代码。相反,在他们将您的源代码打包成 .o (object) 文件后,他们会将 .o 及其库和 "link" 全部放在一起,从而形成标准的可执行文件格式。这些 "executable formats" 本质上是 system-specific 包含低级 very-close-to-machine-code 指令的文件格式,OS 以这样一种方式解释它可以将这些 low-level 指令中继到machine-code 指令形式的 CPU。
例如,我将讨论 Linux 设备最常用的两种可执行文件格式:ELF 和 ELF64(我会让您自己找出同名差异)。 ELF stands for Executable and Linkable Format. In every ELF-compiled program, the file starts off with a 4-byte "magic number", which is simply a hexadecimal 0x7F followed by the string "ELF" in ASCII. The next byte is set to either 1 or 2, which signifies that the program is for 32-bit or 64-bit architectures, respectively. And after that, another byte to signify the program's endianness. 之后,还有几个字节说明架构是什么,依此类推,直到 64 位总共达到 64 个字节 header。
但是,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++ 的库。在那种情况下,很多链接器都有某种 command-line 选项,它本质上告诉链接器 "Hey, I'm only using like, 2 functions from the STDCPP library, you don't need the whole thing"。在 Linux 链接器 ld
上,这是 command-line 选项 -nodefaultlibs
。不过,我不完全确定 windows 上的内容。当然,这可以很快中断大量调用,以及在进行大量标准 C++ 调用的程序中的调用。
所以,最后,我更担心的是 re-writing 你的程序使用常规 C 函数而不是 new-fangled C++ 函数,尽管它们很神奇。那就是如果你担心尺寸。
我正在使用 Dev-C++,它使用 GCC 在 Windows 8.1,64 位上编译。
我注意到我所有的 .c 文件总是被编译成至少 128 KB 的 .exe 文件,无论源代码有多小。即使是一个简单的 "Hello, world!" 也是 128kb。正如我所料,具有更多代码行的源文件增加了可执行文件的大小,但所有文件开始时至少有 128kb,好像这是某种最小大小。
我知道 .exe 实际上没有这样的最小大小; .kkrieger 是一个完整的 first-person 射击游戏,具有 3d 图形和声音,所有这些都适合一个 96kb 的可执行文件。
为了一探究竟,我在 Notepad++ 中打开了 hello_world.exe。我想也许我的编译器添加了一个冗长的 header,恰好是 128kb。
不幸的是,我对可执行文件的了解还不够多,无法理解它,尽管我确实发现像 "Address %p has no image-section VirtualQuery failed for %d bytes at address %p" 这样的字符串隐藏在 .exe 中常见的乱码中。
当然,这不是一个严重的问题,但我想知道为什么会这样。
为什么会出现这个 128kb 的最小值?这是否与我的 64 位 OS 有关,或者可能与我的编译器的怪癖有关?
简答:视情况而定。
长答案:这取决于您拥有的操作系统及其处理可执行文件的方式。
大多数(如果不是全部)编程语言的编译器不会将其分解为绝对的、原始的 x86/ARM/other 体系结构的机器代码。相反,在他们将您的源代码打包成 .o (object) 文件后,他们会将 .o 及其库和 "link" 全部放在一起,从而形成标准的可执行文件格式。这些 "executable formats" 本质上是 system-specific 包含低级 very-close-to-machine-code 指令的文件格式,OS 以这样一种方式解释它可以将这些 low-level 指令中继到machine-code 指令形式的 CPU。
例如,我将讨论 Linux 设备最常用的两种可执行文件格式:ELF 和 ELF64(我会让您自己找出同名差异)。 ELF stands for Executable and Linkable Format. In every ELF-compiled program, the file starts off with a 4-byte "magic number", which is simply a hexadecimal 0x7F followed by the string "ELF" in ASCII. The next byte is set to either 1 or 2, which signifies that the program is for 32-bit or 64-bit architectures, respectively. And after that, another byte to signify the program's endianness. 之后,还有几个字节说明架构是什么,依此类推,直到 64 位总共达到 64 个字节 header。
但是,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++ 的库。在那种情况下,很多链接器都有某种 command-line 选项,它本质上告诉链接器 "Hey, I'm only using like, 2 functions from the STDCPP library, you don't need the whole thing"。在 Linux 链接器 ld
上,这是 command-line 选项 -nodefaultlibs
。不过,我不完全确定 windows 上的内容。当然,这可以很快中断大量调用,以及在进行大量标准 C++ 调用的程序中的调用。
所以,最后,我更担心的是 re-writing 你的程序使用常规 C 函数而不是 new-fangled C++ 函数,尽管它们很神奇。那就是如果你担心尺寸。