Programa "Hola Mundo" de GCC C ++ - > .exe tiene un tamaño de 500 kb cuando se compila en Windows. ¿Cómo puedo reducir su tamaño?

StackOverflow https://stackoverflow.com/questions/1042773

Pregunta

Hace poco comencé a aprender C ++: estoy usando versión de nuwen de MingW en Windows, usando NetBeans como un IDE (también tengo la versión MSDN AA de MSVC 2008, aunque no la uso muy a menudo).

Al compilar este sencillo programa:

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

mi ejecutable tenía aproximadamente 1 MB de tamaño. Cuando cambié la configuración del proyecto de Debug a Release , utilicé -O1 -Os flags (eliminando símbolos de depuración en el camino), el tamaño binario se redujo de 1 MB a 544 KB.

No soy un monstruo del tamaño, pero me pregunto: ¿hay alguna forma de que pueda reducir aún más el tamaño de .exe? Solo creo que 544 KB es demasiado para una aplicación tan simple).

¿Fue útil?

Solución

El

#include <iostream>

hace que gran parte de la biblioteca estándar se vincule, al menos con g ++. Si realmente le preocupa el tamaño ejecutable, intente reemplazar todos los usos de iostreams con printf o similar. Por lo general, esto le dará un ejecutable más pequeño y rápido (reduje el suyo a aproximadamente 6K) a costa de conveniencia y seguridad de escritura.

Otros consejos

El problema aquí no es tanto con la biblioteca como con la forma en que el
La biblioteca está vinculada. De acuerdo, iostream es una biblioteca moderadamente grande, pero no
cree que puede ser tan grande como para hacer que un programa genere un ejecutable que es
900KB más grande que uno similar que utiliza funciones C . El culpable
no es iostream sino gcc . Más exactamente, se debe culpar a enlace estático .

¿Cómo explicaría estos resultados (con su programa)?

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

Se generan diferentes tamaños de ejecutables con exactamente el mismo
Opciones de construcción.

La respuesta está en la forma en que gcc vincula los archivos de objetos.
Cuando compara las salidas de estos dos comandos:

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

descubrirá que los únicos lugares en los que difieren (aparte de los caminos hacia el
archivos de objetos temporales) está en las opciones utilizadas:

   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 

Tienes a tu culpable allí mismo en la parte superior. -Bstatic es la opción que viene
exactamente después del archivo de objeto que puede verse así:

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

Si juega con las opciones y elimina las bibliotecas 'innecesarias',
puede reducir el tamaño del ejecutable de 934KB a 4.5KB max
en mi caso. Obtuve ese 4.5KB usando -Bdynamic , la bandera -O
y las bibliotecas más importantes sin las que su aplicación no puede vivir, es decir,
-lmingw32 , -lmsvcrt , -lkernel32 . Obtendrá un 25KB ejecutable en ese
punto. Pele a 10KB y UPX a 4.5KB-5.5KB .

Aquí hay un Makefile para jugar, por diversión:

## 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

Resultados (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)

Una posible razón para la inclusión de -Bstatic en las opciones de compilación predeterminadas
Es para un mejor rendimiento. Intenté construir astyle con -Bdynamic y obtuve
una disminución de la velocidad de 1 segundo en promedio, a pesar de que la aplicación estaba muy avanzada
más pequeño que el original (400 KB frente a 93 KB cuando UPXed).

No estoy seguro de cuánto le servirá, pero alguien ha trabajado bastante para reducir el tamaño de un simple .exe de Windows .

Pudieron crear un .exe simple que se ejecutará en una versión moderna de Windows en 133 bytes, utilizando algunos métodos muy extremos.

Podrías usar -s, que creo que también está integrado en mingw. Una simple aplicación hello world compilada usando g ++ 3.4.4 en cygwin produjo un ejecutable de 476872 bytes, compilando nuevamente con -s (elimina datos innecesarios), redujo el mismo ejecutable a 276480 bytes.

La misma aplicación hello world en cygwin usando g ++ 4.3.2 produjo un ejecutable de 16495 bytes, usando strip redujo el tamaño a 4608 bytes. Por lo que puedo ver, probablemente sea mejor usar una versión más reciente de g ++.

MingW acaba de lanzar gcc 4.4.0, por lo que si el tamaño ejecutable es importante, entonces consideraría usarlo. Como indica -s, probablemente ayudará a eliminar gran parte de la información de depuración para usted, eso solo se recomienda si es para uso de producción.

Obtiene la biblioteca estándar de C ++, y otras cosas, supongo, estáticamente vinculada ya que mingw tiene su propia implementación de estas bibliotecas.

No te preocupes tanto por eso, cuando haces un programa más complejo, el tamaño no crecerá en consecuencia.

Básicamente, en realidad no hay nada que pueda hacer para reducir ese tamaño .exe con una distribución base de mingw. 550kb es casi tan pequeño como puede obtenerlo, porque mingw y gcc / g ++ en general son malos para eliminar las funciones no utilizadas. Aproximadamente 530kb de eso es de la biblioteca msvcrt.a.

Si realmente quisiera entrar en él, podría reconstruir la biblioteca msvcrt.a con las opciones del compilador -ffunction-secciones -fdata-secciones, y luego usar las opciones de enlace -Wl, - gc-secciones cuando vincular su aplicación, y esto debería ser capaz de eliminar muchas de esas cosas de allí. Pero si solo está aprendiendo C ++, la reconstrucción de esa biblioteca puede ser un poco avanzada.

O simplemente podría usar MSVC, que es excelente para eliminar funciones no utilizadas. Ese mismo bit de código compilado con MSVC produce un exe de 10kb.

Bueno, cuando usas la biblioteca estándar de C ++, exe puede hacerse grande muy rápido. Si después de eliminar el símbolo de depuración, aún desea reducir el tamaño de su software, puede usar un empaquetador como UPX . Pero, tenga en cuenta, algunos antivirus se atragantan con el exe lleno de UPX ya que algunos virus lo usaban hace mucho tiempo.

Siempre puede ejecutar UPX en su exe después de haberlo creado.

Si usa el " nm " o algún otro programa que muestre lo que hay en su .exe, verá que contiene toneladas de clases que alguien podría querer usar, pero que usted no.

Repliqué tu prueba usando Cygwin y g ++. Su código compilado a 480k con -O2. Ejecutar strip en el ejecutable lo redujo a 280k.

Sin embargo, en general, sospecho que su problema es el uso de < iostream > encabezamiento. Esto provoca que se vincule una biblioteca bastante grande. Además, tenga en cuenta que cout < < x hace mucho más que simplemente imprimir. Hay configuraciones regionales y transmisiones y todo tipo de cosas ocultas.

Si, sin embargo, tener un tamaño ejecutable pequeño es un objetivo real de misión crítica, entonces evítelo y use printf o put. Si no es así, yo diría que pague el costo único de iostream y termine con él.

Si necesita ejecutables pequeños, Tiny C compilará un ejecutable de 1536 bytes para un printf (" ¡Hola mundo! ") TinyC es solo C, no C ++ y se sabe que compila más rápido y proporciona ejecutables más lentos que gcc.

EDITADO: Acabo de probar un cout < '' Hello World! '' en DevC ++ (paquetes Mingw 4.8 y un Ide) y obtuve un ejecutable de 4,5 MB !!

¿Cómo es que otros compiladores como msvc8 o incluso un compilador de pedidos como borland c ++ 5.5.1 son capaces de producir ejecutables muy pequeños pero mingw gcc no puede hacerlo?

Hice una compilación rápida de un 'hola mundo' para cada uno de los siguientes conjuntos de herramientas y observó el tamaño ejecutable compilado. Tenga en cuenta que en En todos estos casos, la biblioteca de tiempo de ejecución está estáticamente vinculada y toda la depuración los símbolos se han eliminado:

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

Lo interesante es que la versión posterior de gcc 4.4.1 produce un ejecutable aún más grande que gcc3.4.5, probablemente debido a una versión diferente de libstdc ++.

Entonces, ¿realmente no hay forma de eliminar el código muerto durante la fase de enlace para mingw?

La mayor parte del tamaño proviene del uso de bibliotecas de tiempo de ejecución bastante extensas. Entonces, en la vida real, en realidad estás vinculando una gran parte de 'código muerto' si tienes una aplicación tan simple.

Hasta donde sé, no hay indicadores de enlace para omitir las partes no utilizadas de una biblioteca vinculada.

Hay dos maneras que conozco para falsificar una aplicación más pequeña:

  1. Usar enlaces dinámicos. Entonces su aplicación se refiere a la biblioteca cargada dinámicamente. Todavía necesita el tamaño completo (en realidad más) pero tiene un ejecutable mucho más pequeño.
  2. Utilice un sistema de compresión ejecutable .
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top