|
|
||
| PRIMERO DE INGENIEROS INDUSTRIALES | ||
| UNIVERSIDAD POLITÉCNICA DE CARTAGENA |
FUNDAMENTOS DE INFORMÁTICA. PROGRAMACIÓN EN C. |
|
|
Pedro María Alcover Garau |
PRIMERO DE INGENIEROS TÉCNICOS INDUSTRIALES |
|
|
0.
Presentación. |
LENGUAJE C
Un lenguaje de programación es un conjunto de símbolos y de reglas para combinar esos símbolos, que se usan para expresar algoritmos. Un algoritmo es una forma de describir la solución a un problema. Se establece como una serie ordenada y finita de instrucciones elementales que trabajan sobre datos, y los transforman en busca de una solución. El algoritmo busca, a partir de unos valores iniciales, unos nuevos valores finales. Casi siempre hay que procesar los datos a través de múltiples transformaciones. Los lenguajes de programación, como todos los lenguajes, poseen un léxico (vocabulario o conjunto de símbolos permitidos), una sintaxis (que indica cómo realizar construcciones de lenguaje) y una semántica (que determina el significado de cada construcción concreta). Los lenguajes de programación están especialmente diseñados para programar computadoras. Sus características fundamentales son: 1. Son independientes de la arquitectura física del ordenador. Los lenguajes están, además, normalizados, de forma que queda garantizada la portabilidad de los programas escritos en esos lenguajes. 2. Normalmente un mandato en un lenguaje de alto nivel da lugar, al ser introducido, a varias instrucciones en lenguaje máquina. 3. Utilizan notaciones cercanas a las habituales, con sentencias y frases semejantes al lenguaje matemático o al lenguaje natural. Programar no es tan solo conocer un lenguaje de programación. Para trabajar correctamente con un lenguaje de programación, es necesario conocer su sintaxis, sus palabras y las reglas para la construcción de nuevas palabras. Pero además son necesarias unas nociones sobre algoritmos. No basta saber decir; también es necesario saber qué decir. Por ejemplo, conocer métodos de cálculo, o de búsqueda, o de ordenación. La creación de un programa no es una tarea lineal. Primero se debe definir el problema que se desea solventar y diseñar una solución. No existe habitualmente una única solución, y muchas veces tampoco se puede destacar a una como mejor que las demás. Además, la solución adoptada debe ser eficiente, que sepa hacer buen uso de los recursos de los que se dispone. Hay muchos problemas para los que aún no se ha obtenido una solución aceptable. Uno de esos recursos es el tiempo: no todas las soluciones son igualmente rápidas. Y los programas necesitan con mucha frecuencia modificaciones en sus instrucciones o en las definiciones de sus datos. Los problemas evolucionan y sus soluciones también. Poco a poco mejora la comprensión de los problemas que se abordan, y por tanto soluciones antes adoptadas necesitan pequeñas o no tan pequeñas modificaciones. Antes de presentar el lenguaje C, convendrá tratar de algunos conceptos necesarios para comprender luego mejor las características que presenta el lenguaje C. Estos conceptos serán el de abstracción, el de modularidad, y el de programación estructurada.
Abstracción La abstracción es la capacidad de identificar los elementos más significativos de un sistema que se está estudiando, y las relaciones entre esos elementos: permite separar lo esencial de lo accesorio. La correcta abstracción del sistema que se aborda capacita para la construcción de modelos que permiten luego comprender la estructura del sistema estudiado y su comportamiento. La correcta abstracción, si de verdad está bien hecha, permite centrar el trabajo en los aspectos esenciales de los problemas que se deben abordar. La abstracción es un paso previo en la construcción de cualquier programa. Fundamentalmente hablaremos de dos formas de abstracción: 1. Por un lado se deben determinar los tipos de datos que interviene en el sistema, es decir, cuál es el conjunto de parámetros que definen su estado en todo momento y su rango de valores posibles, y las operaciones que pueden realizarse con esos valores. También interesa determinar cuáles son los datos iniciales y los resultados finales que resumen los estados inicial y final del sistema. 2. Por otro lado, se debe también determinar las funciones o procedimientos del sistema. Los procedimientos que definen su comportamiento.
Modularidad La modularidad es la capacidad de dividir el sistema sobre el que estamos trabajando en sus correspondientes partes diferenciadas (módulos), cada una de ellas con sus propias responsabilidades y subtareas. En una buena modulación de un sistema, para cada uno de los módulos debe quedar bien definidas sus relaciones con todos los demás módulos, su modo de comunicación con todo el resto del sistema. La modularidad permite convertir un problema en un conjunto de problemas menores, más fáciles de abordar. Así se logra la división del trabajo entre programadores o equipos de programadores, se aumenta la claridad del software que se desarrolla y se favorece la reutilización de parte del software desarrollado para problemas distintos para los que pudiera haber algún módulo semejante a los ya desarrollados. Además, en muchas ocasiones, este modo de trabajar reduce los costes de desarrollo del software y de su posterior mantenimiento. Esta tarea, tan ventajosa, no es en absoluto trivial. Determinar los correctos módulos que esquematizan el funcionamiento del sistema y lograr definir sus relaciones entre todos ellos puede llegar a ser muy complicado. Los módulos que se obtienen deben gozar de algunas propiedades, necesarias si se desea que la modularización resulte finalmente útil: 1. Independencia funcional. Es decir, cada módulo definido debe realizar una función concreta o un conjunto de funciones afines (alta cohesión), sin apenas ninguna relación con el resto (bajo acoplamiento). 2. Comprensibilidad. Es decir, cada módulo debe ser comprensible de forma aislada. Para lograr eso, desde luego se requiere la independencia funcional; y también establecer correctamente las relaciones entre cada módulo definido y los restantes. 3. Adaptabilidad. Es decir, los módulos se deben definir de manera que permitan posteriores modificaciones o extensiones, realizadas tanto en ese módulo como en los módulos con los que se relaciona.
Programación estructurada Existen distintas formas de abordar un problema y darle solución mediante un programa informático, distintas enfoques que se pueden adoptar para resolver un problema de programación. A cada enfoque, a cada forma de actuar, se le llama paradigma. El paradigma de programación que asume el lenguaje C es el de la programación estructurada. Viene bien aclarar esto antes de comenzar a tratar sobre el lenguaje C. Conviene tener en cuenta que este lenguaje está diseñado para trabajar en programación estructurada, y ese paradigma de programación es bueno y ha dado muy buenos resultados; pero no es el único, ni el mejor. Hay problemas que se resuelven mejor empleando otros paradigmas de programación: por ejemplo, la programación orientada a objetos. Otros muchos se resuelven mejor, efectivamente, con la programación estructurada. La programación estructurada establece las siguientes reglas: 1. Todo programa consiste en una serie de acciones o sentencias que se ejecutan en secuencia, una detrás de otra. 2. Cualquier acción puede ser sustituida por dos o más acciones en secuencia. Esta regla se conoce como la de apilamiento. 3. Cualquier acción puede ser sustituida por cualquier estructura de control; y sólo se consideran tres estructuras de control: la secuencia, la selección y la repetición. Esta regla se conoce como regla de anidamiento. Todas las estructuras de control de la programación estructurada tienen un solo punto de entrada y un solo punto de salida. 4. Las reglas de apilamiento y de anidamiento pueden aplicarse tantas veces como se desee y en cualquier orden. Es importante comprender bien el paradigma y sus reglas. Aprender a programar en C es aprender un lenguaje que trabaja desde esa perspectiva. Poco a poco veremos cómo se crean las sentencias, como se define una estructura de control condicional, o de repetición, etc. Todos los conceptos aquí presentados adquirirán mayor claridad a medida que nos adentremos en la programación.
Lenguaje C. El lenguaje C se diseñó en 1972 (Dennis Ritchie). Más tarde, en 1983, se definió el estándar ANSI C (que es el que aquí presentaremos). Ya ha quedado dicho que el lenguaje C está creado para la programación estructurada. El lenguaje C tiene muy pocas reglas sintácticas, sencillas de aprender. Su léxico es muy reducido: tan solo 32 palabras. A menudo se le llama lenguaje de medio nivel, más próximo al código máquina que muchos lenguajes de más alto nivel. Y por eso mismo es un lenguaje muy eficiente. Permite el uso del lenguaje ensamblador en partes del código, trabaja a nivel de bit, y permite modificar los datos con operadores que manipulan bit a bit la información. También se puede acceder a las diferentes posiciones de memoria conociendo su dirección. El lenguaje C permite con facilidad la programación modular, creando unidades independientes que pueden compilarse de forma independiente, que pueden posteriormente enlazarse. Así, se crean funciones o procedimientos, que se pueden compilar y almacenar, creando así bibliotecas de código ya editado y compilado que resuelve distintas operaciones. Cada programador puede diseñar sus propias bibliotecas, que simplifican luego considerablemente el trabajo futuro. El ANSI C posee una amplia colección de bibliotecas de funciones estándar y normalizadas.
Entorno de programación. Un entorno de programación incluye todas las distintas herramientas de programación necesarias para trabajar con un lenguaje de programación en particular. El entorno de C incluye los siguientes componentes: 1. Editor: Que permite introducir y modificar el código fuente C. 2. Compilador: Un programa que convierte el programa escrito en C en un código que comprende el ordenador (código máquina). 3. Archivos de biblioteca: Archivos que contienen, ya perfectamente definidas, una serie de funciones específicas. Estos archivos ayudan al programador a desarrollar programas en C. El programador puede crear también sus propios archivos de biblioteca. 4. Enlazador o “linkador”: Permite utilizar funciones ya definidas en distintas bibliotecas de forma que, de un programa editado y de una serie de funciones ya compiladas, se obtiene, al compilar, un ejecutable final. El programa editado en C se llama fichero fuente. El archivo donde se almacena ese código tendrá un nombre (el que se quiera) y la extensión .cpp, o .c. Un programa no es más que archivo de texto. Al compilar, del fichero fuente se llega al código máquina, con el mismo nombre que el archivo donde está el código fuente, y con la extensión .obj. Casi con toda probabilidad en código fuente hará uso de funciones que están ya definidas y precompiladas en las bibliotecas. Ese código precompilado está en archivos con la extensión .lib. Desde luego, para hacer uso de una función predefinida, es necesario conocer su existencia y tener localizada la biblioteca donde está precompilada; eso es parte del aprendizaje de un lenguaje de programación, aunque también se disponen de grandes índices de funciones, de fácil acceso para su consulta. Con el archivo .obj y los necesarios .lib que se deseen emplear, se procede al linkado que genera un fichero ejecutable con la extensión .exe.
Estructura básica de un programa en C. Aquí viene escrito un sencillo programa en C #include <stdio.h> /* Este es un programa en C. */ // Imprime un mensaje en la pantalla del ordenador main() { printf(“mi primer programa en C”); } Todos los programas en C deben tener ciertos componentes fijos. Vamos a ver los que se han empleado en este primer programa: 1. #include <stdio.h>: Con esta línea de código se indica al compilador que se desea emplear, en el programa redactado, alguna función que está definida en la biblioteca stdio.h. Esta biblioteca contiene una colección de programas de entrada y salida por consola (pantalla y teclado). Esta instrucción nos permite utilizar cualquiera de las funciones incluidas en esa biblioteca. La sentencia comentada recoge el nombre del archivo stdio.h, donde están recogidos todos los prototipos de las funciones de entrada y salida estándar. Todo archivo de biblioteca contiene identificadores, constantes, variables globales, macros, prototipos de funciones, etc. Toda sentencia que comience por # se llama directiva de preprocesador. A lo largo del libro se irán viendo diferentes directivas. 2. main: Es el nombre de una función. Es la función principal y establece el punto donde comienza la ejecución del programa. La función main es necesaria en cualquier programa de C que desee ejecutar instrucciones. Un código será ejecutable si y sólo si dispone de la función main. 3. (): Debe aparecer inmediatamente detrás de main. Los paréntesis se encuentran siempre después de un identificador de función. Entre ellos se recogen los parámetros que se pasan a la función al ser llamada. En este caso, no se recoge ningún parámetro, y los paréntesis vienen vacíos. 4. /* comentarios */: Símbolos opcionales. Todo lo que se encuentre entre estos dos símbolos son comentarios al programa fuente y no serán leídos por el compilador. Los comentarios no se compilan, y por tanto no son parte del programa; pero son muy necesarios para lograr unos códigos inteligibles, fácilmente interpretables tiempo después de que hayan sido redactados y compilados. Es muy conveniente, cuando se realizan tareas de programación, insertar comentarios con frecuencia, que vayan explicando el proceso que se está llevando en cada momento. Un programa bien documentado es un programa que luego se podrá entender con facilidad y será, por tanto, más fácilmente modificado y mejorado. También se pueden incluir comentarios precediéndolos de la doble barra //. En ese caso, el compilador no toma en consideración lo que esté escrito desde la doble barra hasta el final de la presente línea. 5. ;: Toda sentencia en C termina con el punto y coma. En C, se entiende por sentencia todo lo que tenga, al final, un punto y coma. 6. {}: Indican el principio y el final de todo bloque de programa. Cualquier conjunto de sentencias que se deseen agrupar, para formar entre ellas una sentencia compuesta o bloque, irán marcadas por un par de llaves: una antes de la primera sentencia a agrupar; la otra, de cierre, después de la última sentencia. Una función es un bloque de programa y debe, por tanto, llevarlas a su inicio y a su fin.
Elementos léxicos Entendemos por elemento léxico cualquier palabra válida en el lenguaje C. Serán elementos léxicos, o palabras válidas, todas aquellas palabras que formen parte de las palabras reservadas del lenguaje, y todas aquellas palabras que necesitemos generar para la redacción del programa, de acuerdo con una normativa sencilla. Llamaremos identificador a cualquier secuencia de una o más letras, dígitos o símbolo subrayado, que pueda ser usada como un nombre dentro de un programa. Un identificador es cualquier palabra válida en C. Con ellos podemos dar nombre a variables (espacios de memoria creados para almacenar determinada información o dato), constantes, tipos de dato, nombres de funciones o procedimientos, etc. (ya iremos viendo todos esos conceptos a lo largo de estas páginas). También las palabras propias del lenguaje C son identificadores; estas palabras se llaman palabras clave o palabras reservadas. Las reglas básicas para la creación de un identificador en el lenguaje C son: 1. Debe comenzar por una letra del alfabeto o por el carácter subrayado. Los demás caracteres pueden ser letras, dígitos o el carácter subrayado. Así pues, un identificador no puede comenzar por un dígito. 2. El compilador sólo reconoce los primeros 32 caracteres de un identificador, pero éste puede tener cualquier otro tamaño mayor. Aunque no es nada habitual generar identificadores tan largos, si alguna vez así se hace hay que evitar que dos de ellos tengan iguales los 32 primeros caracteres, porque entonces para el compilador ambos identificadores serán el mismo. 3. Las letras de los identificadores pueden ser mayúsculas y minúsculas. El compilador distingue entre unas y otras, y dos identificadores que se lean igual y que se diferencien únicamente en que una de sus letras es mayúscula en uno y minúscula en otro, son distintos. 4. Un identificador no puede deletrearse igual y tener el mismo tipo de letra (mayúscula o minúscula) que una palabra reservada o que una función definida en una librería que se haya incluido en el programa mediante una sentencia include. Las palabras reservadas, o palabras clave, son identificadores predefinidos que tienen un significado especial para el compilador de C. Sólo se pueden usar en la forma en que han sido definidos. El conjunto de palabras clave o reservadas (que siempre van en minúscula) en ANSI C es muy reducido (un total de 32) y son las siguientes:
A lo largo del manual iremos viendo la utilidad de cada una de estas palabras.
Sentencias simples y sentencias compuestas Una sentencia simple es cualquier expresión válida en la sintaxis de C que termine con el carácter de punto y coma. Y una sentencia compuesta es una sentencia formada por una o varias sentencias simples. La sentencia simple queda definida cuando el programador termina una expresión válida en C, con un punto y coma. La sentencia compuesta se inicia con una llave de apertura ({) y se termina con una llave de clausura (}).
Errores y depuración No es extraño que, al terminar de redactar el código de un programa, al iniciar la compilación, el compilador deba abortar su proceso y avisar de que existen errores. El compilador ofrece algunos mensajes que clarifican frecuentemente el motivo del error, y la corrección de esos errores no comporta habitualmente demasiada dificultad. A esos errores sintácticos los llamamos errores de compilación. Ejemplo de estos errores pueden ser que se haya olvidado terminar una sentencia con el punto y coma, o que falte una llave de cierre de bloque de sentencias compuestas, o sobre un paréntesis, o se emplee un identificador mal construido… Otras veces, el compilador no haya error sintáctico alguno, y compila correctamente el programa, pero luego, en la ejecución, se producen errores que acaban por abortar el proceso. A esos errores los llamamos errores de ejecución. Un clásico ejemplo de este tipo de errores es forzar al ordenador a realizar una división por cero, o acceder a un espacio de memoria para el que no estamos autorizados. Esos errores también suelen ser sencillos de encontrar, aunque a veces, como no son debidos a fallos sintácticos ni de codificación del programa sino que pueden estar ocasionados por el valor que en un momento concreto adquiera una variable, no siempre son fácilmente identificables, y en esos casos puede ser necesario utilizar los depuradores que muchos entornos de programación ofrecen. Y puede ocurrir también que el código no tenga errores sintácticos, y por tanto el compilador termine su tarea y genere un ejecutable; que el programa se ejecute sin contratiempo alguno, porque en ningún caso se llega a un error de ejecución; pero que el resultado final no sea el esperado. Todo está correctamente escrito, sin errores de compilación ni de ejecución, pero hay errores en el algoritmo que pretende resolver el problema que nos ocupa. Esos errores pueden ser ocasionados sencillamente por una errata a la hora de escribir el código, que no genera un error sintáctico, ni aborta la ejecución: por ejemplo, teclear indebidamente el operador suma (+) cuando el que correspondía era el operador resta (-). Todo error requiere una modificación del programa, y una nueva compilación. No todos los errores aparecen de inmediato, y no es extraño que surjan después de muchas ejecuciones.
|
||||||||||||||||||||||||||||||||
|
Otras publicaciones o páginas sobre el lenguaje C: |
|||||||||||||||||||||||||||||||||
| Comentarios o preguntas a: pedro.alcover@upct.es |