GCC C++“Hello World”程序 -> .exe 在 Windows 上编译时有 500kb 大。我怎样才能减小它的大小?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1042773/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:32:02  来源:igfitidea点击:

GCC C++ "Hello World" program -> .exe is 500kb big when compiled on Windows. How can I reduce its size?

c++optimizationgccnetbeanssize

提问by zeroDivisible

I just recently started learning C++ - I am using nuwen'sversion of MingW on Windows, using NetBeans as an IDE (I have also MSDN AA Version of MSVC 2008, though I don't use it very often).

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

When compiling this simple program:

编译这个简单的程序时:

#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;
} ///:~

my executable was about 1MB big. When I changed project configuration from Debugto Release, used -O1 -Os flags ( stripping debugging symbols along the way ), binary size was reduced from 1MB to 544KB.

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

I am not a "size freak", but I am just wondering - is there any way, that I could reduce .exe size even more? I just think, that 544KB is just too much for such a simple application ).

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

回答by Segfault

The problem here is not so much with the library as it is with the way the
library is linked. Granted, iostreamis a moderately huge library but I don't
think it can be so huge as to cause a program to generate an executable that is
900KBlarger than a similar one that uses Cfunctions. The one to blame
is not iostreambut gcc. More accurately, static linkingis to be blamed.

这里的问题不在于图书馆,而在于
图书馆的链接方式。诚然,iostream是一个相当大的库,但我
认为它不会大到导致程序生成
900KB比使用C函数的类似程序更大的可执行文件。罪魁祸首
不是iostream但是gcc。更准确地说,static linking是受到指责。

How would you explain these results(with your program):

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

g++ test.cpp -o test.exe              SIZE: 935KB
gcc test.cpp -o test.exe -lstdc++     SIZE: 64.3KB

Different sizes of executables are being generated with exactly the same
build options.

使用完全相同的
构建选项生成不同大小的可执行文件。

The answer lies in the way gcclinks the object files.
When you compare the outputs from these two commands:

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

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

you'll find out that the only places they differ(apart from the paths to the
temporary object files) is in the options used:

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

   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 

You've got your culprit right there at the top. -Bstaticis the option that comes
exactly after the object file which may look something like this:

你的罪魁祸首就在上面。-Bstatic是在
目标文件之后出现的选项,它可能如下所示:

"AppData\Local\Temp\ccMUlPac.o" -Bstatic -lstdc++ -Bdynamic ....

If you play around with the options and remove 'unnecessary' libraries,
you can reduce the size of the executable from 934KBto 4.5KBmax
in my case. I got that 4.5KBby using -Bdynamic, the -Oflag
and the most crucial libraries that your application can't live without, i.e
-lmingw32, -lmsvcrt, -lkernel32. You'll get a 25KBexecutable at that
point. Strip it to 10KBand UPXit to around 4.5KB-5.5KB.

如果您使用选项并删除“不必要的”库,则
可以将可执行文件的大小从 我的案例中减少934KB4.5KB最大值
。我得到了4.5KB通过-Bdynamic,该-O标志
和最重要的图书馆,你的应用程序不能没有,也就是说
-lmingw32-lmsvcrt-lkernel32。 届时您将获得一个25KB 的可执行文件
。将其剥离到10KB并将其UPX到大约4.5KB-5.5KB.

Here's a Makefile to play with, for kicks:

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

## 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.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.7.1\crtbegin.o

COLLECT2=C:\MinGW32\libexec\gcc\mingw32.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

Results(YMMV):

结果(YMMV):

// 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)

A possible reason for the inclusion of -Bstaticin the default build options
is for better performance. I tried building astylewith -Bdynamicand got
a speed decrease of 1 second on average, even though the application was way
smaller than the original(400KB vs 93KB when UPXed).

包含-Bstatic在默认构建选项中的一个可能原因
是为了更好的性能。我试图建立astyle-Bdynamic并获得
平均1秒的速度下降,即使应用程序是这样
比原来(400KB VS 93KB时UPXed)小。

回答by zeroDivisible

The

#include <iostream>

causes a lot of the standard library to be linked in, at least with g++. If you are really concerned about executable size, try replacing all uses of iostreams with printf or similar. This will typically give you a smaller, faster executable (I got yours down to about 6K) at the cost of convenience and type-safety.

导致许多标准库被链接,至少使用 g++。如果您真的很关心可执行文件的大小,请尝试用 printf 或类似方法替换 iostreams 的所有用法。这通常会给你一个更小、更快的可执行文件(我把你的可执行文件降到了大约 6K),但代价是方便和类型安全。

回答by Colin Pickard

Not sure how much use it will be to you, but someone has done quite a lot of work on reducing the size of a simple Windows .exe.

不确定它对您多大用处,但有人在减小简单 Windows .exe 的大小方面做了大量工作

They were able to create a simple .exe that will execute on a modern version of Windows in 133 bytes, by using some very extreme methods.

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

回答by Randai

You could use -s, which I believe is built into mingw as well. A simple hello world application compiled using g++ 3.4.4 on cygwin produced executable that was 476872 bytes, compiling again with -s (strips unnecessary data), reduced the same executable to 276480 bytes.

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

The same hello world application on cygwin using g++ 4.3.2 produced an executable of 16495 bytes, using strip reduced the size to 4608 bytes. As far as I can see, probably best to use more recent version of g++.

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

MingW has just released gcc 4.4.0, so if the executable size is important then I'd consider using that. As it indicates -s will probably help strip much of the debugging information out for you, that is only recommended if it is for production use.

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

回答by nos

You get the C++ standard library, and other stuff I guess, statically linked in as mingw has its own implementation of these libraries.

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

Don't worry so much about it, when you make more complex program, the size won't grow accordingly.

不用太担心,当你编写更复杂的程序时,大小不会相应地增长。

回答by Gerald

Basically, there's not really anything you can do to reduce that .exe size with a base distribution of mingw. 550kb is about as small as you can get it, because mingw and gcc/g++ in general are bad at stripping unused functions. About 530kb of that is from the msvcrt.a library.

基本上,您实际上无法通过 mingw 的基本分布来减小 .exe 的大小。550kb 大约是你能得到的那么小,因为 mingw 和 gcc/g++ 通常不擅长剥离未使用的函数。其中大约 530kb 来自 msvcrt.a 库。

If you really wanted to get into it, you might be able to rebuild the msvcrt.a library with -ffunction-sections -fdata-sections compiler options, and then use the -Wl,--gc-sections linker options when linking your app, and this should be able to strip a lot of that stuff out of there. But if you're just learning C++, rebuilding that library may be a bit advanced.

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

Or you could just use MSVC, which is great at stripping unused functions. That same bit of code compiled with MSVC produces a 10kb exe.

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

回答by Kaz Dragon

I replicated your test using Cygwin and g++. Your code compiled to 480k with -O2. Running stripon the executable reduced it to 280k.

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

In general, though, I suspect your problem is the use of the <iostream> header. This causes a fairly large library to be linked in. Also, note that cout << xdoes a lot more than just printing. There are locales and streams and all sorts of under-the-hood stuff.

不过,总的来说,我怀疑您的问题是 <iostream> 标头的使用。这会导致链接一个相当大的库。另外,请注意,cout << x这不仅仅是打印。有语言环境和流以及各种底层的东西。

If however, having a small executable size is a real, mission-critical objective, then avoid it and use printf or puts. If it's not, then I would say pay the one-time cost of iostream and be done with it.

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

回答by Raoul Supercopter

Well when you use C++ standard library, exe can get big really quickly. If after stripping debug symbol, you still want to reduce the size of your software, you can use a packer like UPX. But, be warned, some antivirus choke on exe packed with UPX as some virus used it a long time ago.

好吧,当您使用 C++ 标准库时,exe 可以很快变大。如果在剥离调试符号后,您仍想减小软件的大小,则可以使用像UPX这样的打包程序。但是,请注意,由于某些病毒很久以前使用过它,因此某些防病毒软件会阻塞带有 UPX 的 exe。

回答by SteveL

You could always run UPXon your exe after you have created it.

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

回答by Mike Dunlavey

If you use the "nm" utility or some other program that shows whats in your .exe, you'll see that it contains tons of classes that someone mightwant to use, but that you don't.

如果您使用“nm”实用程序或其他一些显示 .exe 内容的程序,您会看到它包含大量有人可能想要使用但您没有使用的类。