GCC C ++“Hello World”程序 - >在Windows上编译时,.exe是500kb大。如何减小尺寸?

时间:2009-06-25 08:20:27

标签: c++ optimization gcc netbeans size

我刚刚开始学习C ++ - 我在Windows上使用nuwen's版本的MingW,使用NetBeans作为IDE(我也有MSDN AA版本的MSVC 2008,虽然我不经常使用它)

编译这个简单的程序时:

#include <iostream>
using namespace std;

int dog, cat, bird, fish;

void f(int pet) {
  cout << "pet id number: " << pet << endl;
}

int main() {
  int i, j, k;
  cout << "f(): " << (long)&f << endl;
  cout << "dog: " << (long)&dog << endl;
  cout << "cat: " << (long)&cat << endl;
  cout << "bird: " << (long)&bird << endl;
  cout << "fish: " << (long)&fish << endl;
  cout << "i: " << (long)&i << endl;
  cout << "j: " << (long)&j << endl;
  cout << "k: " << (long)&k << endl;
} ///:~

我的可执行文件大约1MB。当我将项目配置从 Debug 更改为 Release 时,使用-O1 -Os标志(沿途剥离调试符号),二进制大小从1MB减少到544KB。

我不是一个“大小怪物”,但我只是想知道 - 有什么办法,我可以减少.exe的大小吗?我只是想,544KB对于这么简单的应用来说太多了。

14 个答案:

答案 0 :(得分:44)

这里的问题不在于图书馆,而在于图书馆的方式 图书馆是链接的。当然, iostream 是一个中等规模的图书馆,但我没有 认为它可能是如此巨大,以至于导致程序生成一个可执行文件 900KB大于使用C函数的类似值iostream。责怪的一个人 不是gcc而是static linking。更确切地说,g++ test.cpp -o test.exe SIZE: 935KB gcc test.cpp -o test.exe -lstdc++ SIZE: 64.3KB 应该受到指责。

您如何解释这些结果(使用您的程序):

g++ -v test.cpp -o test.exe // c++ program using stream functions  
gcc -v test.c -o test.exe   // c program that using printf  

正在生成不同大小的可执行文件,并且完全相同 构建选项。

答案在于 gcc 链接目标文件的方式 比较这两个命令的输出时:

   C++(iostream) | C(stdio)
-------------------------------
-Bstatic         |  (Not There)
-lstdc++         |  (Not There)
-Bdynamic        |  (Not There)
-lmingw32        | -lmingw32 
-lgcc            | -lgcc 
-lmoldname       | -lmoldname 
-lmingwex        | -lmingwex 
-lmsvcrt         | -lmsvcrt 
-ladvapi32       | -ladvapi32 
-lshell32        | -lshell32 
-luser32         | -luser32 
-lkernel32       | -lkernel32 
-lmingw32        | -lmingw32 
-lgcc            | -lgcc 
-lmoldname       | -lmoldname 
-lmingwex        | -lmingwex 
-lmsvcrt         | -lmsvcrt 

你会发现他们唯一不同的地方(除了通往中国的路径) 临时目标文件)在使用的选项中:

-Bstatic

你的罪魁祸首就在那里。 "AppData\\Local\\Temp\\ccMUlPac.o" -Bstatic -lstdc++ -Bdynamic .... 是选项 正好在目标文件之后,可能看起来像这样:

934KB

如果你玩这些选项并删除'不必要的'库,
您可以将可执行文件的大小从4.5KB减少到4.5KB最大
在我的情况下。我使用-Bdynamic -O标志为-lmingw32 和你的应用程序不能没有的最重要的图书馆,即 -lmsvcrt-lkernel324.5KB-5.5KB。您将获得 25KB 可执行文件 点。将其剥离为 10KB ,将 UPX 剥离至## This makefile contains all the options GCC passes to the linker ## when you compile like this: gcc test.cpp -o test.exe CC=gcc ## NOTE: You can only use OPTIMAL_FLAGS with the -Bdynamic option. You'll get a ## screenfull of errors if you try something like this: make smallest type=static OPTIMAL_FLAGS=-lmingw32 -lmsvcrt -lkernel32 DEFAULT_FLAGS=$(OPTIMAL_FLAGS) \ -lmingw32 \ -lgcc \ -lmoldname \ -lmingwex \ -lmsvcrt \ -ladvapi32 \ -lshell32 \ -luser32 \ -lkernel32 \ -lmingw32 \ -lgcc \ -lmoldname \ -lmingwex \ -lmsvcrt LIBRARY_PATH=\ -LC:\MinGW32\lib\gcc\mingw32\4.7.1 \ -LC:\mingw32\lib\gcc \ -LC:\mingw32\lib\mingw32\lib \ -LC:\mingw32\lib\ OBJECT_FILES=\ C:\MinGW32\lib\crt2.o \ C:\MinGW32\lib\gcc\mingw32\4.7.1\crtbegin.o COLLECT2=C:\MinGW32\libexec\gcc\mingw32\4.7.1\collect2.exe normal: $(CC) -c test.cpp $(COLLECT2) -Bdynamic $(OBJECT_FILES) test.o -B$(type) -lstdc++ -Bdynamic $(DEFAULT_FLAGS) $(LIBRARY_PATH) -o test.exe optimized: $(CC) -c -O test.cpp $(COLLECT2) -Bdynamic $(OBJECT_FILES) test.o -B$(type) -lstdc++ -Bdynamic $(DEFAULT_FLAGS) $(LIBRARY_PATH) -o test.exe smallest: $(CC) -c -O test.cpp $(COLLECT2) -Bdynamic $(OBJECT_FILES) test.o -B$(type) -lstdc++ -Bdynamic $(OPTIMAL_FLAGS) $(LIBRARY_PATH) -o test.exe ultimate: $(CC) -c -O test.cpp $(COLLECT2) -Bdynamic $(OBJECT_FILES) test.o -B$(type) -lstdc++ -Bdynamic $(OPTIMAL_FLAGS) $(LIBRARY_PATH) -o test.exe strip test.exe upx test.exe CLEAN: del *.exe *.o 左右。

这是一个可以玩的Makefile,用于踢:

// Not stripped or compressed in any way
make normal    type=static     SIZE: 934KB
make normal    type=dynamic    SIZE: 64.0KB

make optimized type=dynamic    SIZE: 30.5KB
make optimized type=static     SIZE: 934KB

make smallest  type=static     (Linker Errors due to left out libraries)
make smallest  type=dynamic    SIZE: 25.6KB 

// Stripped and UPXed
make ultimate type=dynamic    (UPXed from 9728 bytes to 5120 bytes - 52.63%)
make ultimate type=static     (Linker Errors due to left out libraries)

结果(YMMV):

-Bstatic

在默认构建选项中包含astyle的可能原因 是为了更好的表现。我尝试使用-Bdynamic构建{{1}}并获得了 平均速度降低1秒,即使应用程序是方式的 小于原版(400KB对比UPXed时为93KB)。

答案 1 :(得分:20)

#include <iostream>

导致许多标准库被链接,至少与g ++相关联。如果您真的关心可执行文件大小,请尝试使用printf或类似替换iostream的所有用法。这通常会为您提供更小,更快的可执行文件(我将您的可执行文件降低到大约6K),但代价是方便性和类型安全性。

答案 2 :(得分:10)

不确定对你有多大用处,但someone has done quite a lot of work on reducing the size of a simple Windows .exe

他们能够使用一些非常极端的方法创建一个简单的.exe,它将在133字节的现代版Windows上执行。

答案 3 :(得分:9)

你可以使用-s,我认为它也是mingw。 在cygwin上使用g ++ 3.4.4编译的一个简单的hello world应用程序生成了476872字节的可执行文件,再次使用-s(删除不必要的数据)进行编译,将相同的可执行文件减少到276480字节。

使用g ++ 4.3.2在cygwin上使用相同的hello world应用程序生成了16495字节的可执行文件,使用strip将大小减小到4608字节。 据我所知,最好使用更新版本的g ++。

MingW刚刚发布了gcc 4.4.0,所以如果可执行文件的大小很重要,那么我会考虑使用它。因为它表明-s可能有助于为您删除大部分调试信息,只有在生产用途时才建议这样做。

答案 4 :(得分:8)

你得到了C ++标准库,以及其他我认为静态链接的东西,因为mingw有自己的这些库的实现。

不要太在意它,当你制作更复杂的程序时,尺寸不会相应增长。

答案 5 :(得分:4)

基本上,你可以做任何事情来减少.exe大小和mingw的基本分布。 550kb的大小与你可以得到的一样小,因为mingw和gcc / g ++通常不会剥离未使用的函数。大约530kb来自msvcrt.a库。

如果您真的想进入它,您可以使用-ffunction-sections -fdata-sections编译器选项重建msvcrt.a库,然后使用-Wl, - gc-sections链接器选项链接你的应用程序,这应该能够从那里剥离很多东西。但是,如果您只是在学习C ++,那么重建该库可能会有点进步。

或者您可以使用MSVC,它非常适合剥离未使用的功能。用MSVC编译的相同代码产生一个10kb的exe。

答案 6 :(得分:2)

当你使用C ++标准库时,exe可以很快变大。如果在剥离调试符号后,您仍希望减小软件的大小,则可以使用类似UPX的打包器。但是,请注意,很久以前有些病毒在UPX上使用了一些防病毒软件。

答案 7 :(得分:2)

创建后,您始终可以在exe上运行UPX

答案 8 :(得分:2)

我使用Cygwin和g ++复制了你的测试。您的代码使用-O2编译为480k。在可执行文件上运行 strip 将其减少到280k。

一般来说,我怀疑你的问题是使用&lt; iostream&gt;头。这会导致链接相当大的库。另请注意,cout << x除了打印之外还有很多功能。有地方和溪流以及各种各样的引擎盖。

但是,如果具有较小的可执行文件大小是一个真正的关键任务目标,那么请避免它并使用printf或puts。如果不是,那么我会说支付iostream的一次性费用并完成它。

答案 9 :(得分:2)

如果您使用“nm”实用程序或其他显示.exe中的内容的程序,您会看到它包含了许多可能想要使用的类,但是您不。

答案 10 :(得分:2)

如果你需要小的可执行文件,Tiny C将为printf编译一个1536字节的可执行文件(“Hello world!”) TinyC只是C而不是C ++,并且已知编译速度更快,并且可执行文件的速度比gcc慢。

EDITED: 我刚试过一个cout&lt;“Hello World!”在DevC ++(捆绑Mingw 4.8和Ide)中,我得到了一个4.5 MB的可执行文件!!

答案 11 :(得分:1)

像msvc8这样的其他编译器甚至像borland c ++ 5.5.1这样的订单编译器是如何能够生成非常小的可执行文件但mingw gcc无法实现的呢?

我为以下各项快速编译了一个'hello world' 工具集并观察编译的可执行文件大小。请注意 在所有这些情况下,运行时库是静态链接的并且都是调试的 符号已被删除:

compiler toolchain            exe size                   exe size
                              (w/iostream header)        (w/cstdio printf)
-------------------------------------------------------------------------
Borland C++ 5.5.1             110kbyte                    52kbyte
MSVC 2008 express             102kbyte                    55kbyte
MinGW- GCC 3.4.5              277kbyte                    <10kbyte
MinGW- GCC 4.4.1              468kbyte                    <10kbyte

有趣的是,gcc 4.4.1的更高版本产生了比gcc3.4.5更大的可执行文件,可能是因为libstdc ++的版本不同。

那么在mingw的链接阶段真的没办法删除死代码吗?

答案 12 :(得分:0)

大小的主要部分源于使用相当广泛的运行时库。 所以在现实生活中,如果你有这样一个简单的应用程序,你实际上是在链接一个非常大的“死代码”。

据我所知,没有链接器标志可以跳过链接库的未使用部分。

我知道伪造一个较小的应用程序有两种方法:

  1. 使用动态链接。然后您的应用程序引用动态加载的库。你仍然需要完整的大小(实际上更多),但你的可执行文件要小得多。
  2. 使用executable compression系统。

答案 13 :(得分:0)

答案很晚,但对于到此为止搜索“ 减小问候世界二进制大小”的新c ++用户可能有用:

以下是我编译约20KB的hello_world.exe的步骤:

hello_world.cpp

#include <cstdio>  // not <iostream>
int main()
{
 printf("Hello, World");
 return 0;
}

g ++ cmd行

g++ -s hello_world.cpp -o hello_world.exe

UPX将二进制文件减少约50%:

upx -9 hello_world.exe

      File size         Ratio      Format      Name
 --------------------   ------   -----------   -----------
   40448 ->  20992      51.90%    win64/pe     hello_world.exe