Подтвердить что ты не робот

Программа GCC С++ "Hello World" ->.exe имеет размер 500 КБ при компиляции в Windows. Как уменьшить размер?

Я только недавно начал изучать С++. Я использую версию nuwen в MingW в Windows, используя 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;
} ///:~

мой исполняемый файл был размером около 1 МБ. Когда я изменил конфигурацию проекта с Debug на Release, используемые флаги -O1 -Os (разделив символы отладки вдоль пути), размер двоичного файла был уменьшен с 1 МБ до 544 КБ.

Я не "урод с размерами", но мне просто интересно - есть ли способ, чтобы я мог уменьшить размер .exe еще больше? Я просто думаю, что 544 КБ слишком много для такого простого приложения).

4b9b3361

Ответ 1

#include <iostream>

приводит к тому, что большая часть стандартной библиотеки должна быть связана, по крайней мере, с g++. Если вы действительно обеспокоены размером исполняемого файла, попробуйте заменить все использование iostreams с помощью printf или аналогичного. Это, как правило, дает вам меньший, более быстрый исполняемый файл (я получил ваш примерно до 6K) за счет удобства и безопасности типов.

Ответ 2

Проблема здесь не столько в библиотеке, сколько с тем, как библиотека связана. Конечно, 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

Различные размеры исполняемых файлов генерируются точно так же строить варианты.

Ответ заключается в том, как 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  

вы узнаете, что единственные места, в которых они отличаются (кроме путей к временные файлы объектов) в используемых опциях:

   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 max
в моем случае. Я получил 4.5KB, используя -Bdynamic, флаг -O и самые важные библиотеки, которые ваше приложение не может прожить без, т.е. -lmingw32, -lmsvcrt, -lkernel32. Вы получите исполняемый файл 25 КБ на этом сайте точка. Разделите его на 10KB и UPX до 4.5KB-5.5KB.

Здесь 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\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

Результаты (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)

Возможная причина включения -Bstatic в параметры построения по умолчанию
для лучшей производительности. Я попытался построить astyle с помощью -Bdynamic и получил снижение скорости в среднем на 1 секунду, хотя приложение было способным меньше, чем оригинал (400 КБ против 93 КБ при UPXed).

Ответ 4

Вы получаете стандартную библиотеку С++ и другие вещи, которые, как я полагаю, статически связаны, поскольку mingw имеет собственную реализацию этих библиотек.

Не волнуйтесь об этом, когда вы делаете более сложную программу, размер не будет расти соответственно.

Ответ 5

Вы можете использовать -s, который, как я полагаю, также встроен в mingw. Простое приложение hello world, скомпилированное с использованием g++ 3.4.4 на Cygwin, создало исполняемый файл, который составлял 476872 байта, скомпилировав снова с -s (удалил ненужные данные), уменьшил тот же исполняемый файл до 276480 байт.

То же самое приложение hello world на cygwin с использованием g++ 4.3.2 создало исполняемый файл из 16495 байт, используя полосу, уменьшив размер до 4608 байт. Насколько я вижу, лучше всего использовать более новую версию g++.

MingW только что выпустил gcc 4.4.0, поэтому, если размер исполняемого файла важен, я бы подумал об использовании этого. Поскольку это указывает, что -s, вероятно, поможет удалить большую часть отладочной информации для вас, это рекомендуется только в том случае, если оно используется для производства.

Ответ 6

В принципе, на самом деле вы ничего не можете сделать, чтобы уменьшить размер .exe с базовым распределением mingw. 550kb примерно такой же маленький, как вы можете его получить, потому что mingw и gcc/g++ в целом плохо работают при удалении неиспользуемых функций. Около 530 КБ это из библиотеки msvcrt.a.

Если вам действительно захотелось проникнуть в него, вы можете восстановить библиотеку msvcrt.a с параметрами компилятора -ffunction-sections -fdata-sections, а затем использовать параметры компоновщика -Wl, -gc-sections, если связывая ваше приложение, и это должно уметь отнимать много всего этого. Но если вы просто изучаете С++, перестройка этой библиотеки может быть немного продвинутой.

Или вы можете просто использовать MSVC, что отлично подходит для удаления неиспользуемых функций. Тот же бит кода, скомпилированный с помощью MSVC, создает exe 10kb.

Ответ 7

Хорошо, когда вы используете стандартную библиотеку С++, exe может быстро стать большим. Если после удаления символа отладки вы все же хотите уменьшить размер своего программного обеспечения, вы можете использовать упаковщик, например UPX. Но, будьте осторожны, некоторые антивирусные дроссели на exe, упакованные с UPX, поскольку какой-то вирус использовал его давным-давно.

Ответ 8

Вы всегда можете запустить UPX на своем exe после того, как вы его создали.

Ответ 9

Я повторил ваш тест с помощью Cygwin и g++. Ваш код скомпилирован до 480k с помощью -O2. Выполнение strip в исполняемом файле уменьшило его до 280k.

В общем, я подозреваю, что ваша проблема заключается в использовании заголовка <iostream> . Это приводит к связыванию довольно большой библиотеки. Также обратите внимание, что cout << x делает намного больше, чем просто печать. Есть локали и потоки и всевозможные вещи под капотом.

Если, однако, небольшой размер исполняемого файла является реальной, критически важной задачей, то избегайте его и используйте printf или puts. Если это не так, я бы сказал, заплатить единовременную стоимость iostream и сделать с ней.

Ответ 10

Если вам нужны небольшие исполняемые файлы, Tiny C скомпилирует 1536 байтов для printf ( "Hello world!" ) TinyC - это только C, а не С++ и, как известно, быстрее компилируется и дает более медленные исполняемые файлы, чем gcc.

Редакция: Я только что попробовал cout & Hello World! в DevС++ (пакеты Mingw 4.8 и Ide), и я получил исполняемый файл объемом 4,5 МБ.

Ответ 11

Если вы используете утилиту "nm" или какую-либо другую программу, которая показывает, что в вашем .exe, вы увидите, что она содержит тонны классов, которые кто-то может захотеть использовать, но вы этого не делаете.

Ответ 12

Как получается, что другие компиляторы, такие как msvc8 или даже компилятор заказов, такие как borland С++ 5.5.1, способны создавать очень маленькие исполняемые файлы, но mingw gcc не может?

Я быстро компилировал "мир привет" для каждого из следующих инструментов и наблюдал скомпилированный размер исполняемого файла. Обратите внимание, что в во всех этих случаях библиотека времени выполнения статически связана и все отладки символы были удалены:

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, возможно, из-за другой версии libstdС++.

Так действительно ли вообще невозможно удалить мертвый код во время фазы связывания для mingw?

Ответ 13

Большая часть размера связана с использованием довольно обширных библиотек времени исполнения. Поэтому в реальной жизни вы на самом деле связываете очень большую часть "мертвого кода", если у вас есть такое простое приложение.

Насколько я знаю, отсутствуют флаги компоновщика для пропуска неиспользуемых частей связанной библиотеки.

Есть два способа, которыми я знаю, чтобы подделать меньшее приложение:

  • Используйте динамическое связывание. Тогда ваше приложение относится к динамически загружаемой библиотеке. Вам по-прежнему нужен полный размер (на самом деле больше), но у вас гораздо меньше исполняемого файла.
  • Используйте исполняемое сжатие.