visual sharp que programación lenguaje historia definicion caracteristicas c# java c++ compiler-construction transition

c# - programación - que es c sharp



Comprender los compiladores C++ desde una perspectiva Java/C# (4)

De hecho, recomendaría evitar las explicaciones sobre los compiladores de C ++ y buscar explicaciones en los compiladores de C. En mi experiencia, estos se explican mejor y evitan confundirte con problemas de OOP. Busque material sobre la compilación C por separado. Te habría recomendado un gran folleto de diapositivas de mi alma mater, pero no está en inglés.

La principal diferencia entre la compilación de C y Java / C # es que la compilación no crea una entidad resuelta. En otras palabras, cuando compila en Java, el compilador busca archivos de clase ya compilados para cualquier clase referenciada, y se asegura de que todo esté disponible y sea coherente. La suposición subyacente es que cuando finalmente ejecute el programa, esos archivos también estarán disponibles.

Un archivo C compilado, por otro lado, es solo una "promesa". Se basa en una declaración de cómo se verían las dependencias (en forma de declaraciones de funciones), pero no hay garantías de que estén definidas en ningún lugar. El cambio de mentalidad más difícil que debe hacer es pensar en un archivo C no solo como ese archivo, sino más bien como la agregación de ese archivo con todo lo que incluye (es decir, lo que genera el preprocesador). En otras palabras, el compilador no ve archivos de encabezado, parece un archivo grande. El compilador realiza un seguimiento en el archivo de objeto generado de todo lo que "aún falta". Más adelante, en el momento del enlace, el enlazador soluciona esto tratando de llenar todos los espacios en blanco con materiales de los diferentes archivos objeto.

Soy un programador Java / C # moderadamente experimentado, y recientemente comencé a aprender C ++. El problema es que tengo problemas para entender cómo estructurar los diversos archivos de encabezado y código. Esto parece deberse principalmente a mi falta de comprensión sobre cómo el compilador vincula todo junto. Intenté leer algunos libros de texto, pero mis ideas preconcebidas están fuertemente teñidas por mis conocimientos de Java y C #. Por ejemplo, estoy teniendo dificultades para entender el hecho de que los métodos y similares se pueden definir en un espacio de nombres en lugar de solo en una definición de clase.

He encontrado muchas guías C ++ -> Java / C #, pero prácticamente nada para ir en otra dirección. ¿Existen buenos recursos para facilitar la transición Java / C # -> C ++, particularmente con respecto a la comprensión del proceso de compilación?


Esto es algo que me confundió cuando comencé a usar C también. Los libros no hacen un buen trabajo al describir el uso correcto de los encabezados frente a los archivos de códigos.

El compilador funciona cargando cada archivo .cpp y compilándolo independientemente de todos los demás. El primer paso en la compilación es cargar todos los encabezados a los que hace referencia las sentencias #include. Puedes pensar que está haciendo una inserción textual de foo.h donde sea que haya un #include "foo.h".

¿Cuáles son las implicaciones de esto para la estructura de sus archivos? Los archivos de encabezado deben tener las partes del programa necesarias para que se refieran a otros archivos .cpp. Como regla general, las implementaciones no deberían estar en archivos de encabezado. Esto causará problemas. Los archivos de encabezado deben incluir declaraciones de clases, funciones y variables globales (si debe usarlas).


Las preguntas frecuentes de C ++ son un recurso excelente sobre todas las idiosincrasias de C ++, pero es probable que sea un poco más avanzado de lo que estás buscando: la mayoría de las preguntas (no solo las respuestas) son misterios incluso para desarrolladores bastante experimentados de C ++.

Creo que si buscas en Google tutoriales en C ++, podrás encontrar algo. También es posible que desee intentar aprender el lenguaje ensamblador (o al menos obtener una introducción rápida sobre cómo realmente suceden las cosas en un microprocesador), ya que tanto C como C ++ están bastante cerca del hardware en la forma en que hacen las cosas. De ahí viene su velocidad y poder, pero tiene el precio de algunas de las mejores abstracciones que Java ofrece.

Puedo tratar de responder a sus preguntas específicas, pero no sé qué tan bien lo haré.

Una de las claves para comprender la relación entre los archivos de encabezado y los archivos cpp es entender la idea de una "unidad de traducción". Un archivo de clase Java puede considerarse una unidad de traducción, ya que es la unidad básica compilada en forma binaria. En C ++, casi todos los archivos cpp son unidades de traducción (existen excepciones si haces cosas raras).

Se puede incluir un archivo de encabezado en varias unidades de traducción (y se debe incluir en todas partes que use lo que se define en el encabezado). La directiva #include literalmente solo hace una sustitución de texto: el contenido del archivo incluido se inserta textualmente donde está la directiva #include. Normalmente desea que su interfaz de clase esté definida en el archivo de encabezado y la implementación en el archivo cpp. Esto se debe a que no desea exponer sus detalles de implementación a otras unidades de traducción que pueden incluir el encabezado. En C ++, todo, incluidas las clases, no son realmente objetos ricos, sino fragmentos de memoria a los que el compilador asigna significado ... al compilar la misma información de encabezado en cada unidad de traducción, el compilador garantiza que todas las unidades de traducción tengan el la misma comprensión de lo que representa un pedazo de memoria. Debido a la falta de datos abundantes después del tiempo de compilación, cosas como la reflexión son imposibles.

El segundo paso en el proceso de compilación de C ++ es vincular, que es donde el enlazador toma todas las unidades de traducción compiladas y busca símbolos (generalmente llamadas de función, pero también variables) utilizados en una unidad de traducción pero no definidos allí. Luego busca otra unidad de traducción que defina ese símbolo y "los vincule", de modo que todas las llamadas a una función en particular se dirijan a la unidad de traducción que lo define.

En el caso de los métodos de clase, deben llamarse a través de una instancia de clase, que está detrás de las escenas solo como un puntero a una pieza de memoria. Cuando el compilador ve este tipo de llamadas a métodos, emite un código que llama a una función, pasando implícitamente el puntero, conocido como this puntero, a la función como primer argumento. Puede tener funciones que no pertenecen a las clases (no a los métodos, como dijo, porque un método es propiamente una función miembro de una clase y, por lo tanto, no puede existir sin una clase) porque el vinculador no tiene ningún concepto de una clase. Verá una unidad de traducción que define una función y otra que llama a una función y las une.

Eso terminó siendo mucho más largo de lo que esperaba, y por supuesto es una simplificación excesiva, pero es preciso según mi leal saber y entender y el nivel de detalle proporcionado ... espero que ayude a algunos. Al menos debería darle un punto de partida para algunos googlear.


Es posible que desee saber por qué la compilación y los enlaces también están separados (ya que no veo publicaciones que lo expliquen, y es motivo de mucha confusión no conocer las razones subyacentes de las cosas).

El enlace y la compilación se completan por separado porque (y puede haber más de una razón) de la necesidad de hacer llamadas a la biblioteca. si definió o algo de su estilo, el código que implementa los prototipos de funciones en esos encabezados es parte de la biblioteca que ya está compilada y se encuentra como código objeto en alguna parte. si en su lugar se utilizara un proceso de compilación gigante, necesitaría tener la fuente para esas llamadas a la biblioteca, así como más tiempo durante la compilación porque también estaría compilando el código de la biblioteca.