¿Cuál es la diferencia entre un compilador y un intérprete?

Los compiladores, ensambladores e intérpretes son todos los programas que toman el código fuente de un lenguaje de programación, escrito por un programador, y lo hacen funcionar en una computadora.

Las CPU de las computadoras son demasiado primitivas para ejecutar directamente los comandos de cualquier lenguaje que un humano realmente usaría. Solo pueden ejecutar una cosa llamada ‘código de máquina’, que son los binarios y ceros en los que todos piensan, cuando decimos ‘computadora’.

Las líneas entre los tres son bastante borrosas, en estos días. Tradicionalmente, fue así:

  • Un compilador es un programa que convierte el código fuente de un lenguaje de programación en código máquina ejecutable para una CPU.
  • Un ensamblador hace lo mismo, excepto que funciona en el lenguaje ensamblador utilizado para describir el código de máquina de una CPU. Este es un lenguaje mucho más simple, por lo que un ensamblador es mucho más sencillo de escribir que un compilador.
  • Un intérprete toma cada línea de código fuente de un lenguaje de programación y la ejecuta allí y luego. Continúa haciendo lo mismo para la siguiente línea después.

En esta visión tradicional del mundo, los intérpretes ejecutan programas más lentamente que los compilados o ensamblados.

Pero en el mundo moderno, ya no es del todo cierto.

Toma Java, por ejemplo.

El código fuente de Java es compilado por un programa llamado javac. Pero no produce código de máquina ejecutable para una CPU. Produjo ‘código de bytes’, que es una especie de código de máquina universal.

Debido a que el código de bytes no se ejecuta de forma nativa en casi todas las CPU, debe ejecutarse en algún otro programa que lo convierta. en java, esta es la máquina virtual Java, o JVM.

La primera JVM de 1997 fue intérprete.

Entonces compiló Java-> Bytecode, que luego fue interpretado.

Las JVM modernas toman el código de bytes y analizan qué partes se ejecutan con mayor frecuencia, luego compilan el código de bytes en el código de máquina real para esa CPU. Por lo tanto, se cruzan entre un compilador y un intérprete, y una CPU virtual.

Las tecnologías Microsoft .NET son similares a esto.

Los detalles parecen un poco confusos, pero solo recuerde: todos convierten lo que los programadores escriben en algo que una computadora realmente puede ejecutar.

Muchas, muchas respuestas geniales aquí. Solo puedo agregar una cosa:

Compilador – un programa

Intérprete: un programa que ejecuta un programa

Además, como muchos han dicho que ha sido confundido por la tecnología moderna. Usemos el camino de regreso a la máquina para dirigirnos hacia 1980.

Las primeras “computadoras domésticas” a menudo arrancaban y comenzaban lo que es un intérprete BÁSICO. BASIC era un lenguaje fácil de aprender, por lo que fue elegido como una forma de proporcionar a los usuarios domésticos un sistema que pudieran programar. Significa Código de Instrucción Simbólico para todo uso para principiantes y puede leerlo todo en la página BÁSICA de Wikipedia.

Desmonté el compilador BASIC en aproximadamente tres de estos primeros sistemas hasta que entiendo absolutamente lo que estaban haciendo. Tenían una tabla de búsqueda para convertir nombres de variables en ubicaciones de memoria. Tenían una o más pilas para realizar un seguimiento de los bucles anidados y los parámetros, etc. Se reservaron memoria para las matrices cuando usó la palabra clave DIM. Lo que hicieron fue esto:

1) Tokenize la fuente a tokens internos que representan todas las palabras clave BASIC. Algunos tokenizaron todo el programa y lo almacenaron en algún lugar de la memoria, otros tokenizaron sobre la marcha y fueron bastante lentos. Algunos convirtieron cada línea tal como se ingresó en la versión tokenizada y cuando escribiste LIST la destokenizó de nuevo a una salida legible (la mayoría hizo esto).

2) Cuando se utilizaron variables, insertaron un desplazamiento desde el comienzo de la memoria de variables o la dirección absoluta de la variable.

3) Cada palabra clave BASIC tenía (generalmente) una pequeña pieza de código de ensamblaje que realizaba las acciones necesarias para hacer esa palabra clave. Algunos fueron directos. Cuando dijiste PRINT “HELLO WORLD” (mi primera computadora no tenía minúsculas), saltó a una parte de la memoria que contenía el lenguaje ensamblador para el comando PRINT (más tarde) y lo ejecutó para generar el literal “HELLO WORLD” .

4) Cuando escribió RUN, un programa escrito en ensamblador (el intérprete) cargó el primer token de su programa, saltó a la ubicación de memoria adecuada para realizar esa acción, lo ejecutó hasta que regresó, avanzó al siguiente token, etc. .

Mucho trabajo realmente sucedió cuando ingresó una línea de código. Tenía una rutina que decía así:

  1. espera un personaje
  2. si ese carácter es la tecla “ENTER”, salte a la rutina del analizador
  3. compruebe para asegurarse de que el lugar actual en el búfer de comando no sea más de lo que una línea BÁSICA podría ser larga (tenía un máximo, generalmente, aproximadamente 255 caracteres)
  4. si no lo coloca en la ubicación actual en el búfer de comandos
  5. agregue 1 al lugar actual en el búfer de comando (moviéndose a la siguiente ubicación)
  6. saltar a 1

Cuando escribí esto y presioné ENTER al final:

10 IMPRIME “HOLA MUNDO”

el intérprete saltó al analizador y analizó la línea, haciendo algo como lo siguiente:

  1. leer un personaje
  2. verifique si ese carácter es un carácter especial como un operador o el “:” que dividió los comandos.
  3. Si es así, maneja ese personaje especial
  4. Si no lo considera parte del comando actual
  5. ir a 1

Entonces sería:
lee “10” y convierte que el número 10 ($ 0A en ese entonces $ significaba hexadecimal) y ponlo en una cola FIFO.

Analice “IMPRIMIR” y compare esto uno por uno con una lista de cadenas en la memoria que contenía las palabras clave. Si lo encontró, lo convirtió en un número, digamos 14 ($ 0e) y lo puso en la cola. El dispositivo predeterminado es 0 (la pantalla), por lo que lo coloca en la cola a continuación.

Luego vio la comilla doble (no tenía una comilla simple) y saltó a una rutina que leería y almacenaría una cadena literal. Esta rutina a menudo contaba los caracteres y luego saltaba a una rutina que encontraría espacio en la memoria reservada para contener variables y devolver esta dirección. Ahora copió los caracteres que en realidad eran sus códigos ASCII, uno por uno, desde el búfer de comando a la ubicación de almacenamiento variable hasta que llegó a la siguiente comilla doble. Produce un carácter especial al final de la cadena para denotar “STOP”. Luego empujó la dirección de este literal a la cola

Todo el tiempo hacía un seguimiento de cuán grande era la línea tokenizada.

Ahora, dado que se veía con un número de línea, significaba que era parte del programa actual en la memoria y no algo para ejecutar en este momento (llamado modo inmediato). Comenzó a leer el programa tokenizado en la memoria para ver si había una línea 10 y si se verificó la longitud. Si era más corto, copiaba los tokens en cola a la ubicación y movía el resto del programa hacia atrás en la memoria para cerrar el espacio. Si era más largo, entonces la línea actual 10 encontró espacio para ella en la memoria reservada para el programa y la puso allí cambiando una tabla en la memoria que almacenaba la ubicación de cada línea y luego simplemente reemplazó la nueva dirección con la dirección anterior en una tabla que muestra el orden de las líneas por número de línea y la dirección de cada línea en el espacio de código tokenizado.

Finalmente se hizo y devolvió el control a esa primera rutina. En general, el espacio del programa, espacio para los tokens, creció en una dirección (hacia adelante en la memoria) mientras que los datos del programa crecieron en el sentido opuesto (hacia atrás en la memoria). De esta manera, se puede usar la misma memoria para almacenar tanto el programa como los datos antes de que se quede sin memoria (lo que a menudo hacía). Un programa pequeño que usa muchos datos era tan malo como un programa grande que usa pocos datos.

Cuando escribí “EJECUTAR” hizo lo mismo que antes pero como no comenzó con un número de línea, saltó a la rutina de ejecución que ejecutó la cola que saltó al ensamblado que contiene el comando “EJECUTAR”.

Aquí es donde realmente ejecutó el programa. Fue a la tabla de números de línea y cargó la dirección de la primera línea, que contenía la forma simbólica de la primera línea. Este era un token de $ 0e del comando de impresión. En la memoria había una matriz de todas las direcciones de los comandos BASIC que respaldan las rutinas de ensamblaje almacenadas por número de token como índice. Saltó a la tabla [$ oe], que era el comando de impresión.

El ensamblaje para el comando de impresión, cargó el siguiente token que le indicó el número de dispositivo que debía generar. Las rutinas del vector de salida del dispositivo estaban en una matriz con el número de dispositivo como índice, lo cargó en un registro específico. El siguiente token le dijo que había literalmente el siguiente. El siguiente “token” no era en realidad un token sino la dirección de la cadena “HELLO WORLD” en la tabla literal. Comenzó un bucle en el ensamblaje que cargó la dirección del personaje en un registro y saltó a la dirección de la rutina de salida del dispositivo almacenada en ese registro específico que en este caso era el carácter fuera (“chrout”), una rutina de ensamblaje que puso el Código ASCII del personaje en el búfer de visualización de video en la ubicación actual, y avanzó la posición del personaje. Siguió haciendo esto hasta que llegó al carácter STOP en el que le dijo a la rutina chrout que comenzara una nueva línea.

Luego volvió a la primera rutina en la parte superior porque no había más tokens en el área de almacenamiento del programa.

El programa BASIC era simplemente información utilizada por otro programa para realizar las acciones que se escribieron en ensamblador. Ese programa base (el intérprete) tenía estructuras de datos adicionales que necesitaba, como tablas de vectores, tablas de cadenas, memoria variable y código para administrarlo, etc. Como puede ver, el análisis del lenguaje es leer el código y convertirlo en datos que el comando EJECUTAR puede usar para realizar el trabajo.

Un compilador analiza el código, lo tokeniza y hace algo similar al intérprete al convertirlo en ensamblador. Pero en lugar de ser un programa que utiliza estructuras de datos para “ejecutar” otro programa, convierte el programa a otra forma, generalmente ensamblado para el chip nativo y el código necesario para iniciar e inicializar un programa para el sistema operativo. Muchas veces se trata de un archivo de texto de ensamblaje (código fuente) que se ensambla usando un ensamblador y finalmente se convierte en código de máquina nativo para la CPU y el sistema operativo. Esto crea un archivo en algún dispositivo. Cuando le dice al sistema operativo que desea ejecutar ese archivo, lo abre, lo carga en la memoria y luego lo ejecuta. Ahora, al principio, puede parecer que el sistema operativo es un programa que también ejecuta un programa, pero en realidad no lo es. El código que escribe el compilador es nativo de la CPU y se ejecuta igual que el propio sistema operativo. Dado que el sistema operativo controla el hardware, puede detener su programa después de un momento, guardar todos los registros e iniciar otro programa cargando sus registros, pero su programa tiene el control directo de la CPU por un momento.

Eso es un compilador. O eso parece.

Ahora, durante mucho tiempo, hemos tenido lo que se llama un lenguaje de código de byte como Java, Perl, Inferno y muchos otros. Estos lenguajes son un híbrido entre un compilador y un intérprete. En este caso, su Java está tokenizado y compilado en códigos de bytes muy similares al programa BASIC anterior, pero en su lugar escribe tokens que son como ensamblados pero para otro programa (llamado máquina virtual) que lee y ejecuta esos tokens como si fueran instrucciones de lenguaje ensamblador a algún hardware virtual. Puede ver todos los de Java en el Capítulo 6. El conjunto de instrucciones de la máquina virtual Java. Entonces, este es un programa que, como un intérprete, ejecuta datos como código pero lo hace en un método mucho más eficiente que un intérprete y el lenguaje está diseñado para compilarse, mientras que muchos lenguajes interpretados no son muy fáciles de compilar. Compilar estos códigos de bytes en el ensamblado nativo suele ser mucho más fácil. Lo bueno de esto es que el mismo archivo que se compila en un sistema que se ejecuta sobre Linux en un ARM y se ejecuta en un sistema que ejecuta Windows en un procesador x86 ya que la máquina virtual hace toda la funcionalidad del hardware virtual en el hardware real .

Entonces, este sigue siendo un programa que ejecuta un programa que es un intérprete. O eso parece.

Se compilan muchos lenguajes de códigos de bytes en tiempo de ejecución hasta el ensamblado nativo antes de que se ejecuten. Esto se conoce como compilación justo a tiempo o JIT. En este caso, ese archivo de códigos de bytes para una máquina virtual determinada se lee y se convierte en ensamblado nativo para el chip y el sistema operativo actuales y se ejecuta como código nativo para la CPU. Este es un compilador.

Si llegaste hasta aquí en esta larga respuesta, debes estar interesado. Aquí hay algunos enlaces:

Comparación del software de virtualización de aplicaciones.
Compilación justo a tiempo
BASIC

¿Qué probar un poco BÁSICO? Muchas, muchas, muchas personas aprendieron a programar de esta manera
QuiteBasic: diversión, aprendizaje y nostalgia

Un compilador transforma un programa de un idioma (fuente) a otro idioma (destino). En general, el lenguaje de origen es un lenguaje de programación de alto nivel como C / C ++ y el lenguaje de destino es el lenguaje de máquina de la arquitectura de destino (x86, arm). Una vez que se genera el código de destino, el trabajo del compilador termina. El objetivo del compilador es
1) Verifique que el programa fuente cumpla con la sintaxis y la semántica del lenguaje de programación fuente.
2) Generar código que sea correcto. El código generado debe hacer exactamente lo que significa el programa en el idioma fuente.
3) Realice optimizaciones para que el código generado se ejecute rápidamente en el objetivo.
4) Haz esto lo más rápido posible

Un intérprete es un programa que toma un programa escrito en un idioma fuente y ejecuta el programa. Puede pensar que el código fuente es un conjunto de instrucciones para el intérprete. El intérprete generalmente realizará una verificación de sintaxis primero en el idioma fuente para asegurarse de que sea un programa válido e intente interpretar el código fuente línea por línea. Los intérpretes se usan generalmente en casos donde la flexibilidad y la capacidad de programación es el objetivo principal en lugar de un alto rendimiento. Estos idiomas a menudo tienen una tipificación dinámica. Podemos encontrar intérpretes para lenguajes de script como perl, python, etc.

Esta era una pregunta antigua, que ahora es muy difícil de responder, porque no hay más distinción, porque las máquinas potentes, el compilador y los intérpretes hacen mucho más que las definiciones clásicas.

Una característica antigua era: el intérprete leía la entrada una línea a la vez, y la ejecuta (el intérprete ejecuta el código). Los compiladores leerán todos los archivos y crearán un archivo binario. Las computadoras ejecutarán de forma nativa el código.

Pero tenemos un intérprete de Python que lee todo el archivo (pero solo el archivo, no los módulos incluidos) y bloquea antes de ejecutar cualquier cosa, si se descubre un error de sintaxis (por ejemplo, al final de un archivo). Pero también compilador avanzado. Los compiladores antiguos eran “dos pases”, uno para leer el código y preparar la memoria para las variables, y el segundo para crear el código binario. Luego tuvimos unos pocos compiladores rápidos que solo hicieron una pasada. Ahora los compiladores hacen muchos pases y optimizan. (Las computadoras son mucho más rápidas, pero los idiomas son similares al anterior.

Pero luego hay complicaciones. Los módulos de Python son archivos compilados (en .pyc), solo se interpreta el script principal (pero vea el punto anterior: todos los archivos deberían ser válidos). Pero los archivos .pyc (que son independientes de la arquitectura) no son directamente ejecutables por la computadora: necesita un intérprete de Python. No es diferente del compilador de Java, que crea código para una máquina virtual de Java. Ninguna CPU puede ejecutar directamente dicho código binario de Java.

Computadora rápida y algunos lenguajes interpretados obtuvieron un compilador justo a tiempo. El Javascript que ejecutan sus navegadores recientes probablemente se compila en código de máquina y se ejecuta de manera directa en el navegador. Pero, de nuevo, este no es un código ejecutable directamente por la computadora: todavía necesita soporte del motor / navegador javascript. También Java tiene tal optimización, pero todavía llamamos al compilador de Java.

Y algunas personas crearon una C interpretada, por lo que tampoco es una característica del lenguaje.

Otra característica es: los lenguajes interpretados tienen una función como “eval”, compilada no. Pero no usaré tan poca característica para distinguir los idiomas. “Eval” no se usa tanto, y prohibir el uso de eval no cambia las características de la language (y muchos lenguajes compilados tienen bibliotecas para ejecutar código dinámico (para complementos).

Si puede obtener un ejecutable, con código de máquina, lo definiría compilado. Entonces puedo compilar Python, con herramientas especiales que me crean un ejecutable (py2exe, pyinstaller, etc.) e interpretar, si puedo escribir una línea y obtener el resultado, escribir otra línea (que puede usar el resultado de la primera línea) y tener resultados. Todos los demás son una especie de gris, algunos más oscuros, otros casi blancos.

[Nota: un archivo ejecutable directamente por el sistema operativo no es suficiente, pero OTOH no entiendo por qué debería importar, para mí como programador o como usuario. Unix, Linux y macosx permiten la ejecución directa de scripts de shell.]

Entonces: antigua dicotomía con poco uso ahora

El compilador es un software que compila archivos de texto que contienen código fuente para producir ejecutables, es decir

Existen lenguajes compilados como C, C ++ que están escritos por humanos en archivos de texto para hacer que la computadora realice algunas operaciones. Ahora el programador invoca al compilador que analiza el código fuente para la formación de sintaxis adecuada y otras restricciones de lenguaje, si algo no es correcto, el compilador detiene la compilación e informa al programador sobre los errores o las razones por las cuales el código fuente particular está mal en algunos lugares, esta característica ayuda a detectar errores al principio del ciclo de desarrollo, ahora por otro lado, si todo parece correcto desde el compilador, el compilador producirá archivos de objetos que son código dependiente de la arquitectura para una máquina en particular. Este ejecutable se ejecuta como una tarea en uno o más procesos o subprocesos. Tenga en cuenta que la comprobación de errores en tiempo de compilación no protege contra errores de tiempo de ejecución como el código que accede a un área de memoria a la que no está permitido acceder y este tipo de errores que el núcleo del sistema operativo es responsable de manejar para que otras áreas del sistema operativo no sean afectado.


Los intérpretes, por otro lado, están allí debido a los lenguajes que son esencialmente lenguajes de script como shells, JavaScript, python, etc. En cuyo caso no hay un paso de compilación como tal, sino que de inmediato leen el código fuente y ejecutan esas declaraciones, esto es beneficioso en En el sentido de que no es necesario tener un ciclo de compilación prolongado, el programador puede cambiar directamente el código fuente y ver el resultado inmediatamente. Esto también es maravilloso en el sentido de que el intérprete no necesita generar instrucciones específicas de la máquina, puede generar instrucciones independientes de la máquina que luego se convierten en código de máquina nativo por otro programa como una máquina virtual. El inconveniente es la velocidad y la seguridad, ya que los lenguajes interpretados pierden muchas optimizaciones de código que los compiladores hacen durante la fase de compilación y los errores pueden aparecer solo durante el tiempo de ejecución.


Luego hay una combinación de estos dos procesos como en el caso de lenguajes como Java / C # donde el código se compila primero para producir ejecutables, pero estas son instrucciones intermedias independientes de la máquina (bytecode / IL) que cuando se invoca se ejecuta en un tiempo de ejecución que aloja una máquina virtual y allí las interpretaciones producen código dependiente de la arquitectura nativa

En primer lugar, háganos saber la diferencia entre el lenguaje compilado e interpretado.

Lenguaje interpretado
Ejemplo: JavaScript
Considere que estamos ejecutando un archivo con el nombre “add.js”. Para ejecutar este archivo. Este archivo se entrega a un software llamado Intérprete.
El intérprete hace lo siguiente.

  • Lea la primera línea de add.js.
  • Convierta la primera línea de add.js a binario. Ahora ejecuta este binario.
  • Lea la segunda línea de add.js.
  • Convierta la segunda línea de add.js a binario. Ahora ejecuta este binario.
  • Igualmente, haga lo mismo hasta la última línea del programa.

Entonces lea una línea del programa. Convierte la línea a binario.
Luego, sin almacenar el binario en algún lugar, ejecute el binario inmediatamente.
El lenguaje de programación que requiere un intérprete para ejecutarse. Ese lenguaje de programación se llama lenguaje interpretado.

Lenguaje compilado
Ejemplo: C, C ++.
Considere que estamos ejecutando un archivo con el nombre “add.c”. Para ejecutar este archivo. Este archivo se entrega a un software llamado Compilador.
El compilador hace lo siguiente.

  • Cree un archivo con el nombre “add.o”.
  • Lea la primera línea de “add.c” y convierta esta línea a binario. Almacene este binario en “add.o”.
  • Lea la segunda línea de “add.c” y convierta esta línea a binario. Almacene este binario en “add.o”.
  • Igualmente, haga lo mismo hasta la última línea del programa.
  • Este “add.o” se llama el archivo objeto. Contiene el código binario de cada línea en “add.c”.
  • Ahora se ejecuta la primera línea de “add.o”.
  • Luego se ejecuta la segunda línea de “add.o”.
  • Igualmente, se ejecutará la última línea de “add.o”.

Entonces lea una línea del programa. Convierte la línea a binario. Almacene el binario en algún lugar. Continúe este proceso hasta la última línea del programa. Luego ejecute cada línea binaria línea por línea.
El lenguaje de programación que requiere un compilador para ejecutarse. Ese lenguaje de programación se llama lenguaje compilado.

Java es lenguaje compilado e interpretado
Considere que estamos ejecutando un archivo con el nombre “add.java”. Para ejecutar este archivo. Este archivo se entrega a un software llamado javac. javac es un compilador.
javac hace lo siguiente.

  • Cree un archivo llamado “add.class”.
  • Lea la primera línea de “add.java”. Convierta esta línea en código de bytes. Almacene este código de byte en “add.class”.
  • Lea la segunda línea de “add.java”. Convierta esta línea en código de bytes. Almacene este código de byte en “add.class”.
  • Igualmente, haga lo mismo hasta la última línea del programa.

Entonces lea una línea del programa. Convierta la línea a código de byte. Almacene el código de bytes en alguna parte. Continúe este proceso hasta la última línea del programa. Esto no es más que compilación.

  • El archivo “add.class” contiene el código de bytes de cada línea en “add.java”.
  • El archivo “add.class” se entrega a un software llamado JVM. JVM es un intérprete.
  • JVM lee la primera línea de “add.class”. Convierta esta línea a binario. Luego ejecuta esta línea.
  • JVM lee la segunda línea de “add.class”. Convierta esta línea a binario. Luego ejecuta esta línea.
  • Igualmente, haga lo mismo hasta la última línea del programa.

El código de byte no es el código binario. No es un código java. Es un código intermedio. Java requiere un compilador (javac) para convertir el código de Java en código de bytes.
Java requiere un intérprete (JVM) para ejecutar el código de bytes. Dado que Java requiere tanto el compilador (javac) como el intérprete (JVM) para su ejecución, Java se llama lenguaje compilado e interpretado

Esta respuesta está tomada de 1. ¿Es Java un lenguaje compilado o interpretado?

Respuesta muy simplista:

Todos los compiladores son “traductores”, ya que la palabra compilar es sinónimo de traducir cuando se refiere a programas compilados. Entonces, de aquí en adelante, usaré la palabra traducir, para hacer cumplir la idea.

El “cerebro” de la computadora (la CPU) tiene un “lenguaje” particular propio diseñado por el fabricante. Este lenguaje se conoce como el conjunto de instrucciones de la CPU. Es un lenguaje de muy bajo nivel con solo las cosas básicas necesarias: muy pocas abstracciones de alto nivel (si las hay). Y para que cualquier programa se ejecute realmente, este es el lenguaje final al que debe traducirse: la CPU “no entiende” nada más.

Ahora obtienes traductores que cambian el código fuente del programa del original (por ejemplo, C / Python / Lisp / lo que sea) en este conjunto de instrucciones de la CPU (a veces nos referimos a él como código binario, porque eso es todo lo que son números guardados en formato binario – digamos algo así como 1 = 0001 = sumar, 2 = 0010 = menos, 3 = 0011 = multiplicar, etc.). De este modo, estos traductores generan un archivo que puede enviarse directamente a la CPU sin más modificaciones. Estos son lo que generalmente se conoce como compiladores anticipados (AOT). Así que puedes verlos como análogos a un traductor humano que traduce un libro a otro idioma.

Otra forma es hacer que el traductor se ejecute cuando desee ejecutar su programa. Esto sería un intérprete; tenga en cuenta que también actúa de manera similar a un intérprete humano: traduce un idioma a otro cuando se “habla”. En este sentido, traduce el idioma de origen al idioma de la CPU cuando se ejecuta.

A veces es muy difícil decir una cosa en un idioma y traducirlo a otro. En la programación, una forma de hacerlo menos difícil es usar un lenguaje intermedio. Algunos ejemplos de esto son Java-> JVM ByteCode-> CPU Instruction Set, otro es C # -> DotNet IL-> CPU Instruction Set. Piense en ello como si no pudiera encontrar un intérprete para traducir directamente de su propio idioma a la persona con la que desea hablar. Entonces, contratas a un traductor para que traduzca tus palabras en un pedazo de papel y luego lo entregas a otro intérprete que luego traduce eso mientras lo lee al tercer idioma.

Luego llegamos a JIT (o Just In Time) que es exactamente el mismo que en los 2 escenarios anteriores (ya sea interpretar completamente o interpretar un idioma intermedio), aunque la mayoría de las veces el segundo es el más frecuente (ya que esos idiomas intermedios son más simples y, por lo tanto, más fáciles de traducir al lenguaje de CPU aún más simple). La diferencia es que los “traductores” JIT tienden a escribir lo que se dice en el nuevo idioma. Luego, cuando quieren referirse a algo que ya han traducido, es mucho más rápido.

Ya he respondido parte de esta pregunta aquí.

Un ensamblador realiza la misma función, básicamente, como un compilador, toma su código y lo convierte en un objeto binario ejecutable, pero el ensamblador es un traductor literal, en comparación con un intérprete o un compilador que no lo es.

La principal diferencia es que el programador tiene (casi) control total sobre la salida del objeto del ensamblador. (Digo “casi” porque algunos ensambladores sustituyen una secuencia mnemónica o mnemónica por una más eficiente para ciertas arquitecturas).

Asi que; Las diferencias básicas entre un compilador y un ensamblador …

… uhm …

Bueno … Por un lado, un compilador administrará el alcance de las variables. Al hacerlo, también configurará marcos de pila, tanto por razones de alcance como porque el acceso a los datos en la pila es generalmente bastante más rápido que el acceso a un segmento de datos. El compilador también gestionará la “escritura”. Crea una variable y le dice a su compilador si esa variable es una cadena o una matriz o un hash y el compilador la maneja desde allí.

En contraste, el lenguaje ensamblador no tiene variables. Los datos se almacenan en direcciones de memoria y depende de usted hacer un seguimiento de ellos y de su tamaño. Puede nombrar esas ubicaciones de memoria, pero toda la memoria es accesible para un programa en lenguaje ensamblador, por lo que todo lo que está fuera de un marco de pila es “global”, per se, y no se puede escribir. Por ejemplo:

mov rax, [rdx]; acceso de 64 bits a una ubicación de memoria
mov eax, [rdx]; acceso de 32 bits a la misma ubicación de memoria
mov axe, [rdx]; acceso de 16 bits a la misma ubicación de memoria
mov al, [rdx]; acceso de 8 bits a la misma ubicación de memoria

son todos válidos y nunca arrojarían un error. El procesador no sabe qué datos hay en [.RDX], ni le importa; todo lo que sabe es que desea obtener un byte de 8 bits o una palabra de 64 bits o algo intermedio de una ubicación de memoria particular, ¡y eso es exactamente lo que hace!

No te hagas la idea de que no hay protección de memoria. Intentar acceder a una página de memoria que no existe genera un error de página y su sistema operativo lo va a volcar sin ceremonias, posiblemente sin indicación de un error fuera de los registros. Los programas compilados generalmente atrapan tales condiciones e intentan informarlas en la línea de comando.

Otra cosa que hacen los compiladores es configurar bucles de bloque. En C simplemente:

para (i = 27; i == 37; i ++) {

y ya está: todo lo que necesita hacer es cerrar el soporte. En lenguaje ensamblador, ese mismo bucle puede verse vagamente como:

mov sp, [STACKPOINTERBASE]
mov bp, [STACKFRAMEPOINTEROFFSET]
hacha mov, 0x25
mov bx, 0x1B
sub hacha, bx
mov [bp + __ CounterOffsetMangledName], ax
;
__LoopMangledName:
; coloque una tonelada de declaraciones de ensamblador aquí
; para hacer un trabajo relativamente simple
mov sp, [STACKPOINTERBASE]
mov bp, [STACKFRAMEPOINTEROFFSET]
dec WORD [bp + __ CounterOffsetMangledName]
jnz __LoopMangledName
;

Obviamente semi-optimizado y no una traducción literal de la funcionalidad del bucle C. Lo hice a propósito. Es posible que o no necesite cargar el puntero de la pila o el puntero de bytes, dependiendo de lo que esté haciendo. Pero la declaración for (i=27; i==37; ++i) produciría un código de ensamblaje que tendría una estructura … muy similar pero significativamente diferente debido a la diferencia en cuando el contador del bucle se está “incrementando”.

La mayoría de las optimizaciones que se valoran mucho en los compiladores generalmente se evitan en los ensambladores. Por lo general, cualquier característica de optimización de ensamblador debe estar habilitada o, si está habilitada de manera predeterminada, puede deshabilitarse mediante una opción de interruptor. Esto es solo en virtud de la naturaleza de los propios programadores de ensamblaje, las razones por las cuales el lenguaje ensamblador se está utilizando en primer lugar, y simplemente por convención.

Los ensambladores son traductores literales de código. No puede escribir un programa en lenguaje ensamblador Intel y alimentarlo en un ensamblador Motorola o Zilog. Usted escribe NOP y el compilador genera 0x90 cuando usa un ensamblador para un procesador Intel, pero mientras que los procesadores Motorola tienen una instrucción mnemónica de NOP , el código de operación es 0x01, y un ensamblador Z-80 generaría 0x00. Misma instrucción mnemónica, y hace lo mismo, pero es un código de operación completamente diferente y no relacionado en diferentes procesadores.

Por el contrario, un compilador para OS / X, por ejemplo, tomará un (C) modo de texto o un programa Fortran o Pascal escrito en Linux y lo compilará probablemente con muy poca modificación. Lo mismo para Windows. Sin embargo, la salida de código real a los archivos de objeto sería completamente diferente.

Un programa de lenguaje ensamblador, por el contrario, debería reescribirse por completo, básicamente desde cero si desea apuntar a un procesador diferente, y tal vez no se reescriba completamente desde cero, pero todo para pasar de 32 bits a 64 bits. . Y si decidiera cambiar el tamaño de un dato, tendría que elegir todo su código, cambiando todos los .AX por .EAX o .RAX (por ejemplo) en lugar de simplemente cambiar un int x en un archivo de encabezado a long x o long long x (C).

Así que supongo que acabo de convencerme de una definición: un ensamblador es un traductor literal de código que usa mnemónicos directos para el conjunto de instrucciones de código de operación del procesador. Un compilador es un intérprete de funciones independientes del procesador. (Supongo.)

Espero que ayude.

¿Cuál es la diferencia entre un intérprete compilador y un ensamblador?

bien,
La diferencia entre compilador e intérprete:
Compilador:
1. El compilador toma todo el programa como intérprete de entrada
2) Se genera el código de objeto intermedio
3. Las declaraciones de control condicional se ejecutan más rápido
4. Requisito de memoria: más (ya que se genera el código de objeto)
5. El programa no necesita ser compilado cada vez
6. Los errores se muestran después de que se verifica todo el programa
7. Ejemplo : compilador de C
Interprete:
1. El intérprete toma la instrucción individual como entrada
2. No se genera ningún código de objeto intermedio
3) Las declaraciones de control condicional son ejecuciones más lentas
4) El requisito de memoria es menor
5) Cada vez que un programa de nivel superior se convierte en un programa de nivel inferior
6. Se muestran errores por cada instrucción interpretada (si hay alguna)
7. Ejemplo : BÁSICO
Simplemente entienda el concepto del compilador e intérprete:
1.Damos un programa completo como entrada para el compilador.
2. Sin embargo, el intérprete toma una sola línea de código como entrada a la vez y ejecuta esa línea. Terminará la ejecución del código tan pronto como encuentre el error.
3. El requisito de memoria es menor en el caso del intérprete porque no se crea ningún código objeto en el caso del intérprete.

En el campo de las computadoras, las instrucciones dadas por el usuario son normalmente de lenguaje de alto nivel, mientras que la computadora entenderá las instrucciones solo en formato binario, el idioma de una computadora se conoce como lenguaje de máquina. El único propósito del compilador e intérprete es convertir el lenguaje de alto nivel dado por el usuario en lenguaje de nivel de máquina para que la computadora entienda y ejecute el conjunto de instrucciones dirigido por los usuarios. “Si tanto el intérprete como el compilador se usan con el único propósito, entonces cuál es el significado de cada uno, por esta razón, el informe actual está dirigido a explorar la diferencia entre un compilador e intérprete”. Un compilador traducirá la entrada de lenguaje de alto nivel dada por el usuario al lenguaje de máquina, es decir, en los códigos binarios, mientras que un intérprete también convierte el lenguaje de alto nivel en lenguaje de nivel de máquina, pero el intérprete generará inicialmente un código intermedio y luego convertirá el lenguaje de alto nivel al lenguaje de nivel de máquina.
Diferencia entre compilador e intérprete:
Aunque el compilador y el intérprete se usan para convertir el lenguaje de alto nivel a lenguaje de máquina, existen pocas variaciones entre el compilador en el estilo y las funcionalidades en la conversión de los idiomas.

El compilador es un programa único que ejecuta las instrucciones escritas en un lenguaje de programación determinado y las convierte en el código de máquina que una computadora puede entender. El intérprete solo hace el mismo trabajo que el compilador, pero la principal variación es que convierte el lenguaje de alto nivel en un código intermedio que ejecuta el procesador. Normalmente, un desarrollador compone las instrucciones establecidas utilizando cualquier tipo de lenguaje de programación como C, Java, Pascal, Python, etc. La instrucción escrita por el programador se denomina código fuente. El programador debe iniciar el compilador o intérprete relacionado con el lenguaje utilizado para escribir el código fuente. El intérprete investiga y ejecuta cada línea de código fuente en secuencia, sin considerar todo el programa a la vez. Sin embargo, los programas configurados por compiladores se ejecutan mucho más rápido que las mismas instrucciones ejecutadas por un intérprete.

Diferencias básicas entre compilador e intérprete :

  • El compilador traduce la instrucción de alto nivel al lenguaje de máquina, pero el intérprete traduce la instrucción de alto nivel a un código intermedio.
  • El compilador ejecuta todo el programa a la vez, pero el intérprete ejecuta cada línea individualmente.
  • El compilador informa la lista de errores causados ​​durante el proceso de ejecución, pero el intérprete deja de traducir poco después de encontrar un error, la progresión de las otras líneas del programa se realizará después de refinar el error.
  • El compilador genera un archivo ejecutable autónomo, mientras que el intérprete es obligatorio para un programa de intérprete.


Diferencias en función de diversas características:

  • En un compilador, el tiempo de análisis y procesamiento del programa es mayor, mientras que un intérprete dedica menos tiempo al análisis y procesamiento del programa.
  • El código resultante del compilador está en forma de código de máquina o formato binario, en el caso del intérprete, el código resultante tiene la forma del código intermedio.
  • En el caso del compilador, el código resultante es ejecutado por el hardware de la computadora, en un intérprete; otro programa interpreta el código resultante.
  • La ejecución del programa es rápida en el compilador; En un intérprete, la velocidad de ejecución del programa es relativamente lenta.


Diferencias en función de la programación:

  • El compilador verificará la sintaxis del programa, mientras que el intérprete verifica las palabras clave de un programa.
  • El compilador verificará todo el programa a la vez, pero el intérprete verifica el programa simultáneamente en el editor.
  • La ejecución del programa en el intérprete se realiza línea por línea, pero el compilador ejecuta el programa en general.

Un compilador toma todo el código fuente y crea algo más, como código de máquina, código de bytes de Java o IL. Una vez que se realiza la compilación, está listo para ejecutar su código (que generalmente es un segundo paso, aunque puede estar en un IDE que desdibuja la línea al tener un botón que compila el código y luego lo ejecuta si no hay errores )

Un intérprete toma una línea a la vez y la ejecuta.

Una analogía: digamos que tienes un niño alemán que quisiera un cuento antes de dormir. Tienes un manuscrito que has estado escribiendo, pero no sabes alemán, así que se lo vas a entregar a otra persona.

Su primera opción es como una compiladora: es una traductora que toma un manuscrito de un libro y va a hacer una traducción a un nuevo idioma. Este traductor en particular lee primero todo el libro y puede decir “En la página 57, eliminó accidentalmente un verbo de la oración”. Arreglas el manuscrito y lo devuelves. Ella lee todo el manuscrito nuevamente, lo encuentra libre de errores y le devuelve otra copia del manuscrito en alemán, que luego puede leerle al niño.

El intérprete análogo leería una línea del libro en silencio, luego diría su traducción en voz alta en alemán. En este ejemplo anterior, cuando llega a la página 57, se detendrá y dirá “Esto no tiene sentido. Eliminó accidentalmente un verbo de la oración”.

Un compilador a menudo toma un tiempo notable para traducir todo el programa. Una vez que se realiza la compilación, sabe que una cierta clase de errores no está allí (o el compilador los habría encontrado). Un compilador puede reordenar declaraciones, eliminar métodos que no se están utilizando, etc. A veces puede ser sorprendentemente agresivo para hacer que el código sea más eficiente (como, por ejemplo, aquí: la respuesta de Tim Hammerquist a ¿Usaría shorthands en C ++ disminuye la ejecución del programa? ¿hora? ).

Un intérprete no reordena, elimina métodos, etc. No tiene forma de saber si eso es algo que necesitará más adelante.

Un compilador es algo que traduce un idioma a otro (que potencialmente puede ser el mismo idioma). Piénselo como lo que Google Translator es para lenguajes naturales.

Un intérprete es algo que ejecuta un programa en algún idioma.

Todos los programas se interpretan en última instancia, resulta que un intérprete se implementa directamente en el hardware, y algunos intérpretes se implementan como software (y estos mismos intérpretes son interpretados por algún hardware).

Algunos hardware, como la familia de procesadores Intel x86, también compilan sus programas de un conjunto de instrucciones en otro conjunto de instrucciones antes de interpretar.

Otras respuestas han explicado maravillosamente ambos términos con respecto a los lenguajes de programación, no repetiré esas cosas. Trataré de explicar lo mismo en términos simples: –

Digamos que entiendes inglés pero no español.
Escena 1:-
Si desea leer un libro escrito originalmente en español, comprará una copia traducida al inglés … compilador / traductor (el libro completo está traducido)

Escena 2: –
Estás escuchando la interpretación en inglés de un discurso en vivo, entregado en español … intérprete (interpretación oración por oración)

La diferencia entre compilador e intérprete es la siguiente:

1) El compilador toma el programa completo como entrada, mientras que el intérprete toma la instrucción individual como entrada.

2) El código de objeto intermedio se genera en el caso del compilador, mientras que en el caso del intérprete no se genera ningún código de objeto intermedio .

3) El requisito de memoria es mayor (ya que se genera el código de objeto) en el caso del compilador, mientras que el requisito de memoria es menor en el caso del intérprete.

4) Los errores se muestran después de que se verifica todo el programa en el caso del compilador. Por lo tanto, la depuración es relativamente difícil. En el caso de un intérprete, se muestran errores para cada instrucción interpretada (si la hay). Un intérprete continúa traduciendo el programa hasta que se cumpla el primer error, en cuyo caso se detiene. Por lo tanto, la depuración es fácil.

Espero que esto ayude..

De todas las respuestas, pensé que la de Robin Thomas era la más cercana a cómo había visto el problema.

Escribí un compilador y trabajé en un intérprete, y creo que la mayoría de las respuestas fueron adecuadas sobre lo que hace un compilador. No estaba satisfecho con la mayoría de las respuestas sobre lo que hace un intérprete.

Un compilador solo traduce de un lenguaje formal a otro. Eso lo cubre muy bien. El idioma al que se traduce no se ejecutará necesariamente después de la compilación. Se puede ejecutar a través de otro compilador.

El primer compilador de C ++ funcionó de esta manera. Originalmente se llamaba “cfront”. Tradujo el código fuente de C ++ al código fuente de C, que luego se ejecutó a través de un compilador de C. Los primeros compiladores de C no compilaron directamente al código objeto. Se compilaron para ensamblar, que luego se ejecutó a través de un ensamblador para obtener el código de objeto, que luego se ejecutó a través de un vinculador para finalmente obtener el código de máquina terminado.

Cuando tomé un curso de compiladores en la universidad, escribimos un compilador que traducía un lenguaje similar a Pascal a un código de 3 direcciones en C (que estaba destinado a emular el lenguaje ensamblador). Esto, por supuesto, se ejecutó a través de un compilador de C para finalmente obtener un programa ejecutable.

Los compiladores operan en fragmentos de código a la vez, ya sea una sola declaración, expresión, función o un programa completo. Algunos de los entornos de desarrollo más modernos compilan incrementalmente el código para acelerar el proceso. Algunos pueden estar familiarizados con el término “editar y continuar”. Esto es para que no haya tanto tiempo entre editar el código fuente, ejecutar y depurar un programa.

La forma en que describiría a un intérprete es que “ejecuta una estructura”. Muchas de las respuestas hablan de un intérprete que ejecuta un programa “línea por línea”. Eso es lo que hace un intérprete interactivo . He visto y trabajado en intérpretes que leen todo un programa de una vez y luego los ejecutan. La forma en que los he visto funcionar es que crean una estructura de datos interna, que luego atraviesa, recogiendo información en el camino y actuando sobre ella de inmediato. Es, en cierto sentido, un proceso repetible, pero es complicado. No hay forma regular de ejecutar las instrucciones, o la forma en que se almacena la información (a excepción de esta gran estructura de datos conectados en la que se almacena el programa). Todas las operaciones internas del intérprete dependen de las circunstancias del momento (el estado en el que se encuentra el intérprete y el estado que se representa en la estructura de datos interna).

El código no se traduce al idioma de la máquina. Por el contrario, se ejecuta un conjunto de procedimientos repetitivos parametrizados, aunque cuáles y en qué secuencia dependen de la estructura de datos interna que el intérprete ha construido para sí mismo.

Recuerdo que una vez intenté explicar la diferencia entre una máquina virtual y un intérprete, y creo que para muchas personas la diferencia es sutil, porque en realidad se trata de cómo se organiza la información en la memoria y cómo se ejecuta un programa.

Una máquina virtual, en el sentido en que la estamos usando aquí, es realmente lo que yo llamaría un “procesador abstracto”, y es como la forma en que funciona una máquina Turing. (También he escrito uno de estos).

Una máquina de Turing tiene un punto de partida y tiene un conjunto simple de operaciones que repite a lo largo de toda la ejecución de un programa. Estas operaciones son: leer un símbolo o código donde sea que el “encabezado” esté en la “cinta” (podemos analogizar esto con RAM, por razones de argumento), buscar el código internamente y ejecutar una o más instrucciones internas (que puede parametrizarse) que le corresponden, lo que afectará el estado de la “cabeza” y posiblemente la “cinta”, luego repita este proceso hasta que llegue a un estado “final”.

Por la poca experiencia que he tenido con esto, el proceso de una VM para obtener información, ejecutar instrucciones, proporcionar almacenamiento temporal de información, guardar estado, ramificar y almacenar información a largo plazo, se formaliza y es regular, y equivale a una tarea más simple. proceso de lo que trata un intérprete. Una máquina virtual no forma una estructura de datos conectada a partir del código fuente. Ejecuta un lenguaje compilado (compilado en bytecode) que, si lo mira, está organizado más como un código de máquina. Tiene su propio esquema de direccionamiento, que la VM sigue para buscar y almacenar información, y para controlar el flujo del programa. Este es el tipo de operaciones que un intérprete llevaría a cabo “razonando” sobre las relaciones en la estructura de datos de la que hablé.

Para la mayoría de las personas, esto no hará ninguna diferencia. Desde la perspectiva de la informática, es una gran diferencia.

Hola…..

La diferencia entre un compilador y un intérprete aquí es alguna diferencia …

Interprete

  • Traduce el programa una declaración a la vez.
  • Analizar el código fuente requiere menos tiempo, pero el tiempo de ejecución general es más lento.
  • No se genera código de objeto intermedio, por lo tanto, son eficientes en memoria.
  • Continúa traduciendo el programa hasta que se cumpla el primer error, en cuyo caso se detiene. Por lo tanto, la depuración es fácil.
  • Lenguaje de programación como Python, Ruby usa intérpretes.
  • Compilador
  • Escanea todo el programa y lo traduce como un todo en código máquina.
  • Analizar el código fuente requiere una gran cantidad de tiempo, pero el tiempo de ejecución general es comparativamente más rápido.
  • Genera código de objeto intermedio que además requiere vinculación, por lo tanto requiere más memoria.
  • Genera el mensaje de error solo después de escanear todo el programa. Por lo tanto, la depuración es relativamente difícil.
  • Lenguajes de programación como C, C ++ usan compiladores.

y así. y algunas compañías están ofreciendo trabajos de intérprete para echar un vistazo. Empleos de Intérprete de idiomas, Empleos de Intérprete independiente en India Delhi – TridIndia

Gracias.

Se sentirá aliviado al escuchar que no hay una paradoja que resolver, pero hay un poco de historia, así que me temo que necesitaremos un poco de vocabulario. Comencemos contando algunas mentiras del sistema operativo:

  • cuando se ejecuta un programa, tiene toda la computadora para sí mismo
  • la memoria asignada a un programa es una matriz larga y lineal de direcciones

Esto no está cerca de la verdad, pero cuando se inicia un programa binario nativo (convirtiéndose así en un proceso en la memoria), el sistema operativo lo carga en un entorno limitado de memoria donde puede sentarse y creer esto sin ningún efecto secundario desagradable. Entonces, ¿qué es un programa, de todos modos? Es un montón de instrucciones (inteligentemente llamado segmento de texto ), un montón de espacio para datos globales (llamado imaginariamente segmento de datos ), un montón de espacio de trabajo vacío para cálculos intermedios (llamado pila ) y un montón de espacio vacío para colocar cosas que no sabemos el tamaño de antes de que sea necesario (llamado el montón ). Hay más detalles, pero esas son las partes principales de un proceso. El programa se ejecuta por la CPU manteniendo un contador que apunta en algún lugar del texto, la instrucción allí le dice qué hacer con las áreas de datos o el contador de instrucciones. Luego ajusta el contador de instrucciones (generalmente solo lo incrementa), y se repite con la siguiente instrucción a la que apunta.

Un archivo ejecutable binario es algo así como una copia liofilizada de una imagen de proceso no utilizada, programa incluido. Aquí hay un diagrama aproximado para poner el archivo ejecutable, la imagen del proceso y la CPU en contexto entre sí:

Recuerde, la CPU es tonta como una roca. Esto se debe a que es una roca, simplemente es una roca con propiedades eléctricas interesantes que parecen cálculos, si usas un poco de imaginación. Las instrucciones en el segmento de texto se envían directamente a la unidad de instrucciones estúpidas del procesador, por lo que no pueden contener ninguna filosofía. Simplificados, son índices en una tabla que enumera todos los lugares donde se pueden guardar los números (como A, B en la figura) y todas las formas de combinarlos en un solo paso: cosas como cargar, almacenar, sumar, restar, saltar y Otras operaciones sencillas para una máquina automática. Esta secuencia de códigos numéricos está incrustada en el archivo ejecutable (abra uno con un desensamblador, puede leerlos usted mismo), y es el trabajo de un compilador convertir un programa de nivel superior en esta cadena de números .

Cuando termine, tendrá una imagen de proceso liofilizada que su O / S puede colocar directamente en una caja de arena y señalar el procesador. Lo que encuentra en el segmento de texto ya se ha puesto en el lenguaje nativo de la CPU de una manera, por lo que este programa puede ejecutarse tantas veces como desee sin que se realice ninguna traducción.

Ahora, los procesadores no tienen el monopolio de aceptar instrucciones, los programas también pueden leer las instrucciones, eso es lo que la mayoría de ellos hacen en algún formato. Si escribe un programa que lee un montón de instrucciones generales del programa y hace que sucedan una por una mientras se leen, ha creado un programa que actúa como un procesador diferente (y posiblemente más avanzado) que el que está ejecutando. en. Los programas para este procesador de simulación pueden tener un aspecto muy diferente de las instrucciones nativas, siempre que su programa de simulación pase y los lea cada vez. A modo de ilustración, la situación se ve así:

Este procesador hecho por software es un intérprete. Dependiendo de cuán abstracto y qué tipo de instrucciones lea, las personas también pueden llamarlo cosas como “máquina virtual” o “emulador” o similar, pero el principio es el mismo. Para confundir aún más la terminología, con cosas como Java, hay un compilador que convierte los programas de Java en instrucciones más simples ( código de bytes ) y los almacena en un archivo leído por un intérprete ( máquina virtual ), agregando todo el circo una vez más, solo comenzando desde un nivel subiendo la escalera de la abstracción.

La principal diferencia para los idiomas compilados o interpretados es que los idiomas compilados deben contener toda la información para decidir sobre el flujo de instrucciones de una vez por todas. Es decir, necesitan saber cuáles serán todas las partes, qué tan grandes son, y esto y aquello: el programa debe contener esta información, de una forma u otra, porque el compilador tiene que traducirla en un flujo finalizado de instrucciones. Los lenguajes interpretados pueden inventar y reinventar cosas de diferentes maneras cada vez que ejecutan un programa, por lo que es más fácil para los idiomas admitir programas que cambian, alteran los tipos de datos sobre la marcha, detectan cuando las cosas salen mal y, en general, se comportan un poco más dinámicamente en sus alrededores El precio pagado por esta flexibilidad es que su código no se está ejecutando directamente en el procesador, se está ejecutando encima de otro programa que se ejecuta en el procesador, y para los programas que necesitan hacer un procesamiento pesado todo el tiempo, la capa agregada hará que las cosas corren notablemente más lento.

Dicho sin rodeos, un compilador prepara un programa para que el programa mismo pueda ejecutarse, mientras que a un intérprete le gusta tanto la ejecución que ponemos un programa en su programa, para que pueda ejecutarse mientras se ejecuta.
Por así decirlo.

La respuesta solía ser simple hasta que, imitando a Microsfot, Sun decidió enturbiar las aguas llamando a su intérprete optimizador un “compilador de puntos calientes” 🙁

Entonces, en los viejos tiempos, era simple: un compilador lee el código fuente y lo convierte en código de máquina. Luego, un programa separado carga el código de la máquina y lo ejecuta.

Luego aparecieron los enlazadores, y la imagen cambió un poco: los compiladores ya no producían salida de código de máquina puro, utilizaron un formato intermedio que permitía al enlazador reemplazar marcadores de posición con direcciones reales durante o justo antes del proceso de carga.

Los intérpretes, OTOH, convierten inmediatamente fragmentos de código fuente en secuencias cortas de código de máquina y los ejecutan de inmediato. O, más comúnmente, tienen una “máquina virutal” que usa las primitivas del lenguaje como su “código de máquina”.

Java es un ejemplo de esto. Sin embargo, en realidad combina ambos, haciendo un paso de compilación para crear un archivo de clase y luego interpretándolo en una máquina virtual. Del mismo modo, Pascal también fue un ejemplo de esto, (digo ‘era’ porque más tarde salieron con Pascal compilado), convirtiendo el código fuente en pcode y luego interpretándolo.

BÁSICO originalmente siempre fue interpretado; incluso una línea tan corta como 100 LET A = 0 inmediatamente provocaría que se ejecutara código de máquina para asignar una variable A y poner un cero allí.

Entonces, ¿qué significa todo esto para el usuario de estos idiomas? Lo primero que me viene a la mente es que los lenguajes que tienen compilación y ejecución separadas, como Java y C ++, permiten la verificación de tipos en el momento de la compilación, lo que le ayuda a detectar errores más temprano que tarde. Atraparlos temprano les ahorra a todos tiempo y dinero. Java incluso le permitirá hacer algunas verificaciones de tipo en tiempo de compilación y algunas en tiempo de ejecución. De hecho, si está utilizando genéricos, esta elección se ve obligada a usted debido a “borrado de tipo”;)

Un compilador toma el código fuente que se le da y genera el código de máquina ejecutable. Este suele ser un método más eficiente.
Un intérprete toma el código fuente, y cuando le dice que se ejecute, usa JIT (Just in Time Compilation) para compilar las cosas según sea necesario. Esto generalmente incurre en una leve penalización de rendimiento.
Los lenguajes de programación como C, C ++ u otros lenguajes de alto nivel generalmente se compilan. Lenguajes como Perl, Python, Ruby, generalmente se interpretan. En general, los lenguajes compilados toman un tiempo al principio para construir el código de bytes ejecutable, pero los programas compilados generalmente se ejecutan más rápido después. Los idiomas interpretados son más portátiles (ya que el código de bytes generalmente solo se puede usar en UN sistema operativo) pero conlleva penalizaciones de rendimiento.

Tradicionalmente, los compiladores convierten el código fuente a lenguaje de máquina estáticamente. Por ejemplo, supongamos que tiene un programa escrito en C. Utiliza su compilador de C para convertirlo en un ejecutable que consta de instrucciones escritas en lenguaje de máquina específico para su placa de arquitectura. Es solo después de que todo el programa se convierte al lenguaje de máquina que se procede a ejecutarlo.

Contraste esto con un script bash. No hay un paso de compilación bash separado al que deba someter su script. En cambio, lo que sucede es que el intérprete bash lee el guión una línea tras otra y los ejecuta en secuencia. Por lo tanto, el intérprete a veces se considera un compilador dinámico.

Tenga en cuenta que al comienzo de la respuesta usé la palabra tradicionalmente. Eso es porque es posible tener compiladores que generan instrucciones para ninguna placa de arquitectura física. El compilador javac para el lenguaje Java es uno de esos compiladores, ya que traduce el código fuente de Java en bytecode. JVM es el intérprete aquí que convierte dinámicamente el bytecode en instrucciones de máquina nativas.