c++ - que - Diferencia entre el objeto de código y el archivo ejecutable
que es un programa ejecutable (4)
Soy un principiante de C ++ y estoy estudiando los conceptos básicos del lenguaje. Hay un tema en mi libro sobre el compilador y mi problema es que no puedo entender lo que el texto quiere decir:
C ++ es un lenguaje compilado, por lo que necesita traducir el código fuente en un archivo que la computadora pueda ejecutar. Este archivo lo genera el compilador y se denomina código objeto (.obj), pero un programa como el programa "hola mundo" está compuesto por una parte que escribimos y una parte de la biblioteca de C ++. El vinculador vincula estas dos partes de un programa y produce un archivo ejecutable (.exe).
¿Por qué mi libro dice que el archivo que ejecuta la computadora es el que tiene el sufijo obj (el código del objeto) y luego dice que es el que tiene el sufijo exe ?
Código de objeto (dentro de un archivo de objeto): salida de un compilador destinado como entrada para un enlazador (para que el enlazador produzca un código ejecutable).
Ejecutable: un programa listo para ejecutarse (ejecutarse) en una computadora
La especificación C ++ es un documento técnico en inglés. Para C++11 eche un vistazo a n3337 (o gaste mucho dinero para comprar el estándar ISO de bolsillo). En teoría, no necesita una computadora para ejecutar un programa de C ++ (podría usar un grupo de esclavos humanos, pero eso sería poco ético, ineficiente y poco confiable).
Podría tener una implementación de C ++ que sea un interpreter , no un compiler (por ejemplo, Ch by SoftIntegration )
Si instala Linux en su computadora portátil (lo cual recomiendo que haga a todos los estudiantes), entonces podría tener varios compiladores de C ++ de software libre , en particular GCC y Clang/LLVM (usando los comandos g++
y clang
respectivamente). Los archivos de origen tienen el sufijo .cc
, o .cxx
, o .cpp
, o incluso .C
(prefiero .cc
), y puede pedirle al compilador que maneje un archivo de algún otro sufijo como un archivo de origen en C ++ (pero eso no es convencional). Luego, tanto los archivos de objetos (con sufijo .o
) como los executables comparten el mismo formato ELF . Convencionalmente, los ejecutables no tienen ningún sufijo (por ejemplo, g++
es un ejecutable binario, no hace mucho excepto iniciar otros procesos como cc1plus
-el compilador propiamente cc1plus
, as
-el assembler , ld
-el linker etc ...)
En todos los casos recomiendo encarecidamente:
- para habilitar todas las advertencias e información de depuración durante la compilación (por ejemplo, use
g++ -Wall -g
....) - para mejorar su código fuente hasta que no tenga advertencias
- para aprender a usar el depurador (
gdb
) - para poder construir tu programa en la línea de comandos
- utilizar un sistema de control de versiones como git
- usar un buen editor como
emacs
,gedit
,geany
ogvim
- una vez que esté escribiendo programas en varios archivos de origen, aprenda cómo usar un constructor como
make
- para aprender C ++ 11 (o incluso quizás C++14 ) en lugar de los estándares más antiguos de C ++
- para aprender también otros lenguajes de programación (Ocaml, Scheme, Haskell, Prolog, Scala, ...) ya que mejorarían tu forma de pensar y tu forma de codificar en C ++
- para estudiar el código fuente de varios programas gratuitos codificados en C ++
- para leer la documentación de cada función que está utilizando, por ejemplo, en cppreference o en las páginas del manual (para Linux)
- para entender qué es un comportamiento indefinido (el hecho de que su programa funcione a veces no lo hace correcto).
Concretamente, en Linux puede editar su programa Hello World (archivo hello.cc
) con gedit
o emacs
(con un comando como gedit hello.cc
), etc., compilarlo usando g++ -Wall -g hello.cc -o hello
comando, gdb ./hello
con gdb ./hello
, y repita (no olvide usar los comandos git
para el control de versiones).
A veces tiene sentido generar algún código C ++, por ejemplo, mediante algún script de shell, Python o awk
(o incluso por su propio programa codificado en C ++ que genera código C ++).
Además, entienda que un IDE no es un compilador (pero ejecuta el compilador por usted).
Los archivos de objetos se compilan de origen en lenguaje de máquina binario, pero contienen referencias externas no resueltas (como, por ejemplo, printf
). Es posible que deban vincularse con otros archivos de objetos, bibliotecas de terceros y casi siempre con la biblioteca de tiempo de ejecución C / C ++.
En Unix, los archivos de objetos y archivos ejecutables tienen el mismo formato COFF. La única diferencia es que los archivos de objetos tienen referencias externas no resueltas, mientras que todos los archivos no tienen.
Los lenguajes utilizados para la programación de computadoras junto con las diversas herramientas de desarrollo de software han evolucionado a lo largo de los años.
Las primeras computadoras fueron programadas con números ingresados por interruptores en una consola.
Luego, la gente comenzó a desarrollar lenguajes y software que podrían usarse para crear software de manera más fácil y rápida. El primer desarrollo importante fue crear un lenguaje ensamblador donde cada línea de origen fue convertida por un programa de computadora en una instrucción de código de máquina. Junto con esto vino el desarrollo de enlazadores (que vinculan piezas de código de máquina en piezas más grandes). Los ensambladores se mejoraron agregando una macro o una instalación de preprocesador algo así como el preprocesador C / C ++, aunque diseñado para el lenguaje ensamblador.
Luego, las personas crearon lenguajes de programación que se parecían más a lenguajes escritos que a ensambladores (FORTRAN y COBOL y ALGOL, por ejemplo). Estos idiomas eran más fáciles de leer y una sola línea de origen se podía convertir en varias instrucciones de máquina, por lo que era más productivo escribir programas de computadora en estos idiomas en lugar de ensamblarlos.
El lenguaje de programación C fue un refinamiento posterior utilizando las lecciones aprendidas de los lenguajes de programación tempranos como FORTRAN. Y C usó algunas de las mismas herramientas de desarrollo de software que ya existían, como los enlazadores que ya existían. Aún más tarde se inventó C ++, comenzando como un refinamiento de C que introduce instalaciones orientadas a objetos. De hecho, el primer compilador de C ++ fue realmente un traductor de C ++ que tradujo el código fuente de C ++ al código fuente de C, que luego se compiló con un compilador de C ++. Sin embargo, el C ++ moderno se compila directamente al código de máquina para proporcionar la funcionalidad completa del estándar de C ++ con plantillas, lambdas y todo lo demás con C ++ 11 y posteriores.
Los pasos básicos para crear una aplicación desde un archivo de origen C o C ++ son los siguientes: (1) se crean los archivos de origen, (2) se compilan los archivos de origen (que son realmente dos pasos, preprocesador y compilación), (3) los archivos de objetos creados por el compilador C / C ++ están vinculados para crear el .exe
Así que tienes estos pasos para transformar una versión del programa de computadora a otra. La fuente de C ++ se compila para producir los archivos objeto. Los archivos de objeto se vinculan para producir el archivo ejecutable.
Cuando ejecutas un programa ejecutas el archivo ejecutable. El archivo ejecutable contiene varios tipos de información. La primera es las instrucciones de la máquina que son el resultado de compilar el código fuente de C ++. La otra es la información que utiliza el cargador para saber cómo cargar el ejecutable en la memoria.
En los viejos tiempos, hace mucho tiempo, todas las bibliotecas y los archivos de objetos estaban vinculados entre sí en un archivo ejecutable, el cargador cargaba el archivo ejecutable y el cargador era bastante simple.
Luego, la gente inventó bibliotecas compartidas y bibliotecas de enlaces dinámicos, lo que requirió que el cargador fuera más complejo. El cargador no solo tiene que cargar el archivo ejecutable en la memoria para que pueda comenzar a ejecutarse, sino que también debe encontrar las bibliotecas compartidas o las bibliotecas de enlace dinámico que también son necesarias y cargarlas. Y el cargador también tiene que hacer una cierta cantidad de enlaces de los componentes adicionales, las bibliotecas compartidas, por lo que el cargador hace mucho más de lo que solía hacer.