La naturaleza del lenguaje de programación C

Esto es parte de una serie más grande titulada “How To Program Anything: C Programming

Prefacio

Este artículo hace referencia y hace uso de la información presentada en mi “Programación Crash Curso” artículo. Se recomienda la lectura de ese artículo.

C fue el segundo lenguaje de programación que aprendí. Me había cortado los dientes en el BASIC según lo proporcionado por mi TRS-80 Color Computer II 16k (de Tandy Radioshack). Habría encontrado otro lenguaje de programación como Pascal antes si no fuera por el hecho de que mi Macintosh IIsi tenía, como instalado por mi padre, un compilador / intérprete de QuickBASIC en él que me permitió continuar mis modos BASIC. Traté de programar un montón de cosas en el BASIC desde la aventura de texto simple, a un opus de 300 pg magnum (escrito en un fin de semana) para un juego de rol de arriba abajo, a un desplazador lateral en tiempo real (que no funcionó en No fue hasta que conseguí mi propia computadora, desafortunadamente no recuerdo sus especificaciones técnicas tan bien como lo hago Quadra de mi hermano mayor, que encontré (e instalé) CodeWarrior. Ahora, en CodeWarrior usamos C, C ++ y Assembly. ¡Ahora como en las grandes ligas!

También fue un buen momento para mejorar mis habilidades de programación también porque los libros que estaba encontrando en mis breves e intermitentes visitas a las librerías de la ciudad (yo vivía en una ciudad semi-rural en las montañas que, en realidad, no tenía librería) C y C ++. Éstos incluyeron los secretos de los asistentes de programación del juego y otros títulos tales para Macintosh. Así que empecé a programar C para el sistema 7, 8 y 9 (sí, yo era un programador de Apple). Aquí es donde primero envolví mi cerebro alrededor de funciones, estructuras, clases, objetos, punteros, direcciones y más. Siempre quise crear algún tipo de biblioteca vinculable a mi instalación de QuickBASIC (el manual decía que podía hacerlo) para que pudiera programar cosas intensivas en máquinas como gráficos en C, pero codificar el resto del juego en BASIC, pero podría por desgracia Nunca lo averigüe. El manual decía que podía hacerse, pero por supuesto dio muy poca explicación.

Pasar de un lenguaje de “alto nivel” como BASIC (que me data) a un lenguaje de “nivel inferior” como C (más en estos niveles) me ayuda a entender mejor las máquinas que estaba programando y mejoró mis habilidades de programación . Ahora tenía acceso a toda la máquina, y podía implementar cualquier cosa que quisiera en ella, no sólo qué parte de la caja de herramientas del sistema el intérprete de BASIC decidió que me dejaría probar. Para cualquier persona atrapada en un lenguaje de nivel superior, pero que desean aprender más acerca de su máquina y la informática en general, como algoritmos y tal, el aprendizaje de un “nivel medio” lenguaje como C es una necesidad. Seguramente puede abrir muchas puertas en el futuro.

¿De dónde viene C?

Un hombre llamado Dennis Ritchie, que entonces trabajaba para un lugar llamado Bell Labs, llegó a la semilla de lo que ahora conocemos como el lenguaje de programación C en algún momento entre 1969 y 1973. C es en realidad el tercero en una línea de una serie de programas tempranos BPCL (Lenguaje de Programación Combinado Básico) fue desarrollado por Martin Richards en la Universidad de Cambridge en 1966. Su principal preocupación en el momento era o la creación de compiladores para otros lenguajes de programación. De hecho, introdujo el concepto de “programación de refuerzo”, algo que muchos otros lenguajes de programación posteriores todavía usan. BCPL influenció el desarrollo de B, un lenguaje destinado a programas no-numéricos, posiblemente recursivos, entre máquinas, desarrollado en Bell Labs alrededor de 1969 por Ken Thompson y Dennis Ritchie.

Para conocer la historia de C en realidad llegamos a conocer un pequeño trozo de historia temprana del sistema operativo eUnix. El sistema operativo Unix original fue implementado en lenguaje de ensamblaje en un PDP-7, también por Dennis Ritchie y Ken Thompson (el hombre que los chicos estaban ocupados no eran ellos?) Cuando el PDP-11 salió llevó a la idea de portar el Y Ritchie y Thompson al principio consideraron el uso de B. Sin embargo, B no fue realmente escrito para aprovechar el PDP-11 y así surgieron con un nuevo lenguaje que abordó estas preocupaciones, principalmente la capacidad Para direccionar bytes. Así, C fue resucitado. C fue el nombre dado el lenguaje presumiblemente porque C viene después de B.

Como un lenguaje compilable, C hizo su aparición de debut en la Versión 2 Unix, y pronto en 1972 una gran parte de Unix fue reescrito en C. Bastante gracioso, C no fue diseñado a propósito para ser ejecutable en muchas máquinas diferentes, pero en parte porque era parte De Unix, y en parte debido a su facilidad y simplicidad, comenzó a funcionar en muchas máquinas diferentes. En 1973, el lenguaje C había disfrutado de suficientes desarrollos que ahora era lo suficientemente poderoso para comprender la mayor parte del kernel del sistema operativo Unix (el núcleo interno del código del sistema operativo). Alrededor de 1977 Richie y Stephen Johnson continuaron el desarrollo del lenguaje para facilitar la adopción del sistema operativo Unix, y Johnson escribió un “compilador C portátil” que aumentó la facilidad de desarrollo con C en nuevas máquinas.

En 1978 Ritchie y Brian Kernighan publicaron la primera edición de The C Programming Language (en la foto a la izquierda). Éste era de hecho el mismo libro que “recibí” en mi curso de programación de ANSI C cuando asistí a la universidad. La primera edición de este libro no era compatible con ANSI (explicado en un poco), ya que el estándar no existía, y sirvió como una especificación informal. Esta versión de C a menudo se llama “K & R C.” Una vez más la adopción generalizada comenzó a ocurrir se extendió, por así decirlo, como un reguero de pólvora y se ha convertido desde entonces en uno de los lenguajes de programación más populares y ampliamente utilizados para golpear el mainframe. De hecho, el Instituto Nacional de Estándares Americanos (ANSI) en 1989 creó una versión estandarizada de C y desde entonces una norma ha sido adoptada por la Organización Internacional de Normalización (ISO). El primer estándar implementó muchas características adicionales que no estaban presentes en K & R C, y fue informalmente llamado C89. El libro seminal se publicó nuevamente en una segunda edición y fue doblado “ANSI C” conforme. C89 sirvió de “base” o fundamento sobre el cual se formalizó el estándar C ++, y este estándar se mantuvo relativamente estable durante varios años. Eso es hasta 1995, cuando se hizo una enmienda a la norma de 1990 (conocida como C95) que corrigió algunos detalles e incluyó más apoyo para conjuntos de caracteres internacionales.

Este no es el final de la historia de C. Oh no, de hecho, el estándar fue revisado en 1999. Esta versión se conoció informalmente como C99. También se modificó tres veces. C99 introdujo varias características nuevas, incluyendo funciones en línea, números complejos, arreglos de longitud variable, miembros de matriz flexibles y, sobre todo, soporte para comentarios de una línea (como en BCPL en el día). Luego, en 2007, comenzó el trabajo en otra revisión que se publicó en 2011. Esta versión llegó a ser conocido como C11, y añade otra serie de nuevas características, incluyendo macros genéricas, estructuras anónimas, operaciones atómicas y mucho más. Cada versión buscada para ser compatible con versiones anteriores como se puede, pero sí incluye los métodos de prueba que la versión se está utilizando para que los programas pueden hacer los ajustes necesarios.

De particular importancia aquí es que C se utiliza a menudo en ambientes embebidos. Lo que esto significa es que se está compilando y escribiendo un programa para ejecutar en el entorno de un microcontrolador o procesador especializado con acceso directo a varios hardware como pines GPIO, etc. Podrías pensar en algunos sistemas “incrustados” más populares como el Raspberry PI, o Arduino, pero incrustado puede implicar cualquier tipo de procesador destinado a ejecutar un RTOS (sistema operativo en tiempo real) “incrustado” en una placa de circuito. Sin embargo, a menudo hay requisitos no estándar (no estándares con respecto al estándar de lenguaje de programación C) en estos tipos de sistemas, incluyendo airthmetic de punto fijo, bancos de memoria múltiples, acceso a pines de hardware e interrupciones y varias operaciones de E / S inusuales. Para abordar esto, el Comité de Normas C ha intentado formalizar lo que se conoce como Embedded C: un conjunto de extensiones de lenguaje que abordan estos problemas comunes.

C es un lenguaje próspero y vivo utilizado en todo el mundo en todo tipo de aplicaciones. Combina la facilidad de usar términos más abstractos para programar que ensamblar sin sacrificar la capacidad de tratar direcciones, bits y bytes. Veremos lo que el futuro tiene para C, y qué tipos de revisiones y mejoras podría disfrutar en la próxima versión.

¿Cómo se relaciona C con otros idiomas?

C es un lenguaje de nivel medio, lo que significa que combina los mejores elementos de los lenguajes de alto nivel con los mejores de los lenguajes de nivel inferior. El “nivel” del lenguaje no es una reflexión sobre la calidad del lenguaje: significa que un lenguaje es más o menos poderoso, ni si es más fácil o más difícil de usar. Es más un reflejo de la abstracción utilizada en el proceso de programación en el lenguaje. Explicaré lo que quiero decir con esto.

Tome un lenguaje de bajo nivel como el ensamblado, por ejemplo. En esta perspectiva usted programa directamente con el procesador, controlando sus ciclos, sus registros, sus direcciones, cada pequeño pedazo es cuidadosamente shuttled alrededor. El problema con algo como ensamblaje, que carece de cualquier tipo de abstracción verdadera más allá de los mnemónicos para los comandos del procesador, que cualquier cosa grande o matizada se vuelve engorroso y forjado con todo tipo de índices, saltos y algoritmos difíciles de entender. Además, como un efecto secundario de trabajar tan de cerca con un procesador particular, el programa eventual carece de portabilidad a otras arquitecturas de procesador.

Así que puedes probar un lenguaje de alto nivel, como BASIC (como yo). Ya no estás moviendo cada bit y byte alrededor, sino en lugar de hablar de variables, arreglos, control de flujo encapsulado (ver mi curso de programación de programación de programación imperativa) y métodos muy abstractos de hacer las cosas. Además, debido a que estos métodos abstractos están programados para usted, pueden ser programados para otras plataformas, haciendo que su propio código sea portátil para esas plataformas. Pero, de alguna manera, aunque ahora se puede abordar el problema que el programa está tratando de resolver sin tener que concretar el movimiento de cada byte, le falta la capacidad de lograr ese nivel de control con el procesador cuando lo desee.

Introduzca el lenguaje de nivel medio: ofrece la facilidad y sencillez de una abstracción de programa superior (con variables, tipo de datos, control de flujo), pero también le da acceso y control sobre los bits más detallados que lo hace flexible y potente (y rápido ) Como algo parecido a la asamblea. Además de eso, con un poco de preparación cuidadosa, C es fácilmente portátil a otras máquinas. Aquí es donde reside gran parte del poder del lenguaje de programación C.

¿Qué hace C?

Una noción fundamental del lenguaje de programación C es la de la función. La funcionalidad funcional de C permite al programador encapsular algoritmos y piezas de instrucciones en trozos que pueden ejecutarse solos, con su propia entrada y salida y sus propias variables, aparte de otras piezas de código. Básicamente una función idealmente no tiene ningún impacto en el funcionamiento de otra función, y debido a esto se puede escribir en esencia mini-programas que pueden trabajar juntos hacia alguna solución. La mejor parte es que, si sabes lo que una función se puede utilizar sin saber necesariamente cómo se programó. Esto se conoce como compartir secciones de código y reutilización de código. Un lenguaje que implementa esta compartimentación, como C, se conoce como un lenguaje estructurado. Los lenguajes estructurados en contraste desalientan el uso de variables globales y otras nociones tales porque tener algo que cualquier parte del código puede afectar en cualquier momento es muy volátil y en contra de la idea de encapsulación funcional. Una función impacta el estado y la operación de otra función indirectamente. Del mismo modo, el uso de un comando de salto (conocido como goto de BASIC) está estrictamente prohibido o desalentado. Saltar de una pieza aleatoria de código, o funcionalidad, a otro interrumpe el flujo del programa y afectaría negativamente a la compatibilidad. Para pasar de una parte del programa a otra, debe pasar por las pasarelas de las llamadas de función. Por cierto, las colecciones de estas funciones, y sus “pasarelas”, se llaman interfaces.

Nota: en una escala más “miniatura” la estructuración también ocurre a través de lo que se conoce como bloques de código. Estas son instrucciones encerradas entre llaves, como en un bucle o una cláusula. Éstos se tratan como una unidad de código en el sentido de que todas las sentencias del bloque se ejecutan juntas (linealmente).

C nos permite usar partes comunes de la programación imperativa con muchas instrucciones de control de flujo como if-clauses, while loops, y for loops. Sin embargo, de mayor importancia es el sistema de punteros y direcciones. C emplea un par de operadores que nos permiten referirnos a una pieza de datos como su valor, como el acceso a variables normales, o para referirnos a la dirección de memoria de esos datos. Una variable que se refiere a una pieza de datos de la dirección de memoria se conoce como un puntero en C. El puntero y el sistema de direcciones en C es a la vez simple y complejo: simple en la forma en que está diseñado (en mi opinión), complejo en cómo es Generalmente empleado en la programación del mundo real. Este sistema de manipulación de la memoria es parte de lo que da C es de “bajo nivel” de poder y su capacidad de ser comparado en algunos casos a la programación de ensamblaje.

Otra cosa que se conoce a C es su falta de tipificación de datos fuertes. Lo que esto significa es que C emplea tipos de datos para variables (si recuerda de los tipos de datos de curso intensivo prescribir qué tipo de variables de valores pueden contener), no impone estos tipos de datos para las operaciones. Por ejemplo, en C puede utilizar números enteros, caracteres y variables de tipo de punto flotante todos en la misma expresión matemática. Esto se logra mediante lo que se denomina conversión implícita de tipos. Al compilar C, el compilador convierte tipos de datos dispares en los formatos requeridos para que se produzca el cálculo. Es posible en algunos casos pasar tipos de datos diferentes a las funciones que definen para sus argumentos, C convierte automáticamente los tipos de datos a encajar.

C es un poco de un lenguaje conciso también. Lo que quiero decir con esto es que C utiliza un número mucho menor de palabras clave (cadenas de caracteres que son interpretadas por el compilador como parte del lenguaje) que muchos otros idiomas. C89 emplea 32 palabras clave, mientras que C99 añade 5, y C11 añade sólo unos pocos más. BASIC como un punto de comparación puede tener hasta más de 50 palabras clave.

¿Qué hace C no hacer?

Debido a la conversión de tipo implícita C no requiere ni impone una compatibilidad de tipos estricta entre variables, funciones y expresiones. También, debido a la influencia de su uso por los “programadores del mundo real” (más en ése abajo) deja bastante que el programador haga lo que él quiere y sale de la manera. Esto significa que algunas cosas que se dan por sentado en lenguajes de nivel superior no existen en C, como la comprobación de errores en tiempo de ejecución. Lo que esto significa es que no hay ninguna comprobación de error potencial hecha mientras se ejecuta el programa. Esto se traduce, prácticamente, en una falta de cheques o avisos para informar que los límites de la matriz no están superados, que las conversiones de tipo no son maliciosas, que no se utilizan punteros que apuntan a nada o basura, etc. Escribir el programa. Cuando trabajé como un programador profesional de tiempo completo no puedo decirle cuántas veces un puntero nulo causó mi programa a seg-fault.

C no realiza ningún tipo de recogida de basura en tiempo real (es decir, mientras el programa se está ejecutando). En muchos lenguajes de nivel superior como Python, cuyo intérprete (cubriremos la diferencia entre compiladores e intérpretes en otro post) está escrito en C, los objetos de datos se almacenan en la memoria hasta que ya no son referenciados por ninguna otra parte de las variables en el programa. A continuación, se eliminan sucintamente de la memoria de forma automática, no se requiere ninguna acción por parte del programador. Esto se conoce como recolección automática de basura, y se puede encontrar en muchos idiomas que van desde JavaScript, Java, Python y más. Debido a que C permite la manipulación directa de la memoria y “sale del modo de los programadores”, depende del programador proporcionar sus propios medios de recolección de basura. La memoria permanecerá en uso hasta que el programador lo libere.

Conclusión: ¿Qué Influenció C?

C fue creado para los programadores. Esto puede parecer redundante, pero recuerde, durante el tiempo de la creación de C muchos lenguajes fueron creados para mitigar el problema de los individuos no técnicos diciendo a la computadora qué hacer. Sofisticados programas de usuario final que envuelto todo en buenas interfaces de usuario para casi cualquier campo que se puede pensar que no existía aún. El uso de una computadora muchas veces significaba programar para hacer lo que querías y había muchas personas no técnicas, que a pesar de esto, necesitaban realizar esta tarea. Lenguajes como BASIC, PASCAL y COBOL fueron inventados para tratar de aliviar esta dificultad. BASIC y COBOL, por ejemplo, no fueron diseñados para ser utilizados por personas altamente técnicas. Hicieron muy poco a nada para mejorar el proceso de su tarea de programación de una computadora. Fueron diseñados para permitir a los no programadores definir las cosas que el ordenador debería hacer.

C fue creado fuera del mar de la programación de ensamblaje por los programadores que querían hacer cosas altamente técnicas. Se trataba de mejorar el proceso en el que los programadores profesionales ya estaban comprometidos en el mundo real. Se entendía como una forma mucho más rápida y más confiable para lograr los mismos tipos de cosas que se podían hacer con la programación de ensamblaje, sin el dolor de cabeza de la programación de ensamblaje. De hecho, ese C se puede utilizar en lugar del lenguaje ensamblador, pero con el poder del lenguaje ensamblador, fue una clave enorme para su éxito.

En sus primeros días C se empleó para lograr propósitos altamente técnicos, que es en particular la programación de sistemas operativos y sistemas de control de mainframe. Estos tipos de programas necesarios para programar procesos, ocuparse de la memoria de una manera muy íntima, ofrecen servicios de soporte a otros programas y, en general, requieren acceso a la máquina a nivel de ensamblaje. C con el ensamblaje y, de hecho, la superioridad sobre el ensamblaje en términos de que C es un lenguaje estructurado, mejoró considerablemente estos campos.

Hoy en día muchas personas pueden lograr un poco con los lenguajes de nivel superior, y muchos los usan en lugar de un lenguaje de nivel medio como C debido a su accesibilidad. Las máquinas de estos días han utilizado lenguajes como C para crear entornos de sistemas operativos muy sofisticados que permiten que los lenguajes de nivel superior como Python o JavaScript puedan lograr muchas de las mismas cosas que C ha tenido en el pasado, sin embargo, C sigue siendo el fundamento. Y puede encontrar esa base aún en uso práctico hoy en día en el reino de los procesadores y microcontroladores integrados. Hay computadoras de una sola placa que ofrecen un sistema operativo completo que nos da el poder de Python o JavaScript (como el Raspberry Pi, o el BeagleBone), pero hay una miríada de pequeños “incrustado” ARM procesador de juntas tales como los ofrecidos Por Texas Instruments que requieren el uso de C para controlarlos. Estas placas tienen un RTOS desnudo (sistema operativo en tiempo real) y ofrecen al programador cerca del control total de los circuitos dados. C ha servido de fundamento, curiosamente, a las formas más sofisticadas y accesibles de la programación, pero al mismo tiempo está aquí para quedarse para las operaciones de programación de bajo nivel. Estando casi a un paso de la asamblea, es un excelente lenguaje para cualquier aspirante a programador a aprender y dominar.

Esto es parte de una serie más grande titulada “How To Program Anything: C Programming

Si te gustó este artículo, podrías considerar la posibilidad de apoyarme en mi Patreon.

Si un compromiso mensual es un poco más para usted que está bien, usted podría considerar la compra de mí una taza de café.

photo credit: Ubahnverleih RoboCup 2016 Leipzig – Debugging via photopin (license)

También te podría gustar...

Deja un comentario

A %d blogueros les gusta esto: