Píldoras del conocimiento avanzadoEn homenaje al Promotor y calendario Taco

Sagrada Familia con San Juanito (Rafael)

Hoy: Compiladores, intérpretes, máquinas virtualesy otros seres o enseres de la zoología digital

Este artículo, y -posiblemente- serie de artículos, está humildemente dedicado a 2 personitas que aprecio, que en su sempiterna agónica y desesperada ignorancia sobre temas informáticos, pues siempre me hacen explicarles cosas, que no soy yo mucho de explicar, y explicar bien, pero bueno, me he esmerado, y he puesto todo mi cariño, además de moderme la lengua multitud de ocasiones mientras lo escribía pa no insultaros de lo burras que sois (sí, hasta explicándooslo mentalmente me desespero). ¡Oh, sí!, esas personas son mi hermanita Su, y mi única mejor amiga Cris. Todos los demás que leáis esto y le encontréis provecho, dadle las gracias por ser como son a esas personas. Bueno, mejor no, si os acercáis a ellas os dispararé en las rodillas. Avisaos estáis (esta frase me exime de responsabilidad penal).

Con gusto Hugo Semper Fi. 09/07/2013.

Una flor

1. Entrada en concepto, en materia, o prolegómenos

Los complejos relojes modernos cada vez están más cerca de superar en inteligencia a los primates más bobos. Aunque últimamente con la proliferación de programas de telerealidad, la endogamia instaurada y la coprofagia sin control, causantes de la degeneración del genoma humano que está provocando que este fenómeno esté sufriendo una aceleración incierta.

Un ordenador es una cosa tonta, tonta. Pero tonta, tonta, tonta pa perro. Lo cierto es que es tonta y hace cosas tontas, sólo es útil porque las hace muy rápido y porque los humanos somos vagos de cojones pa andar haciendo muchas cosas tontas... (¡ojo!, tontas y aburridas, no es comparable con hacer balconing o cruzar la calle sin mirar).

Lo cierto es que los ordenadores se pueden entender bien pensando modelos tontos, también, como el de las interfaces. Siempre andan explicando esto en facultades pero yo nunca lo entendí, no hicieron mucho énfasis, por lo menos hasta que di POO y patrones de diseño.

La teoría de interfaces se basa en que para manejar, utilizar o servirse de cualquier cosa compleja, se nos expone una interfaz. Esta interfaz nos permite cierta libertad y manejo a cambio de hacerlo más sencillo y ocultarnos lo que sucede entre bastidores.

Ejemplos, ejemplos, que es gerundio.

Digamos entonces que las interfaces son lenguajes propios o ganchos, vocabularios, es un modo de entenderse. Cada concepto tiene asociado algunas acciones, aunque por debajo no sepamos cómo se mueven los engranajes ni cuantos hay, sí sabemos que lo que queremos es que el la aguja del minutero se mueva de determinada forma.

Des-descarrilando, que al final acabamos empotraos en el matorral

Un ordenador al final es eso, la CPU tiene una interfaz, un modo de funcionar que los programadores deben conocer. La CPU, el cerebro, lee códigos de la memoria, que interpreta como instrucciones, a esto se le llama código máquina, unos y ceros. Cada una de esas instrucciones tiene una representación textual para entender los humanos, que se llama ensamblador.

Por ejemplo 111001001000111101 para el ordenador significa chsst, oye, si no te parece mal, porqué no cargas estos datos y llamas a esa subrutina que me los va comprobar si me gustan o no, y en ensamblador podría llamarse LOADANDCALL 450.

Tener en cuenta las distintas interpretaciones que obligatoriamente se tiene que hacer de la misma cosa, sólo tenemos unos y ceros, a veces 11110000 será una instrucción, otras será un número entero, otras una letra. Aunque para el ordenador sólo es un puñado de bits (8), por eso suele decirse que los ordenadores machacan bits (unos y ceros).

La CPU ejecuta cada una de estas instrucciones secuencialmente, y esto hace que funcione todo. Las instrucciones son sencillas, las hay para mover datos de un lado de memoria a otro por ejemplo. Las hay para tratar esos datos (bits) con operaciones matemáticas (sumar, restar, multiplicar, etc), que se interpretan (los datos) como números, números diferentes, enteros, reales, etc. Las hay para modificar el flujo, quizá en un momento no queremos seguir ejecutando por ahí (secuencialmente) y queremos saltar a otro lado a ejecutar otra cosa.

Luego la CPU tiene interfaces con otros componentes, externos o internos a ella. Por ejemplo para comunicarse con el monitor tiene que ordenarle instrucciones/mandatos (para no confundir con instrucciones en ensamblador). Dibuja esto (punto, línea, triángulo), pon esta resolución, apágate para ahorrar energía, etc. Esas interfaces pueden ser a través de la memoria por ejemplo, ciertas zonas de memoria al escribir en ellas, no van a la memoria sino al monitor, por eso en lugar de memoria hablamos de espacio de direcciones, y en las direcciones está todo, la memoria, los periféricos, etc. Digamos que la interfaz común es el de acceso a memoria por estas direcciones, aunque luego esos datos pueden no ir a los bancos de la memoria RAM, sino a algún otro tipo de hardware. Todos absolutamente todos funcionan así.

Entre diferentes periféricos que podemos pensar están los típicos, disco duro, teclado, monitor, etc. Pero también hay otros tipos de chips o de hardware del que no podemos tener conciencia, la memoria RAM puede tener un controlador (otra especie de mini-cpu, o microcontrolador-mcu), la BIOS, el reloj del sistema y otros controladores para tantas cosas.

Agárrate que empieza lo bueno: Los lenguajes y compiladores.

Plataforma

Quedamos entonces en que las CPU's, ese Intel Core Trio i10 Orgium, ejecuta instrucciones muy simples, tontas... sólo que muy rápido. A partir de aquí los seres programadores, tuvimos que colaborar, ya que nadie puede avanzar si tiene que partir de cero (reinventing the wheel dicen en inglés).

Para empezar las instrucciones de cada chip, de cada CPU son diferentes, no todas entienden las mismas órdenes. Esto es parte de lo que viene a llamarse la plataforma, concretamente la arquitectura.

El ensamblador de los procesadores de mesa Intel o AMD, x86 o x86_64 (los de 64 bits), no hablan el mismo idioma que los ARM (tu móvil casi fijo lleva uno), o los SPARC, o los ALPHA, o los MIPS (las primeras Playstation) por poner un breve ejemplo.

Aún así, tener la misma CPU no es suficiente para que algo pueda funcionar sin tocarse de una máquina a otra. Aparte que hay mas chips implicados (en juegos quizá tu tarjeta no soporta OpenGL 3.3 y no puedas jugar al último Super Mario Bros Survival Carnivore, OpenGL es otra interfaz), también hay partes que funcionan construídas sobre asunciones (cosas que se dan por sentadas, o se esperan), o pactos, entre diferentes partes. Todo para simplificar el desarrollo. Si ves esto raro, sólo imagina un chip, un circuito, que contiene una de estas unidades controladores, un microprocesador o microcontrolador (la diferencia es sutil), que está destinado para una operación espacial, y que debido a la radiación alta por la zona que va a estar cerca del Sol, la memoria RAM que tiene cambia por efecto de esta radiación espontáneamente. En las CPU's de nuestras casas parece aceptable suponer que si guardo un dato en la RAM este se guardará inalterado, ningún programador está pensando en la posibilidad contraria, sin embargo quizá en ese otro entorno las condiciones, las reglas del juego y las suposiciones (lo que se da por sentado) tengan que ser diferentes, lo cual nos otorga control sobre ello (podemos hacer que la RAM se duplique en 2 sitios o que tenga códigos de control de error en otro tipo de memoria acorazada contra radiaciones que nos permita detectar estos casos y actuar en consecuencia).

Por ejemplo otra parte de la plataforma, es el sistema operativo. El SO para abreviar, es responsable de que puedas correr programas sin que se peguen entre ellos, los considera como seres en un entorno competitivo, todo proceso quiere para él toda la memoria y toda la velocidad de la CPU, los hay incluso malvados que quieren borrarte el disco duro o estropearte el ordenador (el SO te protege). También provee comodidades, como los ficheros, si cada programa tuviera que acceder al disco entero por sectores sería un caos, no podrían llegar a una acuerdo entre ellos (no se conocen, son de diferentes empresas).

Entonces la plataforma está constituída básicamente por esas 2 cosas, o interfaces, la arquitectura (los chips que usa la máquina) y el sistema operativo (los programas de Windows no funcionan en MS/DOS o en Linux). En realidad la plataforma es algo más general, puede pensarse en ella como el lugar al que se tiene que acomodar el programa que viene para hacer una tarea.

Plataforma con S.O. Linux Diagrama de una plataforma, se suele estructurar por capas, cada cual por lo general sólo accede a la capa inferior por medio de una interfaz que ésta expone.

Entre las asunciones, por poner un ejemplo, el núcleo de Linux, si quieres tener un sistema operativo Linux, exige que la máquina donde se vaya a ejecutar tenga una unidad de control de memoria, hay chips malejos que no tienen esto, pues ahí no puedes compilar Linux, o no tan fácilmente. Por cierto que sepas que tienes Linux aunque leas esto desde el Internet Explorer en tu Windows 7 pirata. Para que veas que el concepto arquitectura y plataforma es muy amplio. Tu router me apuesto los huevos (y son de casa) a que corre Linux. Tu smartphone corre una versión modificada de Linux quizá, Android. Tu tele quizá lleve Linux al igual que tu disco duro multimedia, o tu mp3 de última generación. Asín que eres otra maldita linuxera friki más en sin saberlo.

Portabilidad y alto/bajo nivel

Aquí empezamos con la necesidad de lenguajes y compiladores. Portabilidad se le dice a la facilidad de que un programa escrito para hacer cierta cosa, funcione en diferentes plataformas. Este es uno de los intereses de los lenguajes.

El otro es el [alto] nivel, los lenguajes de programación suelen considerarse de más alto a más bajo nivel, siendo el único considerado de bajo nivel (nivel hacia la máquina) el ensamblador.

Los lenguajes proveen de maneras de escribir ensamblador sin saber que lo escribes (en realidad no lo escribes, sólo se traduce después), y de un modo mucho más rápido. También por tanto vienen con modelos o abstracciones de su idea de lo que es un ordenador, o la computación que se adaptan a los modelos reales del hardware (chips, cpu, etc).

Por último un lenguaje de alto nivel, proveerá naturalmente de portabilidad, aunque esto es algo dependiente del compilador, ahora lo veremos mejor. Es decir un lenguaje de alto nivel con su modelo ficticio puede adaptarse a varios modelos reales, pero un modelo real de ensamblador es por lo general absolutamente incompatible con otros, esto es, un programa escrito en ensamblador sólo correra en la máquina concreta para el que está escrito.

El mismo programa en diferentes representaciones Programa que calcula el máximo común divisor de dos números por el algoritmo de Euclides. Escrito en diferentes lenguajes o representaciones. De izquierda a dedrecha: lenguaje C, ensamblador de ARM, ensamblador de x86 (sistemas PC)

Lenguajes, traductores e intérpretes (así se llamaba una optativa que hice y que por supuesto llamábamos por una estúpida sigla abreviatoria)

Entonces los lenguajes que escribimos, de alto nivel, como son el C (aunque se le considera de bajo nivel dentro de los de alto, que lío), C++, Pascal, Java, Javascript, Fortran, Visual Basic, C#, Haskell, etc. No son entendidos por la máquina, por ninguna, de hecho. Sólo son mejor entendidos y manejados por nosotros, es una especie de puente.

De todos modos nuestro objetivo final es que la máquina nos entienda, no entendernos sólo a nosotros mismos (eso es más bien del psicoanálisis), aquí es donde intervienen los traductores.

Un traductor es el que convierte el código de alto nivel a código máquina (el traductor es un programa, me refiero, por supuesto). Los hay básicamente de dos tipos, compiladores e intérpretes. Se diferencian en el modo de hacerlo. Los compiladores cogen tooodo el programa, que puede ser largo de narices, y lo transforman en un objeto, un fichero posiblemente, o varios, que la máquina entiende y puede ejecutar sin mayor problema. A veces la compilación puede tardar lo suyo. No suele ser un proceso muy rápido.

Los intérpretes por otro lado, van cogiendo línea a línea del programa, o elemento a elemento, lo traducen y lo ejecutan on-the-fly, en el aire, en línea.

Las ventajas e inconvenientes de uno y otro son varios. La compilación puede ser lenta, pero el programa es más rápido una vez generado (mucho más rápido) que uno interpretado.

La interpretación permite más flexibilidad desde el punto de vista de la portabilidad, tú al compilar recibes un objeto que sólo corre en la máquina para la que compilaste. Al interpretar llevas o bien el mismo código fuente, que es el que tiene el lenguaje de alto nivel, o una representación intermedia (sí, a veces se compila lo que luego se va a interpretar, en otra máquina intermedia, véase Java)

La última bestia o bastión, la máquina virtual.

Habiendo pillado esto anterior more-or-less, podemos pasar a eso llamado la máquina virtual. La máquina real ya sabemos cual es, esa llena de chips que se programan en un lenguaje muy tonto y pesado, el ensamblador. En el que por ejemplo para hacer un cálculo como a+b+c*3-2, tenemos que hacerlo operación por operación, sacando valores de memoria y volviéndolos a guardar, algo tedioso.

El producto de la compilación, como quizá dijimos, es un ejecutable, que se llama normalmente, esos ficheros .exe en Windows. Una vez compilado, el programa pasa a un estado de poca portabilidad, se compila para una plataforma determinada.

Quizá te chirríe que antes te dije que los lenguajes de alto nivel venían para darnos portabilidad entre otras de sus cualidades. Y sigue siendo cierto, lo que pasa que la portabilidad se queda con el código fuente, si queremos hacer uso de esa portabilidad para ejecutar ese programa en otra plataforma hay que volver a compilar y generar otro archivo diferente.

A veces no es tan sencillo, en la práctica son necesarios pequeños cambios al programa (código fuente), que puede ser muy costoso, pero siempre es más fácil que reescribirlo de cero como pasaría si fuese escrito en ensamblador. Al final es el número de cosas a cambiar.

Con la interpretación esto no pasa. Sin embargo como dijimos, perdemos mucha velocidad. Al pasar de ensamblador a alto nivel ya solemos perderla, ya que el compilador al traducir el programa es un poco más tonto de lo que podría hacer un buen programador a mano, pero en la práctica nos vale (y tener en cuenta que un mal programador haría peor código en ensamblador posiblemente que un buen compilador).

Entonces siempre se puede optar por el camino de en medio. Una máquina virtual, es una especie de chip, cpu, máquina, ordenador, imaginario, con una plataforma concreta propia inventada, y con un modelo lo más genérico para adpatarse al máximo de plataformas reales posibles. Entonces los programas ahora los compilamos, pero a un lenguaje ensamblador falso, ficticio, que ninguna máquina entiende, sino que es traducido por otro programa en línea, lo que antes decíamos, interpretar. Y precisamente ese programa es lo que llamamos máquina virtual, aunque la MV en sí sea un concepto más bien, una definición. Esto lo hace más rápido, muchas veces, mucho más rápido, aunque siempre a medio camino, entre interpretados puramente y puramente compilados.

Algunos ejemplos y casos prácticos I: El lenguaje C.

Uno de los más populares y exitosos lenguajes de programación es el C. Este es compilado, y probablemente el más portable y eficiente (rápido, y ligero en memoria y recursos). He de notar aquí que los lenguajes en los términos que hablamos no son más o menos eficientes, más o menos rápidos, más o menos portables, esto recae puramente en las implementaciones, lo que los hace realidad, esto es llamado de otro modo los compiladores. Por eso cuando digo que C es de los más eficientes, rápidos, portables, etc; no me refiero a un hecho teórico sino fáctico.

Se inventó exclusivamente para hacer uno de los primeros sistemas operativos importantes, el UNIX, que luego dio a muchos otros, como (en uso actuales) el GNU/Linux, el FreeBSD/OpenBSD/NetBSD (los BSD), o no sé, el GNU/Hurd.

El modelo que presenta es de statements o instrucciones o órdenes que se ejecutan secuencialmente, de una en una de arriba hacia abajo. Se estructura en funciones, que son llamadas desde otras y contienen estas órdenes, reciben parámetros y devuelven resultados. Y la memoria la expone a través de un gran espacio accesible, se accede por medio de punteros de memoria o punteros, aunque también incluye variables, que son regiones de memoria a las que asignamos nombre y le damos un tipo o significado, por ejemplo una puede ser un carácter del código ASCII (letras), otra puede ser un número entero, otra un puntero, otra un array(vector) de otros valores, etc.

El proceso para hacer un programa en C suele comenzar por escribir las funciones que hacen la funcionalidad deseada en el lenguaje C y organizadas en uno o varios ficheros con extensión .c

Luego compilamos cada fichero y obtenemos un fichero objeto (extensión .o), este aún no es ejecutable, es un paso intermedio. Luego linkamos o enlazamos varios ficheros objeto para generar el ejecutable final. La fase intermedia se introduce para ahorrar y facilitar la compilación, imaginemos un programa de 1000 ficheros de código fuente, compilamos y probamos el programa, y queremos cambiar algo, el título de la ventana, para hacer ese cambio sólo necesitamos tocar 2 ficheros de los mil, si no hubiera ese paso intermedio tendríamos que compilarlo todo junto de nuevo, tardando lo suyo; de esta otra manera, sólo re-compilamos 2 ficheros. También se utiliza para crear librerías, normalmente cuando quieres usar algún comportamiento complejo en un programa haces uso de librerías o mejor dicho, bibliotecas (libraries), estas contienen un API (de Application Programming Interface), que es una interfaz, en C són funciones a las que puedes llamar, y esta biblioteca hace el trabajo por tí. Por ejemplo quizá necesites hacer un programita que saque ventanas to guapas en la pantalla, para eso necesitas una biblioteca, para que tu digas ventana y él haga las rayas y te abstraiga de todo lo que está pasando por debajo. Pues estas bibliotecas no hace falta que sean entregadas en código fuente, sino que ya vienen compiladas, sólo que a formato de código objeto, para ser unidas a los otros objetos en el enlazamiento y generar un ejecutable con todo.

Por último decir que a estas se les llaman bibliotecas estáticas, porque son pegadas dentro del ejecutable. Muchos sistemas operativos proveen de otro tipo de enlazamiento, que es el dinámico, las bibliotecas(o librerías, mal dicho pero se usa en el hablaje común) dinámicas. Las .dll de Windows, o .so de Linux. Estas son pegadas al programa pero no en el fichero ejecutable, sino en el momento de ejecución, cuando el programa es llevado de disco a memoria, o también dicho cargado, entonces ahí se enlazas nuevamente cosas.

Quizá tú misma puedas pensar en las consecuencias de esto, las bibliotecas estáticas aumentan el tamaño del programa (si tienes 100 programas que todos usan la misma biblioteca, necesitas 100 copias de la biblioteca, cada fichero tiene la suya). Con las dinámicas no, sólo necesitas una copia. Por otro lado las librerías dinámicas a veces no aparecen, entonces el programa no funciona, mientras el que se enlazó estáticamente tiene las pilas incorporadas y no puede pasarle esto. Estoy seguro que alguna vez su Windows le dijo que le faltaba la MSVCRT100.dll o algo de eso. La próxima vez que le pase, dígale a la compañía que le vendío el software que se lo enlace estáticamente, verás que risas, de boquiabierto que lo dejas al jefe de Google.

Con las bibliotecas estáticas ese quebradero de cabeza se traslada al programador, que a veces no sabe por qué el compilador no encuentra la biblioteca que tenemos instalada.

Algunos ejemplos y casos prácticos II: El lenguaje Java.

Pasemos a algo completamente diferente. Java nació como un intento de conseguir la máxima portabilidad posible. Por Java se entienden muchas y diferentes cosas. Tenemos por una parte el lenguaje de alto nivel llamado Java, luego la máquina virtual que ejecuta el código intermedio, llamada JVM o Máquina Virtual de Java. Luego aplicaciones Java, incluso suena a applets Java, que son programas para ejecutarse en el navegador (obsoleto ya).

Java es un lenguaje de cierto medio-bajo nivel, parecido a C. Sin embargo es orientado a objetos, y basado en clases (hay diferentes aproximaciones a la orientación a objetos). El código Java es compilado a un código intermedio, un código parecido al ensamblador, llamado bytecode. Los ficheros .java son compilados a .class, a veces los .class son agrupados en .jar que forman los ejecutables de Java. Luego los ejecutamos o lanzamos con la máquina virtual, que se llama java, hay un comando que es java, por ejemplo java -jar Aplicacion.jar. Luego siempre necesitamos un programa, ese java (que es la máquina virtual), para ejecutar los programas en java.

Diagrama estructural de la plataforma Java Diagrama estructural un poco más detallado de la plataforma Java. Se puede observar cómo se crean un montón de abstracciones comunes que luego funcionarán sin ningún problema y transparentemente en cualquiera de las plataformas soportadas.

El modelo como decía es basado en objetos y clases. No hay un acceso tan directo a la memoria como en C. Lo que es muy común en lenguajes de alto nivel. Con el lenguaje LISP nació una tecnología que se quedó entre nosotros, los recolectores de basura (garbage collector). En C cuando necesitas memoria, para que tu programa crezca en ejecución (por ejemplo tienes que cargar un documento de más o menos megas), tú tienes, o mejor dicho, el programador tiene que estar pendiente de cuando acabe de usar esa memoria, la libere para poder volver a ser usada. Sino se quedará un agujero, o leak (de algo que queda colgado), un memory leak. Estos agujeros tenderán frecuentemente a una irremediable falla del sistema o el programa, cuando se acabe la memoria no podrá seguir funcionando y el sistema operativo lo abortará (por las malas).

A esto se le llama gestión manual de la memoria, y es muy engorrosa, fuente de la mayor parte de bugs y exploits (cuando nos hackean o nos meten virus son por estos descuidos de los programadores muchas veces).

En contraposición tenemos la gestión automática de memoria (suena mejor, ¿eh?), que es lo habitual, la moneda de uso corriente en los lenguajes de alto nivel. Es el intérprete (es más fácil hacerlo interpretado), el que detecta cuando ya no usas algo en memoria, para entonces liberarla. Es esencial que la memoria no se pueda liberar cuando aún hay posibilidad de usarla. Así el programador no se entera de nada y coge memoria a su antojo que ya se encarga un ángel de la guardia en reciclarla según la vamos tirando.

En java tenemos referencias a objetos, es lo que se maneja en las variables, de modo que se cuentan el número de referencias que apuntan a un objeto, cuando un objeto se queda sin referencias es inaccesible por el programa (¿cómo encuentras a un amigo cuando perdiste su teléfono?), y entonces es marcado para eliminar. Cada cierto tiempo el recolector de basura interrumpe la ejecución del programa y barre la memoria borrando los objetos marcados. Es una simplificación, hay muchos tipos de recolectores de basura, muchos métodos y técnicas (cada cual con sus virtudes y defectos).

En java, el intérprete que tenemos, el de la máquina virtual, también usa una técnica llamada compilación JIT (Just-In-Time), en Java con lo que les gusta a las corporaciones poner sus propios nombres a las cosas ya inventadas, se lo bautizó con HotSpot, que usan muchos otros intérpretes. En lugar de interpretar instrucción por instrucción, coge un bloque de ellas y las compila a lenguaje máquina, luego ejecuta eso, de ese modo es mucho más rápido. Hay veces que ciertos bloques de instrucciones se repiten, por ejemplo en un bucle, así no hace falta volver a compilarlos al lenguaje máquina con el JIT, lo reaprovecha. El lenguaje Java obtiene muy buenos resultados de velocidad en muchos benchmarks, aunque siempre es y será adelantado por C y C++ (los 2 grandes compilados).

Algunos ejemplos y casos prácticos III: Los otros pobres parásitos del trastero.

Tener en cuenta que los lenguajes son eso, lenguajes, el hombre es el que les da interpretación. Nada impide que un programa en C pueda ser interpretado o pueda ser compilado para la máquina de Java. Esto más bien es la tradición, la lógica y sentido común también, y por supuesto el trabajo, lo que existe, los compiladores, la práctica, el mundo real.

Un koala Hacía rato que no ponía una foto y estaba quedando soso. No tiene nada que ver, pero un bichejo siempre alegra el día y saca una sonrisa. Aquí: un bonico koala, el rey de los pantanos

Java será muy portable, pero no creo que encuentres muchas máquinas virtuales de java para plataformas que usen microcontroladores PIC, por ejemplo. Puedes hacértela tú, seguro, pero es un proyecto de buena envergadura. Compiladores de C, como históricamente se tomó este lenguaje como el más bajo de los altos, y el más portable, pues no es raro encontrarse, más bien es de esperar, un compilador de C para arquitecturas raras, microcontroladores, nuevas arquitecturas. Lo normal es que cuando se diseña una nueva arquitectura (un procesador) y se saca para uso público o al mercado, venga con un compilador de C como mínimo (también se le llama toolchain, por la cadena de compilar, ensamblar, enlazar). O quizá de otro lenguaje de alto nivel, en microcontroladores aún se usa el BASIC o el PASCAL, o hasta el FORTH. También decir que no son compiladores de C puro, cada fabricante cambia sus cosillas, quizá no te deje usar tipos de números reales (porque el procesador no lo incluye), o te de ciertas funciones para acceder a funcionalidad específica.

Big resumen

Comentarios: Tú opinas, sin censuras. La opinión del pueblo

Radiactivo
O la zona marrón caca y cutre pa los pesaos

Samuel Sabana dijo...

Esta chévere esta página wei, pero cómo se hace para aplicar el crack del photoshop cs3? alguien sabe? ayuda plssssss

Anónimo dijo...

No funsionan los links men, esta berga no ba bien boludo rechingón ojala se muere en tremendos dolores y espamos huebón y se le sarga la bilis por la nariz ijoo e perra conchudo

Alonsocito dijo...

Guaaaa, sabes que te pasas tío, seguro que me puedes alludar, no me deja entrar al hotmail, porfi, me sale una pantalla blanca y luego azul como de un error, y luego pone error 209: canot found final exeception fatal error hohoho o no se que? fue despues de poner un mod de minecraft con chetos para volar e infinito credit points, te dejo mi email alludame por favor «email suprimido por el bot anti spam»