2009-08-07 8 views
10

escribí programa¿Por qué los archivos de cabecera C no aumentan el tamaño del binario?

class MyClass { 
public: 
     int i; 
     int j; 
     MyClass() {}; 
}; 

int main(void) 
{ 
     MyClass inst; 
     inst.i = 1; 
     inst.j = 2; 
} 

y he realizado la siguiente C++.

# g++ program.cpp 
# ls -l a.out 
-rwxr-xr-x 1 root wheel 4837 Aug 7 20:50 a.out 

Entonces, #include d la iostream archivo de cabecera en el archivo de origen y que compilan de nuevo.

# g++ program.cpp 
# ls -l a.out 
-rwxr-xr-x 1 root wheel 6505 Aug 7 20:54 a.out 

El tamaño del archivo, como se esperaba, se incrementó.

También escribí el siguiente programa en C

int main(void) 
{ 
    int i = 1; 
    int j = 2; 
} 

y compilé

# gcc program.c 
# ls -l a.out 
-rwxr-xr-x 1 root wheel 4570 Aug 7 21:01 a.out 

Entonces, #include D, el archivo de cabecera stdio.h y compilados de nuevo

# gcc program.c 
# ls -l a.out 
-rwxr-xr-x 1 root wheel 4570 Aug 7 21:04 a.out 

Por extraño que parezca, el tamaño de los archivos ejecutables sigue siendo el mismo.

Respuesta

18

Al incluir iostream en su archivo fuente, el compilador necesita generar código para configurar y desmontar la biblioteca de E/S estándar de C++. Esto se puede ver observando la salida de nm, que muestra los símbolos (generalmente funciona) en su archivo de objeto:

$ nm --demangle test_with_iostream 
08049914 d _DYNAMIC 
08049a00 d _GLOBAL_OFFSET_TABLE_ 
08048718 t global constructors keyed to main 
0804883c R _IO_stdin_used 
     w _Jv_RegisterClasses 
080486d8 t __static_initialization_and_destruction_0(int, int) 
08048748 W MyClass::MyClass() 
     U std::string::size() [email protected]@GLIBCXX_3.4 
     U std::string::operator[](unsigned int) [email protected]@GLIBCXX_3.4 
     U std::ios_base::Init::Init()@@GLIBCXX_3.4 
     U std::ios_base::Init::~Init()@@GLIBCXX_3.4 
080485cc t std::__verify_grouping(char const*, unsigned int, std::string const&) 
0804874e W unsigned int const& std::min<unsigned int>(unsigned int const&, unsigned int const&) 
08049a3c b std::__ioinit 
08049904 d __CTOR_END__ 
... (remaining output snipped) ... 

(--demangle toma de C++ nombres de función "destrozado" por el compilador y produce más significativo . nombres la primera columna es la dirección, si la función está incluido en el ejecutable la segunda columna es el tipo "t" es el código en el "texto" segmento "U" son símbolos vinculados en de otros lugares;... en este caso, de la biblioteca compartida de C++)

Compare esto con las funciones generadas a partir de su archivo fuente sin incluir iostream:

$ nm --demangle test_without_iostream 
08049508 d _DYNAMIC 
080495f4 d _GLOBAL_OFFSET_TABLE_ 
080484ec R _IO_stdin_used 
     w _Jv_RegisterClasses 
0804841c W MyClass::MyClass() 
080494f8 d __CTOR_END__ 
... (remaining output snipped) ... 

Cuando el archivo de origen incluye iostream, el compilador genera varias funciones que no están presentes sin iostream.

Cuando el archivo de origen incluye sólo stdio.h, el binario generado es similar a la prueba sin iostream, ya que la norma C biblioteca E/S no necesita ninguna inicialización extra por encima y más allá de lo que ya está sucediendo en la biblioteca dinámica C. Puede ver esto mirando la salida nm, que es idéntica.

En general, sin embargo, tratando de intuir información acerca de la cantidad de código generado por un archivo fuente en particular en función del tamaño del ejecutable no va a ser significativo; hay demasiado que podría cambiar, y cosas simples como la ubicación del archivo fuente pueden cambiar el binario si el compilador incluye información de depuración.

También puede encontrar objdump útil para hurgar en el contenido de sus archivos ejecutables.

7

iostream incluye el código. stdio.h no.

Más específicamente, las siguientes definiciones en iostream (hay más de la lista, y varían según el compilador) objetos de referencia creado en la biblioteca estándar, que a su vez están vinculados en su código:

extern istream &cin; 
extern ostream &cout; 
extern ostream &cerr; 
extern ostream &clog; 
+2

las definiciones 'extern' no generan código; simplemente dejan que el compilador sepa que existen, por lo que el compilador puede hacer referencia a ellos si realmente se usan. – bdonlan

+0

Sí, una aclaración valiosa. –

+0

No creo que sean 'externos', e incluso si lo son, tiene que haber algunas definiciones. Sus ctors/dtors son una (tal vez la única) causa del aumento del tamaño del ejecutable. – sbi

9

Los archivos de cabecera son típicamente solo declaraciones y no resultan directamente en la generación de código máquina. El enlazador es lo suficientemente inteligente como para no extraer funciones no utilizadas del CRT, por lo que simplemente incluir stdio.h sin utilizar ninguna de sus funciones no daría como resultado más código en el ejecutable.

EDITAR: Pueden incluir funciones en línea, clases, etc. que incluyen código, pero que no deberían dar como resultado un aumento en el tamaño del ejecutable hasta que realmente se usen.

+1

Sí, pero esto no explica por qué '' aumenta el tamaño del archivo ejecutable. – sbi

2

Normalmente, los archivos de encabezado contienen solo información para el compilador, no el código real. Por ejemplo:

struct foo { 
    int x; 
}; 

Definiciones de estructuras como que a menudo aparecen en los encabezados, pero en realidad no causa código que se genera, ya que sólo dan la información compilador acerca de cómo manejar 'foo', en caso de verlo luego. Si no ve foo, la información se pierde cuando finaliza la compilación.

De hecho, tener cualquier cosa que hace generar código generalmente producirá un error. Por ejemplo:

void foo() { 
    printf("bar!\n"); 
} 

Si esto es en una cabecera, y se incluye en dos archivos .c, la función foo() se generará dos veces. Esto causará un error en el enlace. Es posible evitar el error si tiene una buena razón para hacerlo, pero en general se evita generar código en los encabezados si es posible.

Tenga en cuenta que una excepción aquí son los miembros en línea en C++.Por ejemplo:

class Foo { 
    void bar() { /* ... */ } 
}; 

Técnicamente hablando bar() se genera en cada archivo que incluye este código. El compilador realiza varios trucos para evitar el error (enlace débil, etc.). Esto de hecho puede aumentar el tamaño del ejecutable, y es probablemente lo que viste con <iostream>.

3

Hay algunas inicializaciones estáticas en iostream, mientras que en stdio.h sólo hay funciones y definiciones. Por lo tanto, incluir iostream producirá ejecutables de mayor tamaño.

0

archivo iostream declarado algunos objetos globales:

std :: cout, std :: cerr, std :: cin que son de tipo ostream.

A continuación, el compilador traerá esa clase y compilar la derecha en su binario final, añadiendo tanto a su tamaño.

1

La cabecera <iostream> viene con un par de objetos, 'std :: cin , 'std::cout, std::cerr y std::clog. Estas son instancias de clases que tienen constructores y destructores no triviales. Estos son códigos y tienen que estar vinculados. Esto es lo que aumenta el tamaño del ejecutable.

yo sepa, <cstdio> no viene con el código, así que por eso no hay aumento en el tamaño del ejecutable.

Cuestiones relacionadas