I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
FUNDAMENTOS DE PROGRAMACIÓN Tema 3
El C como lenguaje estructurado
1º Administración de Sistemas Informáticos I.E.S. Francisco Romero Vargas Departamento de Informática __________________________________________________________________________________________________________ El C como lenguaje estructurado 0
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
1. HISTORIA DEL C. Antecedentes del lenguaje C.
FORTRAN. A principios de la década de los 50 la mayoría de los programas informáticos todavía se escribían en ensamblador, con los inconvenientes que ello suponía en cuanto al esfuerzo a realizar, el tiempo a invertir, etc.. Evidentemente, esa forma de trabajar de los programadores repercutía negativamente en la economía de las empresas que los empleaban. Por este motivo, es decir, fundamentalmente por razones de carácter económico, a finales de 1953 un empleado de IBM propuso la idea de desarrollar un lenguaje de alto nivel, que se llamaría FORTRAN, de forma que se facilitaría el trabajo de los programadores y se incrementaría su productividad. Así pues, a principios de 1955, un grupo de trabajo se dedicó a diseñar e implementar un compilador de FORTRAN y lo terminó en 1957. Pretendía ser un lenguaje que se pudiera traducir eficazmente a lenguaje máquina y, además, fácil de usar. Su notación era similar a la empleada en matemáticas. Su potencia en los cálculos matemáticos era significativa. Este lenguaje serviría principalmente para desarrollar aplicaciones técnicas y científicas. La versión FORTRAN-66 consiguió ser independiente de la máquina, lo que hizo a este lenguaje más portable. La versión FORTRAN-77 le añade instrucciones para posibilitar la programación estructurada, el manejo de archivos y el tratamiento de cadenas de caracteres. La última versión FORTRAN-90 incluye características como recursividad, manejo de memoria dinámica, etc. En resumen, podemos considerar al FORTRAN como el más antiguo de los lenguajes de alto nivel.
ALGOL El ALGOL (Algorithmic Languaje -lenguaje algorítmico-) se desarrolló entre 1957 y 1962 con la idea de ser un “lenguaje de programación universal”, de propósito más general que el FORTRAN y de ser independiente de la máquina. Fue auspiciado por la americana Association for Computing Machinery (ACM) y por su equivalente europea, la GAMM. Con el tiempo fue adquiriendo una tendencia algebraica, orientándose hacia las aplicaciones científicas y de ingeniería. Es un lenguaje de estructura clara que ha tenido gran influencia en el desarrollo de otros lenguajes de alto nivel (Pascal y ADA) y en la arquitectura de muchos ordenadores. El lenguaje C. Basándose en el lenguaje Algol, en 1967, Martin Richards inventó un nuevo lenguaje llamado BCPL. Por otro lado, el M.I.T. (Instituto de Tecnología de Massachussets) y las compañías AT&T y General Electrics estaban desarrollando lo que pretendían que fuera un sistema operativo multiusuario capaz de dar servicio a una ciudad con un solo ordenador (!?). Cuando vieron que el proyecto era demasiado ambicioso dos de ellas abandonaron, y sólo en el M.I.T. siguieron trabajando en el proyecto. Al final consiguieron construir el sistema operativo, pero más modesto que el que en un principio pretendían. Se llamó MULTICS (MULTiplexed Information Computing System). __________________________________________________________________________________________________________ El C como lenguaje estructurado 1
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Uno de los que trabajaron en el proyecto, Ken Thompson, de los Laboratorios Bell de AT&T, cogió un viejo mini-ordenador DEC PDP-7 que no se utilizaba y, con el propósito de construir un juego espacial, se fabricó una versión de MULTICS para un solo usuario. La escribió en ensamblador y, bromeando, le pusieron de nombre UNICS, por contraposición a MULTICS. Otros colegas se sumaron a la idea de Thompson. Éste, ya en 1970, modificó el BCPL con la idea de volver a escribir UNICS en un lenguaje de más alto nivel. El nuevo lenguaje, por ser un «hijo» del BCPL, se llamó con su inicial: B. Con la excusa de construir un procesador de texto convencieron a su compañía de la compra de una DEC PDP-11 y Thompson escribió mientras tanto un compilador de FORTRAN en B; sin embargo, no pudo rescribir el UNICS en B. Entonces, su colega Dennis Ritchie lo modificó añadiéndole tipos de datos y otras herramientas de las que adolecía el lenguaje, y tras otro intento más, consiguieron escribir el sistema UNICS, que ya se llamó UNIX y además, era multitarea. Como era una derivación del B, le llamaron con la siguiente letra, tanto del alfabeto como del antecesor BCPL, es decir: C. Resumen cronológico Año
Autor
Desarrollo
1962
ALGOL
1967
Jonh Backus, Peter Naur,... Martin Richards
1970
Ken Thompson
1971
Dennis Ritchie
Escribe versión de MULTICS en ensamblador UNICS modifica BCPL lenguaje B (no consigue escribir UNICS en lenguaje B) Amplía y modifica el B lenguaje C
Lenguaje BCPL
Escribe el UNICS en C UNIX 1972 1984 1989
Ken Thompson Dennis Ritchie Brian Kernighan Bjarne Stroustrup
1º versión definitiva de C Lenguaje C++ Estándar ANSI C y el ISO C
En 1972, el lenguaje C modificado un poco más por Ritchie, Thompson y Brian Kernighan, ya era lo suficientemente estable y maduro como para que pudiera distribuirse junto con el sistema operativo UNIX. En 1973 la versión 5 se distribuyó gratuitamente por las universidades americanas, y no es de extrañar el éxito que obtuvo, pues incluía un compilador de C, el código fuente, el procesador de texto de alta calidad (troff) y muchos programas de utilidad. A partir de ahí, la prosperidad del lenguaje C corrió paralela al del UNIX. En 1978 Kernighan y Ritchie escribieron el libro "The C Programming Language", que se convirtió en el documento de referencia del lenguaje. Tras una serie de añadidos __________________________________________________________________________________________________________ El C como lenguaje estructurado 2
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
finales al lenguaje ese mismo año, que se recogieron en un anexo del libro, el C se fue haciendo famoso día a día. Se escribieron compiladores para prácticamente todas las plataformas. Con el tiempo, los fabricantes de compiladores empezaron a crear extensiones del lenguaje, y algunos programadores empezaron a crear variantes, es decir, lenguajes nuevos aplicados a un propósito específico pero basados en el C. El más famoso de todos fue, y sigue siendo sin duda, el que Bjarne Stroustrup inventó en 1984, llamado C++. Estandarización del lenguaje C. En 1985 se vio claro que había que unificar criterios. Por eso el organismo americano de estándares (ANSI) creó un comité llamado X3PJ11 para normalizar el lenguaje. En 1989 el trabajo ya estaba hecho. No obstante, el organismo internacional de normalización ISO también quiso darle carácter mundial al lenguaje C y creó otro comité. Ya que el estudio estaba casi hecho no era cuestión de acabar teniendo dos estándares. Simplemente, el organismo ISO copió la norma elaborada por ANSI, pero se quejaron de que era demasiado «americano» (evidente, puesto que ANSI es American National Standards Institute) y entonces empezaron a trabajar en la internacionalización del lenguaje. Se dice que el C es el último lenguaje de programación sin internacionalizar (se refieren al tradicional pre-ANSI) y también el primero que se internacionalizó. El mismo año 1989 ANSI acabó el trabajo; al año siguiente ISO acabó de redactar su documento. Los dos son iguales; sólo varía el estilo de la redacción. Así, podemos hablar de C ANSI o de C ISO indistintamente. El libro de Kernighan y Ritchie [Kern87] fue revisado y salió una segunda edición un poco antes del estándar definitivo que recoge casi todas las novedades y cambios del lenguaje. Sea como fuere, se hizo un buen trabajo. No sólo se normalizó y reguló el lenguaje sino también una biblioteca auxiliar de funciones que le proporcionan al C todo lo que de por sí le falta. Entre estas funciones están las de internacionalización. Mediante ellas es posible que un programa se comporte de forma diferente según el entorno cultural donde se ejecute. Por ejemplo, mientras que en España o en México una función que devolviera el nombre del día de la semana daría Domingo, en Gran Bretaña o EE.UU. daría Sunday; en cambio, un número real se imprimiría en España como 12,34 mientras que en México lo haría como 12.34. En septiembre de 1994 se introdujo un anexo que incluía algunas ampliaciones al sistema de internacionalización: algunas macros y funciones adicionales, y los ficheros de cabecera , <wctype.h> y <wchar.h>. Uno de los puntos de la norma dice que cada cinco años el comité debe volver a reunirse para revisarla; en efecto, en 1995 se empezó a trabajar en una posible revisión o modificación y se hicieron algunas propuestas.
2. VENTAJAS DEL C. Enorme flexibilidad y adaptabilidad.
__________________________________________________________________________________________________________ El C como lenguaje estructurado 3
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
El lenguaje es en sí muy pequeño y versátil, sólo tiene 32 palabras reservadas; gracias a funciones externas, agrupadas en forma de bibliotecas, se puede adaptar a casi cualquier tarea. Compiladores pequeños y fáciles de transportar. En efecto, al ser el lenguaje muy pequeño y conciso, el compilador también lo era. Además los primeros compiladores estaban pensados para trabajar muy rápidamente; no hacían ningún tipo de comprobación sobre el código fuente, dejándole toda la responsabilidad al programador. Además es fácil escribir en C programas transportables, y esto incluye al propio compilador. Concisión en la sintaxis. A veces se abusa de ello y se llega a perder claridad. C permite poner en un par de líneas lo que en otros lenguajes parecidos se pondría en una docena. Hecho por y para programadores. Como ya se ha dicho el propósito de C fue construir el sistema operativo UNIX y sus utilidades. Fue hecho por un pequeño número de personas casi por afición, no por una compañía comercial con cientos de programadores. Las personas que lo hicieron tenían las ideas muy claras. Éxito de UNIX, distribución gratuita en ambientes universitarios. Como también se ha dicho, AT&T tuvo la brillante idea de distribuir gratuitamente en universidades el UNIX incluyendo todo el código fuente y el compilador de C (aún hoy con cada sistema UNIX se suele incluir un compilador de C gratis). Como UNIX estaba escrito en C, quien quería modificar el sistema tenía que aprender C. Además las universidades son un buen caldo de cultivo, pues los profesionales que salen de ellas quieren seguir trabajando en entornos que les son familiares. Eficiencia en la producción de código objeto. Muchas veces no merece la pena escribir algo en ensamblador pudiendo hacerse en C. Tradicionalmente el ensamblador generado por el compilador de C ha sido casi tan bueno como el que se pudiera escribir directamente, y por supuesto se gana siempre en claridad, lo que es muy importante a la hora de hacer modificaciones posteriores. Amplio juego de operadores. El lenguaje C tiene más operadores que la mayoría de lenguajes comparables, y son operadores útiles y concisos. Auge de los PCs y compiladores para DOS y Windows. El auge de la microinformática ha hecho que la producción de programas para entornos ofimáticos y caseros aumente extraordinariamente. Esto ha hecho que muchas __________________________________________________________________________________________________________ El C como lenguaje estructurado 4
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
compañías vendieran compiladores de C que, además, incluían un atractivo entorno de desarrollo integrado.
3. DESVENTAJAS DEL C. No se comprueban los límites de los vectores. La implementación de los vectores es a bajo nivel, como otros aspectos, y el C no comprueba en absoluto si se sobrepasan los límites en tiempo de ejecución, por lo que un programa puede referenciar una zona que no tiene reservada, sin que el compilador proteste. Normalmente esto provocará un error y el programa no funcionará, pero dependiendo del sistema concreto con el que se trabaje puede que a veces si funcione, lo cual puede ser peor. La no comprobación de límites era antes una virtud, pues se buscaba que el compilador fuera sencillo de construir y rápido al trabajar, y se dejaba la responsabilidad a los programadores de C que se suponía serían expertos y profesionales. Hoy día las máquinas son mucho más rápidas que en aquella época. La precedencia de los operadores no es totalmente intuitiva. En caso de duda, consulte la tabla de precedencia de operadores; si no la tiene a mano, ponga paréntesis. La sintaxis puede llegar a ser demasiado concisa. Enseñarle el listado de un programa medianamente complejo a un profano es desilusionarlo: sólo verá símbolos extraños. Sin embargo, si el programa está bien escrito, cualquiera con experiencia lo entenderá todo perfectamente; pero es posible abusar de la concisión que nos proporciona y dejar el programa completamente ilegible. Incluso, existe un programa que sirve para ofuscar código fuente en C; esto es oscurecer o liar el código de tal forma que no se entienda nada pero compile perfectamente. Se emplea en los casos en que haya que distribuir el código fuente de un programa sin compilar para que el usuario pueda cambiar ciertos parámetros antes, pero sin que pueda modificar dicho código o apropiarse de algún algoritmo. Por otra parte, existen concursos de C ofuscado donde se premia el código fuente más original, sin que importe mucho lo que haga el programa. Existen algunos que se listan a sí mismos, uno que calcula el número PI con muchos decimales y ocupa dos líneas, etc. También se dan premios al programa más feo, o al menos estructurado (con más gotos), o al que se presenta formando un dibujo, etc
4. CARACTERÍSTICAS DEL C. El C es un lenguaje de nivel medio. Esto no significa que sea menos potente, más difícil de utilizar o menos desarrollado que un lenguaje de alto nivel como puede ser BASIC o Pascal; tampoco implica que C sea parecido al lenguaje ensamblador y por tanto presente sus problemas asociados. Por el contrario, manifiesta que el C combina elementos de los lenguajes de alto nivel con la funcionalidad del lenguaje ensamblador; por ejemplo, permite la __________________________________________________________________________________________________________ El C como lenguaje estructurado 5
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
manipulación de bits, bytes y direcciones (elementos básicos con los que opera la computadora). Esto hace que se adapte al desarrollo de software base (programación del sistema), donde estas operaciones son habituales. El código C es muy portable. Portabilidad significa que es posible adaptar el software escrito para un tipo de computadora a otra. Como muestra, si un programa escrito para un Apple Macintosh se puede trasladar fácilmente a un IBM PC, se considera portable. No es un lenguaje de tipos fuertes. Todos los lenguajes de programación de alto nivel soportan el concepto de tipos de datos. Un tipo de datos define un conjunto de valores que puede almacenar una variable junto con un conjunto de operaciones que se pueden realizar sobre esta variable. Aunque C incorpora cinco tipos de datos básicos (entero, real -simple y doble precisión-, carácter y vacío), no se trata de un lenguaje de tipos fuertes (como Pascal o Ada) en los que los datos utilizados deben tener sus tipos declarados explícitamente y el lenguaje limita la mezcla de tipos en las expresiones. La ventaja de los lenguajes de tipos fuertes es que se gasta menos esfuerzo en la depuración del programa, ya que el compilador detecta muchos de los errores causados por la utilización indebida de los tipos de datos. Así pues, C permitirá casi todas las mezclas de tipos. Por ejemplo, los tipos entero y carácter se pueden entremezclar libremente en la mayoría de las expresiones. El C tiene pocas palabras reservadas. Sólo tiene 32 palabras reservadas (27 por parte del estándar de Kemighan y Ritchie y 5 incorporadas por el comité de estandarización ANSI), que son las órdenes que constituyen el lenguaje C. Los lenguajes de alto nivel normalmente incluyen algunas más. El C es un lenguaje estructurado. Aunque el término lenguaje estructurado en bloques no es aplicable estrictamente al lenguaje C, normalmente se considera un lenguaje estructurado por las similitudes en su estructura con ALGOL, Pascal y Modula-2. Técnicamente, un lenguaje estructurado en bloques permite que los procedimientos y funciones se declaren dentro de otros procedimientos o funciones. De esta forma, los conceptos de «global» y «local» se extienden mediante la utilización de reglas de ámbito adicionales, que establecen la «visibilidad» de una variable o procedimiento. C no se puede considerar realmente estructurado en bloques, puesto que no permite la creación de funciones dentro de funciones. La característica que distingue a un lenguaje estructurado es la compartimentalización del código y los datos, o sea, la capacidad de un lenguaje de dividir y ocultar del resto del programa toda la información e instrucciones necesarias para llevar a cabo una determinada tarea. Una forma de llevarla a cabo es utilizar subrutinas que empleen variables locales (temporales), mediante las cuales el programador puede conseguir que los eventos que se producen dentro de las mismas no causen efectos colaterales en otras partes del programa. Esta capacidad hace que sea __________________________________________________________________________________________________________ El C como lenguaje estructurado 6
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
muy fácil que programas en C compartan secciones de código. Si se desarrollan funciones compartimentalizadas, sólo se necesitará conocer qué es lo que hace una función y no cómo lo hace. Recuérdese que el uso excesivo de variables globales (variables con ámbito en todo el programa) puede hacer que los errores proliferen en el programa, al permitir los efectos colaterales no deseados. Un lenguaje estructurado permite muchas posibilidades en programación: Soporta directamente distintas construcciones de bucles, como pueden ser while, dowhile y for; la utilización de goto está totalmente prohibida o desaprobada; permite sangrar instrucciones, etc. Los lenguajes estructurados son más recientes que los no estructurados (COBOL, BASIC, etc.). Hoy en día se ha aceptado ampliamente que la claridad de los lenguajes estructurados facilita la programación y el mantenimiento. Realmente, muy pocos programadores podrían considerar seriamente la posibilidad de utilizar un lenguaje no estructurado para nuevos desarrollos de software. El componente principal de la estructura de C es la función (una subrutina independiente de C). En C las funciones son bloques constituyentes en donde tiene lugar toda la actividad del programa. Permiten que tareas distintas de un programa se definan y se codifiquen de forma separada, permitiendo así la modularidad de los programas. Después de crear una función, se puede utilizar de forma apropiada en varias situaciones, evitando la creación de efectos colaterales en otras partes del programa. El hecho de que se puedan crear funciones independientes es extremadamente crítico en grandes proyectos donde el código de un programador no debe afectar accidentalmente al de otro. Otra forma de estructurar y compartimentalizar código en C es utilizar bloques de código. Un bloque de código es un grupo de instrucciones de programa conectadas lógicamente que es tratado como una unidad. En C, un bloque de código se crea colocando una secuencia de instrucciones entre llaves. En este ejemplo, if (x<10) { printf(“Demasiado pequeño, inténtelo de nuevo.\n”); restablecer_contador(-1); } } las dos instrucciones que aparecen entre llaves tras el if se ejecutarán si x es menor que 10. Estas dos instrucciones, junto con las llaves, constituyen un bloque de código. Se trata de una unidad lógica: una de las instrucciones no se puede ejecutar sin la otra. Los bloques de código no solamente permiten implementar muchos algoritmos con claridad, elegancia y eficiencia sino que también ayudan al programador a asimilar la verdadera naturaleza de la rutina. El C es un lenguaje para programadores. Todos los lenguajes de programación no están hechos para los programadores. Por ejemplo, COBOL se diseñó para permitir a los no programadores comprender los programas de gestión (aplicaciones comerciales, orientadas a negocios); BASIC se creó esencialmente para introducir a los estudiantes en la programación; PASCAL se diseño para enseñar computación a los estudiantes universitarios; etc. Por contra, C fue creado, influenciado y probado por programadores profesionales. El resultado final es que C ofrece al programador lo que éste quiere: pocas restricciones, pocas pegas, estructuras __________________________________________________________________________________________________________ El C como lenguaje estructurado 7
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
de bloques, funciones independientes y un conjunto compacto de palabras reservadas. Es muy interesante que mediante la utilización de C, un programador pueda alcanzar casi la eficiencia del código ensamblador, combinada con la estructura de ALGOL o Modula-2. Por tanto no es de extrañar que C sea uno de los lenguajes más populares entre los programadores profesionales de élite. Ventajas respecto al lenguaje ensamblador. El hecho de que C se utilice a menudo en lugar del lenguaje ensamblador contribuye mucho a su popularidad entre los programadores. El lenguaje ensamblador utiliza una representación simbólica del código binario real que la computadora ejecuta. Cada operación en lenguaje ensamblador se corresponde con una tarea básica que la computadora ejecuta. Aunque el lenguaje ensamblador ofrece a los programadores la potencia de realizar las tareas con la mayor flexibilidad y eficiencia, es notoriamente difícil trabajar con él en el desarrollo y depuración de un programa. Más aún, como el lenguaje ensamblador no es estructurado, el programa final tiende a ser código «espagueti» (un complejo desorden de saltos, llamadas e indexaciones). Esta falta de estructuración provoca que los programas en lenguaje ensamblador sean difíciles de leer, mejorar y mantener. Quizás lo más importante sea que las rutinas del lenguaje ensamblador no son portables entre máquinas con unidades de procesamiento central diferentes. Inicialmente, C se utilizó para la programación de sistemas. Un programa de sistemas forma parte de una amplia clase de programas que constituyen el software base de una computadora o de sus utilidades de soporte. Por ejemplo, los siguientes se denominan normalmente programas de sistemas: Sistemas operativos, Intérpretes, Editores, Programas específicos, Compiladores, Administradores de bases de datos, etc. Portabilidad del C. Conforme C crecía en popularidad, muchos programadores comenzaban a utilizarlo para programar todas las tareas debido a su portabilidad y eficiencia. Dado que existen compiladores de C para la mayoría de las computadoras, es posible utilizar el código escrito para una máquina, y compilarlo y ejecutarlo en otra con pocos o ningún cambio. Esta portabilidad ahorra tanto tiempo como dinero. Además, los compiladores de C tienden a generar código objeto más compacto y rápido que la mayoría de los otros tipos de compiladores. Eficiencia del C. Quizás la razón más significativa por la que C se utiliza para todo tipo de tareas de programación es que a los programadores ¡les gusta!. C ofrece la velocidad del lenguaje ensamblador y la extensibilidad de FORTH, aunque tiene algunas de las restricciones de Pascal o Modula-2. Cada programador de C puede crear y mantener una biblioteca única de funciones que se ajuste a su personalidad y que puede utilizar en diferentes programas. Puesto que C permite (de hecho fomenta) la compilación separada, ofrece a los programadores poder gestionar proyectos con facilidad y minimizar la duplicidad de esfuerzos. Y, por supuesto, es el lenguaje sobre el que está desarrollado C++. Compiladores frente a intérpretes. __________________________________________________________________________________________________________ El C como lenguaje estructurado 8
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Los términos compilador e intérprete se refieren a la forma de ejecutar un programa. En teoría, cualquier lenguaje de programación puede ser compilado o interpretado, pero algunos se suelen ejecutar en una forma o en otra. Por ejemplo, normalmente, BASIC es interpretado y C es compilado. La forma de ejecutar un programa no viene definida por el lenguaje en el que se haya escrito. Los intérpretes y compiladores son simplemente programas sofisticados que trabajan sobre el código fuente del programa. Un intérprete lee el código fuente del programa línea a línea, realiza las instrucciones específicas contenidas en esa línea y, a continuación, pasa a la siguiente línea. Un compilador lee el programa entero y lo convierte en código objeto, que es una traducción del código fuente del programa a un formato que puede ejecutar directamente la computadora. El código objeto también se denomina código binario y código máquina. Una vez compilado un programa, una línea de código fuente deja de tener significado en la ejecución del programa. Cuando se utiliza un intérprete, el código fuente debería estar presente cada vez que se quiere ejecutar el programa. Por ejemplo, en BASIC tradicional se tiene que ejecutar primero el intérprete de BASIC y, a continuación, cargar el programa y escribir RUN cada vez que se desea utilizarlo. Luego, el intérprete de BASIC examina el programa línea a línea, para ver si es correcta, y a continuación la ejecuta. Este lento proceso se realiza cada vez que se ejecuta el programa. Por contra, un compilador convierte un programa a código objeto que puede ejecutar directamente la computadora. Debido a que el compilador traduce el programa una sola vez, lo único que hay que hacer es ejecutarlo directamente, normalmente mediante el simple proceso de escribir su nombre. Por tanto, la compilación sólo se realiza una sola vez, mientras que el código interpretado conlleva un costo de tiempo cada vez que se ejecuta el programa. Dos términos que aparecerán con frecuencia son tiempo de compilación, que se refiere a los eventos que ocurren durante el proceso de compilación, y tiempo de ejecución, que se refiere a los eventos que ocurren mientras se ejecuta el programa. Normalmente, estos términos se verán en las discusiones de errores, como pueden ser en las frases «errores en tiempo de compilación» y «errores en tiempo de ejecución».
5. FORMATO DE UN PROGRAMA EN C. Palabras reservadas. La siguiente tabla muestra las 32 palabras reservadas que, junto con la sintaxis formal de C, constituyen el lenguaje de programación C. auto break case char const continue default do double else enum
extern float for goto if int long register return short signed
sizeof static struct switch typedef union unsigned void volatile while
__________________________________________________________________________________________________________ El C como lenguaje estructurado 9
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Todas las palabras reservadas de C están en minúsculas. En C, las mayúsculas y minúsculas tienen un tratamiento diferente: else es una palabra reservada; ELSE no lo es. Una palabra reservada no se puede utilizar para cualquier otro propósito en un programa en C (es decir, no se puede utilizar como nombre de variable o de función). Funciones. Todos los programas en C están constituidos por una o más funciones, las cuales son los módulos básicos del programa y, al menos, una de ellas debe llamarse main() que significa "principal"-. Es decir, todo programa debe tener, al menos, una función denominada main(), y se trata de la primera que se llama cuando comienza la ejecución del programa. En un código C escrito correctamente, main() engloba el esbozo o esqueleto de lo que realiza el programa; dicho esqueleto está compuesto por las llamadas a las funciones. En definitiva, un programa en C siempre comienza ejecutándose por la función main(); las demás funciones son llamadas desde main(), y por tanto, se puede considerar que ésta es, por sí misma, el programa. Aunque técnicamente main() no forma parte del lenguaje C, se debe tratar como si lo fuera. Por ejemplo, no intente utilizar main como nombre de una variable. El formato general de un programa en C se muestra en la siguiente figura, donde f1() a fN() representan funciones definidas por el usuario y una de ellas debe ser main(). declaraciones globales tipo_devuelto main(lista de parámetros) { secuencia de instrucciones } tipo_devuelto f1 (lista de parámetros) { secuencia de instrucciones } tipo_devuelto f2 (lista de parámetros) { secuencia de instrucciones } ...................... tipo_devuelto fN (lista de parámetros) { secuencia de instrucciones }
Ejemplo: .............. void main(void) { int operando1, operando2; int resultado_suma, resultado_producto; .......... resultado_suma = suma(operando1, operando2); resultado_producto = producto(operando1, operando2); .............. } int suma (int op1, int op2) { return op1 + op2; } int producto (int op1, int op2) __________________________________________________________________________________________________________ El C como lenguaje estructurado 10
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
{ }
return op1 * op2;
El nombre de una función va seguido de unos paréntesis entre los cuales puede ir o no uno o más argumentos. Una función consta de un encabezamiento (en el que va incluido su nombre) y de un cuerpo, que está delimitado por llaves: { y } . El cuerpo consiste en una serie de sentencias, cada una de las cuales termina en punto y coma. Se hablará detenidamente de las funciones en el tema siguiente. Directivas del preprocesador. En el encabezamiento de los programas encontraremos instrucciones para el compilador de las que hablaremos ampliamente más adelante. Por ahora, nos fijaremos sólo en la directiva #include. Por ejemplo, la línea #include <stdio.h> le indica al compilador que incluya en el programa datos del fichero stdio.h el cual se suministra como parte del compilador y aporta información sobre aspectos relacionados con la E/S de datos : funciones como printf(), scanf(), getchar(), ... Es conveniente añadir siempre dicha línea pues no hay regla segura de cuando se necesitará esa información; además, el compilador sólo tomará del mencionado fichero la información que necesite y cualquier otra que no sea de utilidad no formará parte del programa y, por tanto, no hará que sea más largo innecesariamente. Sentencias. Las sentencias que componen una función no van numeradas y se ejecutan secuencialmente, de arriba a abajo, unas a continuación de otras, sin saltos hacia atrás. Para una buena legibilidad del programa conviene escribir cada sentencia en una línea. El símbolo punto y coma (;) del final de cada línea identifica a ésta como una sentencia C o instrucción. Hay que tener por ello en cuenta que el punto y coma es parte de la sentencia y no es un separador de sentencias, como ocurre en el lenguaje Pascal. No obstante, en cada línea se puede escribir más de una sentencia, o bien, se puede espaciar una sentencia en más de una línea (no se debe partir en mitad del texto entrecomillado); y será el compilador el que averigüe dónde termina una sentencia y comienza la siguiente por medio de los puntos y comas introducidos. Comentarios. Es interesante usar comentarios para hacer más comprensibles los programas. Se utilizan los símbolos /* y */ para delimitarlos, y todo el texto encerrado entre ellos será ignorado por el compilador. Ello posibilita cualquier disposición de los comentarios en el programa: por ejemplo, al final de una sentencia, o bien, ocupando más de una línea. Los comentarios no pueden anidarse. Un comentario estilo C++ comienza con los caracteres // y termina al final de la línea, es decir: estos comentarios no pueden ocupar más de una línea. En general, el estilo C se utiliza para comentarios de más de una línea, y el estilo C++, para comentarios de una sola línea. __________________________________________________________________________________________________________ El C como lenguaje estructurado 11
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
6.TIPOS DE DATOS. Existen cinco tipos de datos básicos en C: caracteres (char), enteros (int), punto flotante (float), doble punto flotante (double) y sin valor (void). -
Los valores del tipo char se utilizan para almacenar caracteres ASCII o cualquier cantidad de 8 bits. Las variables del tipo int se utilizan para almacenar cantidades enteras. Las variables del tipo float y double almacenan números reales. El tipo void se utiliza en tres casos: 1º. Para declarar explícitamente el tipo de una función que no devuelve ningún valor. 2º. Para declarar explícitamente una función que no tiene parámetros. 3º. Para crear punteros genéricos.
C soporta otros tipos distintos, incluyendo estructuras, uniones, campos de bits, enumeraciones y tipos definidos por el usuario que se explicarán más adelante. Modificadores de tipo. A excepción del tipo void, pueden añadirse distintos modificadores precediendo a los tipos de datos básicos. Un modificador se utiliza para alterar el significado del tipo básico para que se ajuste de manera más precisa a las necesidades de cada situación. Tales modificadores son: signed, unsigned, long y short. -
Todos ellos se pueden aplicar a los tipos enteros base. Se puede aplicar unsigned y signed a los caracteres. Se puede aplicar long al tipo double. La utilización de signed sobre enteros es redundante (pero permitida) debido a que la declaración por omisión del entero asume un número con signo. Lo mismo ocurre cuando se aplica al tipo char.
La tabla siguiente muestra todas las combinaciones permitidas que se ajustan al estándar ANSI de C para tamaños de palabra de 32 bits, junto con sus rangos y longitudes en bits, tal como se implementan en Borland C++. (Para tamaños de palabra de 16 bits, sólo cambia * de 32 a 16 bits). Tamaño en bits 8 16 32(*) 32 32 64 80
Tipo char = signed char unsigned char short int = signed short int unsigned short int int = signed int unsigned int long int = signed long int unsigned long int float double long double
Rango -128 a 127 0 a 255 -32768 a 32767 0 a 65535 -2147483648 a 2147483647 0 a 4294967295 -2147483648 a 2147483647 0 a 4294967295 3.4E-38 a 3.4E+38 1.7E-308 a 1.7E+308 3.4E-4932 a 1.1E+4932
__________________________________________________________________________________________________________ El C como lenguaje estructurado 12
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
7. DECLARACIÓN DE VARIABLES. La declaración de variables consiste en asociar un tipo de datos determinado a una variable. Todas las variables a usar en un programa se deben declarar antes de utilizarlas. A continuación, se muestra el formato general de una declaración: tipo lista_variables; Aquí, tipo debe ser un tipo de dato válido en C y lista_variables puede estar constituida por uno o más nombres de identificadores separados por comas. unsigned int anio_nacimiento, numero_socios; float importe, total_compra; double distancia_sol, peso; char letra, digito ; Es posible inicializar una variable (asignarle un valor inicial) en el momento de su declaración: para ello basta con poner un signo igual (=) a continuación del nombre de la misma, seguido de la constante correspondiente al tipo adecuado de ésta. int dia = 15; // declaración e inicialización short int coord_x =10, coord_y =12 ; char caracter = ‘z’; int blancos=11, negros=15, verdes=30; No conviene mezclar, en una misma línea, variables que sólo se declaran con otras que se inicializan.
8. CONSTANTES. Las constantes en C se refieren a valores fijos que no puede modificar el programa. Las constantes literales representan valores fijos que pueden ser de cualquier tipo de dato, como se muestra en la siguiente tabla. Tipo de dato char int long int short int unsigned int float double
Ejemplos ‘a’ ‘\n’ ‘9’ 974 21000 -345 20000L -15000L -100 120 20000U 50000U 475.231F 9.87e2 987.654 -0.00345
C soporta, además de las constantes correspondientes a los tipos de datos predefinidos, las constantes de tipo cadena que se encierran entre comillas dobles. No se deben confundir las cadenas de caracteres con las constantes de tipo carácter que se __________________________________________________________________________________________________________ El C como lenguaje estructurado 13
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
encierran entre comillas simples (apóstrofos). Debido a que las cadenas son simplemente vectores de caracteres, se tratarán más adelante. Encerrar todas las constantes de carácter entre apóstrofos funciona para la mayoría de los caracteres de impresión, pero algunos, como el retorno de carro, son imposibles de introducir desde el teclado. Por esta razón, C utiliza las constantes de carácter especiales de barra invertida -mostradas en la siguiente tabla- cada uno de las cuales equivale a un carácter único. Se puede utilizar un código de barra invertida exactamente de la misma forma que se haría con cualquier otro carácter; por ejemplo, se puede asignar a una variable de tipo char. Código \n \t \v \b \’ \r \” \f \0 \a \ooo \xhh \\
Significado nueva línea tabulador horizontal tabulador vertical Retroceso Apóstrofo retorno de carro Comillas salto de página carácter nulo Alarma constante octal (ooo = valor octal) constante hexadecimal (hh = valor hexadecimal) barra atrás
Las constantes con nombre se pueden declarar en C de dos maneras: -
Usando la directiva del preprocesador #define. Ej. #define PI 3.141592 En cuyo caso se estará indicando al compilador que antes de realizar la compilación sustituya dentro del programa fuente todas las apariciones de PI por el valor 3.141592
-
Anteponiendo la palabra reservada const delante de la declaración del objeto. Ej. const int PI=3.141592; En cuyo caso se indicará al compilador que PI es un objeto cuyo valor no puede ser modificado durante el programa. Cualquier intento de modificación del valor de PI provocaría un error de compilación.
Es costumbre entre los programadores de C declarar las constantes con nombre en mayúsculas.
__________________________________________________________________________________________________________ El C como lenguaje estructurado 14
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
9. OPERADORES. Una característica del lenguaje C es la cantidad de variedad de operadores que tiene, es decir, símbolos que operan sobre operandos (objetos) para producir los valores deseados. Cualquier combinación válida de operadores con los operandos necesarios constituye una expresión; una expresión completa que finaliza en un punto y coma es una sentencia y una serie de sentencias componen un programa. La importancia de operadores y expresiones es pues evidente; se encuentran entre las unidades de construcción fundamentales de un programa en C. Operadores aritméticos. En la siguiente tabla se muestra una lista de los operadores aritméticos permitidos en C. Operador + * / % -++
-
Acción Resta, también signo menos unario. Suma. Multiplicación División Módulo de la división. Decremento. Incremento.
Los operadores +, -, * y / funcionan de la misma forma en C que en la mayoría de los lenguajes de programación. Estos se pueden aplicar a casi cualquier tipo de dato predefinido de los que permite C. El menos unario, en realidad, multiplica su único operando por -1. Esto es, cualquier número precedido por un signo menos cambia su signo. Cuando se aplica / a un entero o a un carácter, se trunca el resto de la división, es decir, se obtiene la división entera; por ejemplo, la división entera de 10/3 es igual a 3. El operador módulo de la división % también funciona en C de la forma en que lo hace en otros lenguajes: la operación módulo de la división calcula el resto de una división entera. Sin embargo, % no se puede utilizar sobre los tipos float o double. El siguiente fragmento de código ilustra su uso: int x=10, y=3; printf(“%d %d \n”, x/y, x%y ); // mostrará 3 y 1 x=1; y=2; printf(“%d %d \n”, x/y, x%y ) ; // mostrará 0 y 1
-
C permite dos operadores muy útiles que, generalmente, no se encuentran en otros lenguajes de programación. Estos son los operadores de incremento (++) y decremento (--). El operador ++ suma 1 a su operando, y el operador -- le resta 1. Por tanto, las siguientes operaciones son equivalentes: variable = variable + 1; es equivalente a
++variable;
__________________________________________________________________________________________________________ El C como lenguaje estructurado 15
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
variable = variable - 1; es equivalente a
–-variable;
Los operadores de incremento y decremento pueden preceder (modalidad prefijo) o suceder (modalidad sufijo) al operando. Modalidad sufijo: Modalidad prefijo:
variable++ ++variable
variable---variable
Es indiferente escoger una modalidad u otra cuando la variable afectada de este operador va sola en una sentencia. ...
variable1 = variable2 = 0; variable1++; ++variable2; printf("%d %d",variable1,variable2); ...
// Salida: 1 1
Sin embargo, cuando el operador y su operando forman parte de una expresión mayor (por ejemplo, van en sentencias de asignación o en sentencias de salida por pantalla) existe una diferencia en utilizar la modalidad prefijo o la modalidad sufijo, que radica en el momento en que se realiza la operación de incremento o de decremento: - En el modo sufijo, primero se utiliza la variable afectada por el operador de incremento o decremento, y luego se incrementa o decrementa en 1 el valor de la variable. Ejemplo: la sentencia... q = 2 * a++; multiplica «a» por 2, asigna el resultado a «q» y luego incrementa «a» - En el modo prefijo, primero se incrementa o decrementa la variable afectada por el operador y luego se opera con ella. Ejemplo: la sentencia... q = 2 * ++a; incrementa «a» en 1, después multiplica su valor por 2 y luego asigna el resultado a «q». #include <stdio.h> /#include void main(void) { int a=1, b=1; int a_mas, mas_b; a_mas = a++; mas_b = ++b; printf(" a a_mas b mas_b\n"); printf("%5d%5d%5d%5d\n", a, a_mas, b, mas_b); //Salida: 2 1 2 2 getch(); }
- C tiene una taquigrafía especial que simplifica la codificación de un cierto tipo de instrucciones de asignación. En general, __________________________________________________________________________________________________________ El C como lenguaje estructurado 16
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
variable = variable + expresion; se puede escribir, en taquigrafía de C, como: variable += expresion; Esta taquigrafía funciona para todos los operadores binarios de C. Operadores relacionales y lógicos. A diferencia de otros lenguajes, una expresión de relación o una expresión lógica, si es cierta toma el valor 1, y si es falsa toma el valor 0. Por ejemplo, la expresión var_entera = ( 'b' > 'a' ); le está asignando 1 a la variable var_entera. La clave de los conceptos de operadores relacionales y lógicos es la idea de verdadero y falso. En C, verdadero es cualquier valor distinto de 0. Falso es 0. Las expresiones que utilizan los operadores relacionales y lógicos devolverán 0 para falso y 1 para verdadero. Operadores relacionales Operador
Acción
> >= < <= == !=
Mayor que Mayor o igual que. Menor que. Menor o igual que. Igual. Distinto. Operadores lógicos
Operador
Acción
&& || !
AND (Y) OR (O) NOT (NO)
Operadores a nivel de bit. Puesto que C se diseñó para sustituir al lenguaje ensamblador en la mayoría de las tareas de programación, soporta un completo complemento de operadores a nivel de bit. Estos son la comprobación, configuración o desplazamiento de los bits actuales de un byte o una palabra, que se corresponden con los tipos de datos char e int (NUNCA con los tipos float, double, long double y void). Operadores a nivel de bit Operador
Acción
__________________________________________________________________________________________________________ El C como lenguaje estructurado 17
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
& | ^ ˜ >> <<
Y O O exclusivo (XOR) Complemento a uno Desplazamiento a la derecha (divide por 2) Desplazamiento a la izquierda (multiplica por 2)
Ejemplos: x << 2; z=x & y; z=120^127; El operador en tiempo de compilación sizeof. sizeof es un operador unario en tiempo de compilación que devuelve la longitud, en bytes, de la variable o el especificador de tipo encerrado entre paréntesis al que precede. Por ejemplo: float f; printf(“%f”, sizeof f); // 4 printf(“%d”, sizeof(int));// 4 (suponiendo enteros de 32 bits)
La utilidad principal de sizeof es ayudara generar código portable cuando dicho código depende del tamaño de los tipos de datos incluidos en C. En C existen, además, otros operadores que iremos estudiando en apartados posteriores como ? y : (condicional), & y * (punteros) y -> y . (elementos de estructuras y uniones). Precedencia de los operadores. El compilador evalúa de izquierda a derecha los operadores con el mismo nivel de precedencia. Por supuesto, se pueden utilizar paréntesis para modificar el orden de evaluación. La precedencia de operadores usada en C atiende a la siguiente tabla. Denominación Expresión
Unitarios
Multiplicativos Aditivos
Operadores () [] . -> ˜ ! * & ++ -sizeof(tipo) * / % +
Función Paréntesis Corchetes de indexación Operador de estructura Operador de puntero a estructura Cambio de signo NOT binario NOT lógico Contenido de la dirección de un puntero Dirección de memoria de una variable Incremento Decremento Tamaño en tiempo de ejecución Multiplicación División Resto de la división Suma
__________________________________________________________________________________________________________ El C como lenguaje estructurado 18
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
De movimiento de bits >> << > Relacionales >= < <= == != & Binarios | ^ && Lógicos || ?: Condicionales Asignaciones abreviadas = *= /= %= += -= <<= >>= &= |= ^= , Otros
Resta Desplazmiento de bits a la derecha Desplazamiento de bits a la izquierda Mayor que Mayor o igual que Menor que Menor o igual que Igual que Distinto de AND binario OR binario XOR binario AND lógico OR lógico if..else abreviado
Operador coma
Los operadores relacionales y lógicos tienen menor precedencia que los operadores aritméticos. Este significa que una expresión como 10 > 1+12 se evalúa igual que si se hubiera escrito 10 > (1+12) . El resultado es, por supuesto, falso. Se pueden combinar varias operaciones en una expresión, como se muestra a continuación: 10>5&&!(10<9)113<=4 cuya evaluación será verdadera. Como sucede con las expresiones aritméticas, se puede hacer uso de los paréntesis para modificar el orden natural de evaluación de una expresión relacional o lógica. Por ejemplo: !1&&0 será falso puesto que primero se evalúa ! (NOT) y, a continuación, se evalúa && (AND). Sin embargo, cuando se utilizan paréntesis en la misma expresión: !(1&&0) , el resultado es verdadero. Recuérdese que todas las expresiones relacionales y lógicas generan un resultado de 0 ó 1. Por tanto, el siguiente fragmento de programa es totalmente correcto int x; x = 100; printf(“%d”, x>10); // muestra 1 Una ventaja del operador incremento es que genera un código compilado ligeramente más eficiente, ya que su estructura se asemeja más al código máquina real. Además, hay que tener en cuenta que x*y++ significa x*(y++) y no (x*y)++ lo cual carece de sentido. __________________________________________________________________________________________________________ El C como lenguaje estructurado 19
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
No se deben utilizar operadores de incremento o decremento en variables que se emplean más de una vez como argumento de una función o más de una vez en una misma expresión ya que el resultado depende de qué argumento (o término) evalúe primero el sistema. #include int num, a, b, c; void main(void) { // Expresiones no aconsejables: num = 5; printf ("%10d %10d\n", num, num*num++); num = 5; a = num/2 + 5*(1 + num++); printf ("%10d %10d\n", num, a); a=8; b=5; c = a * b / 5 + ( a * b++); printf ("%10d %10d %10d\n", c, b, a); a=8; b=5; c = (a * b++) + a * b / 5; printf ("%10d %10d %10d\n", c, b, a); a=8; b=5; c = a * b++ + ( a * b / 5); printf ("%10d %10d %10d\n", c, b, a); getch(); }
//
6
30
//
6
32
//
48
6
8
//
48
6
8
//
48
6
8
10. CADENAS DE CARACTERES. Una cadena de caracteres (string) consiste simplemente en una serie de uno o más caracteres. Ejemplos: "Esto es una tira" ,"x" , etc. Las comillas no forman parte de la cadena, sino que sirven para especificar su comienzo y su final. Las cadenas se declaran igual que los arrays unidimensionales (un array es una secuencia ordenada de datos de un determinado tipo): char nombre_cadena [longitud]; En C no existe el tipo cadena y por ello hay que declararla como un array de tipo char. Ello permite imaginar los caracteres de la cadena (mejor dicho, sus códigos) almacenados en posiciones de memoria adyacentes, cada una de las cuales puede almacenar un valor de tipo char. Ocupando la última posición del array el compilador del C (o el programador) coloca el carácter nulo \0 (código ASCII = 0) para indicar el final de la cadena de caracteres. Por esta razón, para declarar arrays de caracteres es necesario que la longitud del array tenga un carácter más que la cadena más larga que pueda almacenar. Hay que destacar que no es lo mismo, por ejemplo, el carácter 'x' que la cadena "x", puesto que el primero pertenece a un tipo básico char mientras que el segundo es un tipo derivado: un array de char y, además, contiene dos caracteres: 'x' y \0 . Para manejar tanto la entrada -scanf()- como la salida -prinft()- de la cadena se usa el especificador %s . La función strlen() nos informa de la longitud de una tira en caracteres, y si es un variable, deja de contar al llegar al carácter nulo, no importando si se ha declarado una longitud mayor. __________________________________________________________________________________________________________ El C como lenguaje estructurado 20
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Por su parte, el operador sizeof nos facilitará la longitud del array, o sea, el valor indicado en su declaración, o bien, en caso de definir una cadena (constante) en el encabezamiento, contará su longitud, incluyendo el carácter nulo. Las cadenas de caracteres se estudiarán a fondo en el tema “Estructuras estáticas”.
11. INSTRUCCIONES DE ASIGNACIÓN. Se utilizan las sentencias de asignación para dar un valor a las variables ya declaradas; posteriormente se les podrá dar, si es necesario, un valor diferente. El formato general de la instrucción de asignación es: nombre_variable = expresión; donde la expresión puede ser tan sencilla como una única constante o tan compleja como una combinación de variables, operadores y constantes. El C utiliza un único signo igual para indicar una asignación (a diferencia de Pascal o Modula-2, que utilizan el constructor :=). El destino, o la parte izquierda, de la asignación debe ser una variable, no una función o una constante. Además de mediante instrucciones de asignación, también se puede asignar un valor a una variable a través de las funciones de entrada de datos -por ejemplo, scanf()que permite introducir datos a través del teclado. Asignaciones múltiples. C permite asignar el mismo valor a varias variables utilizando asignaciones múltiples en una única instrucción. Por ejemplo, este fragmento de programa asigna a las variables coordx, coordy y coordz el valor 0: coordx = coordy = coordz = 0; En los programas profesionales, a las variables se les asignan frecuentemente valores comunes utilizando este método. Conversión de tipo en asignaciones. La conversión de tipo se refiere a la situación en la que variables de un tipo se mezclan con variables de otro tipo (véase el APÉNDICE de este capítulo). Cuando ocurre esto en una instrucción de asignación, la regla de conversión de tipos es muy sencilla: El valor de la parte derecha (expresión) de la asignación se convierte al tipo de la parte izquierda (variable destino) El siguiente ejemplo ilustra lo anterior: ......... int i; char c; float f; void main (void) __________________________________________________________________________________________________________ El C como lenguaje estructurado 21
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
{ c = i; i = f; f = c; f = i, } ..........
-
-
En la línea 1ª, los bits de orden superior situados a la izquierda de la variable entera i se recortan, dejando c con los 8 bits menos significativos. Si i valía inicialmente entre 0 y 256, entonces i y c deberían tener valores idénticos. En otro caso, el valor de c debería reflejar sólo los bits menos significativos de i . En la línea 2ª, i recibe la parte no fraccionaría de f . En la línea 3ª, f recibe el valor entero de 8 bits almacenado en c, convertido en formato de coma flotante. En la línea 4ª, f recibe el valor del entero i convertido en formato de coma flotante.
A continuación, varios ejemplos de todo lo visto hasta ahora. #include <stdio.h> void main(void) { long importe = 35000L; // Sentencia de declaracion e inicialización double veces = 1000e300; printf ("¡Atencion!: El valor del entero largo de este"); printf ("\nprograma es %ld, pero puede ser cambiado", importe); importe = -1000000000; // menos 100 millones printf (" %.0e veces.\nPor ejemplo, ahora vale %ld.", veces, importe); getchar(); } ¡Atencion!: El valor del entero largo de este programa es 35000, pero puede ser cambiado 1e+303 veces. Por ejemplo, ahora vale -1000000000.
#include <stdio.h> void main(void) { unsigned short int ovejas, patas; ovejas = 15000; patas = 4 * ovejas; printf ("En un rebaño de %u ovejas hay %hu ", ovejas, patas); printf ("patas,\nsuponiendo que ninguna es coja."); getchar(); } En un reba±o de 15000 ovejas hay 60000 patas, suponiendo que ninguna es coja.
#include <stdio.h> void main(void) { __________________________________________________________________________________________________________ El C como lenguaje estructurado 22
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
unsigned short int patas, patos, cojos; patos=25; cojos=3; patas = 2 * patos; printf("En un lago hay %u patos con %u ", patos, patas-cojos); printf("patas,\nsuponiendo que %u son cojos.\n", cojos); getchar(); } En un lago hay 25 patos con 47 patas, suponiendo que 3 son cojos.
#include <stdio.h> void main(void) { unsigned long int inicial=10000L, gana=3000L, pierde=2000L; printf("Carolina entro en el casino con %lu dolares, ", inicial); gana = gana * 3; printf("gano %lu y \nperdio ", gana); printf("%lu. Aun puede perder %lu mas.",2*pierde,inicial+gana2*pierde); getchar(); } Carolina entro en el casino con 10000 dolares, gano 9000 y perdio 4000. Aun puede perder 15000 mas. void main(void) { char comillas = '"'; // char comillas = 34 ; char barraatras = '\\'; // char barraatras = 92 ; short int i = 32767; double a,b; float c,d; b = 2.0e10 + 1.0; a = b-2.0e10; printf("%lf \n", a); //Resulta 1.000000 => Si tiene bastante precisión d = 2.0e10 + 1.0; c = d-2.0e10; printf("%f\n",c); //Resulta 0.000000 => No tiene suficiente precisión printf("Se imprime una constante de caracter: %c\n", 'R'); printf("Juan dijo:\"%c es una barra invertida y su codigo es %d.%c.\n", barraatras, barraatras, comillas); printf("%hd %hd %hd \n", i, i+1, i+2); getchar(); //Se desborda }
1.000000 0.000000 Se imprime una constante de caracter: R Juan dijo:"\ es una barra invertida y su codigo es 92.". 32767 -32768 -32767
12. SALIDA FORMATEADA POR PANTALLA: printf() La siguiente función sirve para escribir información en la pantalla. __________________________________________________________________________________________________________ El C como lenguaje estructurado 23
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
printf(cadena_formato ,
item1 ,
item2, ...
);
El prototipo de la función printf() se encuentra en stdio.h. #include <stdio.h> void main(void) { printf("Un proverbio chino dice:\n" ); printf("Si no dominal infolmatica, "); printf("infolmatica dominal a ti.\n"); printf(" Filmado: Pon te Yang \n"); getchar(); //El programa espera que se pulse INTRO. } Un proverbio chino dice: Si no dominal infolmatica, infolmatica dominal a ti. Filmado: Pon te Yang _
-
El cuadro superior contiene el código fuente del programa y el inferior el resultado que se puede observar en pantalla al ejecutarlo. Nótese que la combinación de caracteres \n dentro del entrecomillado genera un retorno de carro en esa posición. Obsérvese también como el cursor queda a principio de la 4ª línea. Por último, todas las sentencias printf contienen el parámetro cadena_formato, pero ninguna contiene items.
cadena_formato
es una tira de caracteres que contiene aquellos que se han de imprimir tal como aparecen en el entrecomillado y/o los especificadores de formato (se distinguen por el símbolo % ).
item1, item2,..
son las distintas variables, constantes o expresiones, cuyo valor se quiere imprimir. No tienen que aparecer obligatoriamente.
De este modo, la función printf() describe la manera en que han de imprimirse los items en caso de que existan. Debe aparecer una especificación de conversión por cada item. Es decir, debe haber exactamente el mismo número de argumentos que de especificadores de formato y ambos deben coincidir en orden de izquierda a derecha. #include <stdio.h> void main(void) { printf("Mas vale pajaro en mano "); printf("que %d volando. No hay %u sin %u.\n La", 100, 2, 3); printf(" letra %c es la %s del alfabeto.\n", 'z', "ultima" ); printf("%8.5s pesa %9.4f kilos y esa roca %.15g .\n", “Mariano”, 75.687, 1234567890.1230e12); printf("Gana %ld pesetas con %hu apuestas.\n", 35000000L, 100 ); printf ("ASCII 65 = \x41, ASCII 69 = \105 "); getchar(); } Mas vale pajaro en mano que 100 volando. No hay 2 sin 3. La letra z es la ultima del alfabeto. Maria pesa 75.6870 kilos y esa roca 1.234567890123e+21 . Gana 35000000 pesetas con 100 apuestas. __________________________________________________________________________________________________________ El C como lenguaje estructurado 24
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
ASCII 65 = A, ASCII 69 = E
-
En la primera sentencia printf no hay ningún "item", en la segunda aparecen tres, etc. En la 2ª sentencia el símbolo % indica que se va a imprimir una variable en esa posición y el especificador de formato d informa que la variable a imprimir es un número entero.....
-
Especificadores de formato de printf(). Código %c %d %i %e %E %f %g %G
Formato Código Formato Carácter %o Octal sin signo Enteros decimales con signo %s Cadenas de caracteres Enteros decimales con signo %u Enteros decimales sin signo Notación científica (e min.) %x Hexadecimales sin signo (min.) Notación científica (E may.) %X Hexadecimales sin signo (may.) Coma flotante %p Puntero El más corto entre %e y %f %n Argumento asociado es un ptro. El más corto entre %E y %f %% Imprime símbolo % Modificadores.
Los modificadores son apéndices que se agregan a los especificadores de conversión básicos para modificar la salida. Se colocan entre el símbolo % y el carácter que define el tipo de conversión: % [-] [X] [.Y] [longitud] conversión -
Indica que el item comience a escribirse empezando por la izquierda del campo que tenga asignado.
X
Anchura mínima del campo; si no es suficiente se usa uno mayor.
.Y
Número de decimales o máximo número de caracteres a imprimir. (El valor por defecto en los formatos en punto flotante son 6 decimales).
longitud
Si la conversión es un nº entero : h trata al argumento como short l trata al argumento como long Si la conversión es un nº real: L trata al argumento como long double
Consejos. Para imprimir columnas de datos se puede conseguir una salida nítida utilizando campos de anchura fija lo suficientemente grandes.
__________________________________________________________________________________________________________ El C como lenguaje estructurado 25
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
Cuando un número está destinado a aparecer dentro de una frase es a menudo conveniente especificar un campo igual o menor que el esperado, para evitar que aparezcan blancos suplementarios que afeen el texto.
13. ENTRADA FORMATEADA POR TECLADO: scanf() La rutina de entrada por teclado de propósito general es scanf(). Permite leer todos los tipos de datos predefinidos y convierte los números automáticamente al formato interno apropiado. Se trata de la rutina inversa a printf(). El prototipo de la función scanf() se encuentra en stdio.h. El formato general de scanf() es: int scanf(const char *cadena-formato, lista de argumentos); La cadena-formato determina cómo se deben leer los valores y cómo se almacenan en la variables apuntadas por los argumentos de la lista. La cadena de formato está constituida por tres clases de caracteres: Especificadores de formato. Caracteres de espacio en blanco. Caracteres distintos del espacio en blanco. La función scanf() devuelve el número de campos que constituyen la entrada. Especificadores de formato. Los especificadores de formato de entrada van precedidos por un signo de porcentaje (%) e indican a scanf() el tipo de dato que se va a leer a continuación. Los especificadores de formato se asocian en orden, de izquierda a derecha, con los argumentos que aparecen en la lista de argumentos. Estos códigos aparecen en la siguiente tabla: Código %c %d %i %e %f %g %o %s %x %p %n %u %[ ]
Significado Leer un único carácter. Leer un entero decimal. Leer un entero decimal. Leer un número en coma flotante. Leer un número en coma flotante. Leer un número en coma flotante. Leer un número octal. Leer una cadena. Leer un número hexadecimal. Leer un puntero. Recibe un valor entero igual al número de caracteres leídos. Leer un entero sin signo. Examinar un conjunto de caracteres.
#include <stdio.h> #include void main(void) __________________________________________________________________________________________________________ El C como lenguaje estructurado 26
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
{ unsigned char ch=65; // es equivalente ->char ch='A'; printf("Inicialmente la variable ch vale %c.\n", ch); ch = 129; // si ch fuera char, su código = -127 printf("Luego, ch vale %c y su codigo %d.\n", ch, ch); printf ("Caracter (1 solo): "); scanf("%c", &ch ); // &ch significa dirección de ch printf("Ahora, ch vale %c cuyo codigo es %d.\n", ch, ch); getch(); } Inicialmente la variable ch vale A. Luego, ch vale ü y su codigo 129. Caracter (1 solo): ñ Ahora, ch vale ñ cuyo codigo es 164.
Lectura de números. Para leer un número decimal se utilizan los especificadores %d o %i (se incluyen ambos especificadores, que realizan lo mismo, por razones históricas). Para leer un entero sin signo, se utiliza el especificador de formato %u. Los especificadores %e, %f o %g se utilizan para leer un número en coma flotante tanto en notación estándar como científica (de nuevo se incluyen estos especificadores, que realizan lo mismo, por razones históricas). Se puede utilizar scanf() para leer enteros tanto en formato octal como en formato hexadecimal utilizando las órdenes de formato %o y %x, respectivamente. El especificador %x puede utilizarse tanto en mayúsculas como en minúsculas. Se pueden introducir las letras de la A a la F en mayúsculas o en minúsculas al escribir los números hexadecimales. La función scanf() detiene la lectura de un número en el momento en el que se encuentra con un carácter no numérico. Lectura de caracteres individuales. Se pueden leer caracteres individuales utilizando getchar() o una función alternativa. También, se puede utilizar scanf() para llevar a cabo este propósito mediante la utilización del especificador de formato %c. Cuando se leen, además de caracteres, otros tipos de datos, se utilizan los espacios en blanco, tabuladores y caracteres de nueva línea como separadores de campo; sin embargo, cuando se lee un único carácter, los caracteres de espacio en blanco se leen como cualquier otro carácter. Por ejemplo, con un flujo de entrada «x y» , el siguiente fragmento de código scanf (“%c%c%c”, &a, &b, &c);
asigna el carácter x a a, un espacio a b y el carácter y a c. Lectura de cadenas. __________________________________________________________________________________________________________ El C como lenguaje estructurado 27
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
La función scanf() se puede utilizar para leer una cadena desde el flujo de entrada utilizando el especificador de formato %s. Este especificador le indica a scanf() que lea caracteres hasta que encuentre un carácter de espacio en blanco. Los caracteres que se leen se van introduciendo en el array de caracteres apuntado por el argumento correspondiente y se añade un terminador nulo al resultado. En lo que se refiere a scanf(), un carácter de espacio en blanco se corresponde con un espacio, un carácter de nueva línea, un tabulador, un tabulador vertical o un salto de línea. A diferencia de gets(), que lee una cadena hasta que se introduce un retorno de carro, scanf() lee una cadena hasta que se introduce el primer carácter de espacio en blanco. Esto significa que no se puede utilizar scanf() para leer una cadena del tipo «Esto es un prueba» debido a que el primer espacio provoca la finalización del proceso de lectura. #include <stdio.h> #include main() { int edad; float peso, altura, valor; char nombre[15], ape1[20]; printf ("Nombre: "); scanf("%s", nombre); printf ("Primer Apellido: "); scanf("%s", ape1); printf ("Edad: "); scanf ("%d", &edad ); printf ("Peso (kilos): "); scanf ("%f", &peso ); printf ("Altura (cm.): "); scanf ("%f", &altura ); printf ("%s %s, su edad es %d, pesa %.2f kilos y mide %.2f metros.\n", nombre, ape1, edad, peso, altura/100); //Se supone que 1 onza equivale a 32.1512 kg., luego en onzas: valor = peso / 32.1512; //Se supone cotización oro=400$ la onza. valor = valor * 400.0; printf ("Su peso en oro es $%.2f\n", valor); getch(); //Lee un carácter del teclado, sin dar "eco" en la pantalla } Nombre: María Primer Apellido: García Edad: 30 Peso (kilos): 60 Altura (cm.): 176 María García, su edad es 30, pesa 60.00 kilos y mide 1.76 metros. Su peso en oro es $746.47
14. INSTRUCCIONES PROGRAMA.
DE
CONTROL
DE
FLUJO
DEL
C define tres categorías específicas de instrucciones de control de flujo de programa: selección (if y switch), iteración (while, for y do/while) y salto (break, __________________________________________________________________________________________________________ El C como lenguaje estructurado 28
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
continue y goto). Además, hay que considerar instrucciones de salto a la instrucción return y a la función exit() ya que afectan al control del programa.
15. INSTRUCCIONES DE CONTROL SELECTIVAS. if Ya hemos comentado en este mismo tema que una expresión de relación o una expresión lógica, si es cierta toma el valor 1, y si es falsa toma el valor 0. Por otra parte, una expresión cualquiera (combinación de operadores, constantes y/o variables) se considera cierta si tiene un valor distinto de 0, y se considera falsa sólo si toma el valor 0. La sintaxis de la sentencia if es if (expresión) sentencia La expresión podrá ser: de relación: if ( x > y )... lógica: if (sw)... aritmética if ( x + y )... En caso de que la expresión sea verdadera (en general, si es distinta de 0) se ejecuta la sentencia; en caso contrario, se pasa a ejecutar la siguiente sentencia a if. La sentencia o instrucción puede ser simple o compuesta. Si es simple, consistirá en una sola sentencia terminada en punto y coma. Si es compuesta, contendrá varias sentencias terminadas en punto y coma que están delimitadas por llaves, lo que se conoce como bloque. De una forma más general, la sintaxis de la sentencia if es: if (expresion) sentencia_1 else sentencia_2 donde la cláusula else es opcional. En esta sintaxis, si expresion es distinta de 0 (cierta), se ejecuta sentencia_1; pero si es 0 (falsa), se ejecuta sentencia_2. Análogamente, sentencia_1 y sentencia_2 pueden ser simples o compuestas. Recuérdese que sólo se ejecuta el código asociado con if o el código asociado con else, pero nunca ambos. El formato general de la sentencia if con bloques de instrucciones es: if (expresion) { secuencia de sentencias_1 } else { secuencia de sentencias_2 } #include <stdio.h> #include int main(void) { __________________________________________________________________________________________________________ El C como lenguaje estructurado 29
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
int clave = 4300 + 'A' , adivina; printf("*** BANCO BBUVA de LAS VIÑAS ***\n"); printf("Su clave secreta: "); scanf("%d",&adivina); if (clave == adivina) { printf("Su clave es correcta, pero mejor, vaya al otro "); printf("cajero de la esquina porque me queda poco dinero.\n"); } else printf("Clave no válida.\n"); getch(); }
Ifs anidados. Un if anidado es una instrucción if que forma parte del cuerpo de un if o un else. La razón de que los ifs anidados sean tan problemáticos es que puede resultar difícil saber con qué if se asocia cada else. Afortunadamente, C proporciona una regla muy simple: cada else está asociado con el if precedente más cercano (en el mismo nivel de ámbito) que no tenga ya asociado una instrucción else. Para anidar sentencias if hay que tener en cuenta: La sentencia if...else cuenta como una única sentencia, y por tanto, no es necesario encerrarla entre llaves. PSEUDOCODIGO si a > 0 entonces si a > 1000 entonces Escribir("a positivo grande") sino Escribir("a positivo pequeño") finsi sino si a < -1000 entonces Escribir("a muy negativo”) sino Escribir("a poco negativo”) finsi finsi
LENGUAJE C if (a>0) if (a>1000) printf("a positivo grande”); else printf("a positivo pequeño”); else if (a < -1000) printf("a muy negativo”); else printf("a poco negativo”);
Cada else va asociado al if anterior más próximo, a no ser que se incluyan llaves que indiquen lo contrario. Ejemplos: if (n > 6) { if ( n<12 ) printf ("%d está comprendido entre 6 y 12.",n); } else printf ("%d es menor o igual que 6.",n); if (n > 6) if ( n < 12 ) printf ("%d está comprendido entre 6 y 12.", n); else printf ("%d es mayor o igual que 12.", n); // Aunque la sangría confunda el else depende del segundo if #include <stdio.h> #include #define CLAVE 4444 void main(void) { printf("%d",CLAVE); __________________________________________________________________________________________________________ El C como lenguaje estructurado 30
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
int adivina, decenas; printf("*** BANCO BBMOSTO de LAS VIDES ***\n\n"); printf("Su clave secreta: "); scanf("%d",&adivina); if (CLAVE == adivina) { printf("Clave correcta... pero por razones técnicas transitorias,"); printf(" momentáneas y pasajeras, no podemos atenderle.\n"); } else { printf("Clave no válida... Pero creo que esa es la clave de "); if (adivina < CLAVE) { printf("su cuñada, que es mas baja.\n"); decenas =(CLAVE-adivina)/10 ; if (decenas) printf("Suba %d decena\s.\n", decenas); } else { printf("su vecina, que es mas alta.\n"); decenas = (adivina-CLAVE)+/10 ; if (decenas) printf("Baje %d decena\s.\n", decenas); } } getch(); }
El operador condicional ? El operador ? recibe el nombre de ternario porque requiere tres operandos. condicion
?
expresion_1
:
expresion_2
Se puede utilizar para reemplazar a las instrucciones if/else que contengan sólo sentencias simples. Si la condición es cierta, la expresión condicional (todo el conjunto) toma el valor de expresión_1; si es falsa (o sea, 0), toma el valor de expresión_2. Ejemplos:
A > B ? mayor = A : mayor = B ; mayor = A > B ? A : B ; x = x>0 ? x : -x ; // halla valor absoluto
#include <stdio.h> #include #define CLAVE 15*433 void main(void) { int adivina; printf("*** BANCO BBH2O del GUADALETE ***\n"); printf("Su clave secreta: "); scanf("%d",&adivina); if (CLAVE == adivina) printf("Clave correcta, pero vuelva mañana que ya estaba cerrando."); else { printf("Clave no válida. El número introducido "); adivina
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
}
switch Aunque el if-else-if escalonado puede realizar evaluaciones múltiples, no es excesivamente elegante. El código puede ser difícil de seguir y puede incluso confundir a su autor pasado un tiempo. Por estas razones, C incorpora una instrucción de decisión de bifurcación múltiple llamada switch, que compara sucesivamente un valor entero frente a una lista de constantes enteras o de carácter; cuando se encuentra una coincidencia, se ejecuta la instrucción o instrucciones asociadas con ese valor. El formato general de la instrucción switch es switch (expresión_entera) { case cte_1: sentencias; [ break ]; case cte_2: sentencias; [ break ]; ...... [ default : sentencias ; ] } El funcionamiento es como sigue: -Se evalúa la expresión entera y se rastrea la lista de constante o etiquetas hasta encontrar un valor de una constante con el que coincida. - Si se encuentra dicho valor, se ejecutan las sentencias que haya desde esa etiqueta hasta el primer break. Tales sentencias no hay que escribirlas encerradas entre llaves, es decir, las sentencias asociadas a cada etiqueta no son bloques sino secuencias de instrucciones. De todos modos, es posible utilizar un bloque como una sentencia de la secuencia, e incluso, declarar una o más variables dentro de él. - Si no coincide el valor de la expresión entera con ninguna de las constantes de las etiquetas no se ejecuta ninguna acción, excepto si existe la etiqueta default (es opcional incluirla o no) en cuyo caso se ejecutan las sentencias asociadas a ella. - La sentencia break es opcional. Si no se coloca ninguna, una vez que la sentencia switch bifurque a una etiqueta, se ejecutarán todas las sentencias que haya desde dicha etiqueta hasta el final del switch. - Las etiquetas del switch deben ser constantes de tipo entero (incluyendo el tipo char). En ningún caso se pueden emplear variables en las etiquetas, ni tampoco colocar comparaciones. - Si queremos que dos etiquetas distintas den el mismo resultado se pueden poner juntas: case x: case y: ; - Dos constantes case del mismo switch no pueden tener los mismos valores. __________________________________________________________________________________________________________ El C como lenguaje estructurado 32
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
- Si se utilizan constantes de carácter en el switch, éstas se convierten automáticamente en sus valores enteros. #include <stdio.h> #include void main(void) { unsigned short nota; printf("Su nota: "); scanf("%d", ¬a); switch (nota) { case 0: printf("No apareció.\n"); break; case 1: case 2: printf("Estudio muchísimo...una lección que no entraba.\n"); break; case 3: case 4: printf("Estudio toda la noche...y se durmió en el examen.\n"); break; case 5: printf("Se le perdió una de las dos \"chuletas\".\n"); break; case 6: printf("Usted va como España.\n"); printf("Si la gira 180 grados, ira sobresaliente.\n"); break; case 7: case 8: printf("Esa es una nota ble"); break; case 9: printf("Si hace el pino, sólo verá Bien"); break; case 10: printf("Le he pedido una nota, no dos.... ¡ah, bien!... \n"); printf("...Perdone, es la falta de costumbre.\n"); break; default: printf("Desafina."); } getch(); }
- Es posible incluir un switch como parte de una secuencia de instrucciones de switch más externo. En este caso es posible que las etiquetas de los switch internos coincidan con algunas de los switch externos.
16. INSTRUCCIONES DE CONTROL REPETITIVAS. while La sintaxis es: while (condición) sentencia Un bucle es un conjunto de sentencias que se repetirán cíclicamente al ejecutar el programa. En el lenguaje C, dentro del bucle se acepta una sentencia simple, una sentencia compuesta o una sentencia vacía: - Si es una sentencia compuesta estará delimitada por llaves. - Cuando la sentencia es simple no son necesarias las llaves. __________________________________________________________________________________________________________ El C como lenguaje estructurado 33
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
- Cuando el programa llegue por primera vez a la sentencia while , comprueba si la condición expresada entre paréntesis se cumple o no; en caso afirmativo (en general, si toma un valor distinto de 0) se ejecutan las sentencias del bucle y se vuelve a comprobar la condición. Así sucesivamente, hasta que la condición se vuelva falsa (o toma el valor 0), y entonces el programa prosigue su tarea ejecutando la sentencia siguiente al bucle (si el cuerpo del bucle era una sentencia compuesta, se ejecutará la que está a continuación de la llave de cierre). #include <stdio.h> #include #define FIJO_CENT_FAHR 32.00 #define ESCALA 1.8 void main(void) // Grados centígrados a Fahrenheit desde -10 a 50 { float cent, fahr ; char tecla; printf ("Conversión de Grados centígrados a Grados Fahrenheit\n\n"); cent = -10.0; while (cent < 51.0) { fahr = ESCALA * cent + FIJO_CENT_FAHR ; printf("%8.1f C = %5.1f F", cent, fahr); // 8 5 5 2 = 20 caracteres cada vez cent++; } printf ("\n\n Quiere calentarse más? (N/N) .\n"); while ( (tecla = getch()) != 'N' ) ; // sentencia vacía }
Es posible incluir los operadores de incremento y de decremento en la condición de la sentencia while, de modo que se combinen el proceso de incremento del índice y la parte comparativa del bucle. De este modo se obtiene una representación más compacta y se consigue ubicar en un lugar los dos procesos que controlan el bucle. #include <stdio.h> #include // Mejora el ejemplo anterior #define FIJO_CENT_FAHR 32.00 #define ESCALA 1.8 void main(void) // Grados centígrados a Fahrenheit desde -10 a 50 { float cent, fahr; char tecla; printf ("Conversión de Grados centígrados a Grados Fahrenheit\n\n"); cent = -11.0; // cent = -10.0; while (++cent < 51.0) { fahr = ESCALA * cent + FIJO_CENT_FAHR; printf("%8.1f C = %5.1f F", cent, fahr); } printf ("\n\n Quiere calentarse más? (N/N) .\n"); while ( (tecla = getch()) != 'N' ) ; // sentencia vacía }
do while La sintaxis es: do sentencia __________________________________________________________________________________________________________ El C como lenguaje estructurado 34
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
while (expresion); La sentencia se ejecuta al menos una vez, aunque ya en ese momento la expresión fuera falsa (o cero). Cada vez que se ejecuta la sentencia se evalúa la expresión y si es cierta (distinta de cero) vuelve a ejecutarse otra vez la sentencia. Es decir, la sentencia se ejecuta MIENTRAS la expresión sea distinta de 0 o cierta. Dicho de otra forma, la sentencia se ejecuta hasta que la expresión sea falsa o 0. La sintaxis para una sentencia compuesta sería la siguiente: do { sentencia_A; sentencia_B; ........... } while (expresion); Nótese cómo las llaves sólo son necesarias cuando la sentencia es compuesta. De todos modos, las llaves se pueden utilizar siempre para mejorar la legibilidad. Es frecuente usar esta estructura cuando, por ejemplo, se pide un dato por teclado o una opción válida de un menú, ya que el bucle se ejecutará al menos una vez y si los datos introducidos no son correctos, se volverán a pedir. #include <stdio.h> #include #include <math.h> //necesario por las funciones fabs() y sqrt() void main(void) { // Estando plácidamente sentado bajo una haya, acomodado entre // sus raíces, y a raíz de no tener nada mejor que hacer (si no // hago esto no me hallo) y queriendo evitar echar raíces intentando // el cálculo manual de raíces cuadradas, diseñé este programa que // las halla, lindo donde los haya. double num, raiz , mem_raiz , error; printf("CALCULO RAICES CUADRADAS.\n" "Numero=0 --> Fin del programa.\n\n"); do { do { printf("Numero: "); scanf("%lf",&num); if (num<0.0) printf("Numero no válido\n"); } while (num < 0.0); if (num > 0.0) { do { printf("Margen error (>0 y <0.01) : "); scanf("%lf",&error); if (error<=0.0 || error>=0.01) printf("Valor del margen de error no válido\n"); } while (error <= 0.0 || error >= 0.01); raiz = num / 2; printf("Aproximaciones: \n"); do { __________________________________________________________________________________________________________ El C como lenguaje estructurado 35
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
mem_raiz = raiz; raiz = ( num / raiz + raiz ) / 2 ; printf("%20.8lf",raiz); } while (fabs ( raiz - mem_raiz ) >= error ); printf("\n\nRaiz cuadrada de %.3lf = %.8f\n\n", num, raiz); printf("Calculado directamente: %.8lf\n\n\n", sqrt(num) ); } } while(num > 0.0); }
for for (expresion_1 ; expresion_2 ; expresion_ 3) sentencia - expresion_1 se realiza una sola vez, al comenzar el bucle for. Suele ser una inicialización. - expresion_2 es una condición que se evalúa antes de cada ejecución potencial del bucle; si es cierta (distinta de cero) se ejecuta una vez el bucle completo. - expresion_3 se evalúa al final de cada bucle; suele ser la actualización de las variables implicadas en la condición. #include <stdio.h> #include void main(void) { int num, x; printf(" N N al cubo\n"); //Tabla con 10 números y sus cubos for (num=1; num<=10; num++) printf("%5d %5d\n", num, num*num*num); printf("\n\n"); getch(); //Del 100 al 1 num=100; for (;num>=1;) printf("%5d",num--); printf("\n\n"); getch(); // De 0 a 50, con incrementos de 5, y // de 50 a 100, con incrementos de 10. for (num=0, x=5 ; num<=100; num+=x ) { printf("%5d",num); (num>=50)? x=10 : 1 ;// Hay que poner algún valor } // detrás de los dos puntos, aunque no se utilice getch(); }
La sentencia que se ejecuta puede ser simple (terminada en punto y coma) o compuesta (conjunto de sentencias simples delimitadas por llaves). Una o más expresiones se pueden dejar en blanco, por ejemplo: 1) 2)
La expresion_1, si la variable que controla el bucle ya viene inicializada. Si la expresion_2 no existe dará lugar a un bucle infinito puesto que un test vacío se considera cierto.
__________________________________________________________________________________________________________ El C como lenguaje estructurado 36
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
3)
La expresion_3 no hace falta si la actualización de la variable que controla el bucle se produce dentro del cuerpo del bucle.
En todo caso, aunque una expresión se deje en blanco siempre hay que colocar los dos punto y coma. Los parámetros de las expresiones 2 y 3 se pueden alterar dentro del cuerpo del bucle. Se puede utilizar el operador coma (,) que enlaza las expresiones, por ejemplo, para realizar más de una inicialización o más de una actualización. No tiene sentido utilizarlo en la expresion_2, pues no equivale ni al operador && (and) ni al operador || (or) . El operador coma evalúa las expresiones de izquierda a derecha. El operador coma no está renstringido al bucle for, pero es donde se utiliza con mayor frecuencia. Por otra parte, no hay que confundirlo con el “separador coma” que se usa por ejemplo en sentencias printf para distinguir argumentos. Si la condición es inicialmente falsa, no se ejecutará nunca el cuerpo del bucle. El bucle for se puede utilizar para generar un retardo; por ejemplo: for (cont=1; cont<10000; cont++) ;
Como se puede apreciar la sentencia está vacía. break Se usa en la sentencia switch, y en los bucles for , while y do while para salir de dicho bucle y ejecutar la siguiente sentencia. Cuando se encuentra en una estructura anidada, la liberación afecta a la estructura más interna que la contenga. #include <stdio.h> #include int main(void) { const int VALOR_BUSCADO=789; const int REPETICIONES=15; int t, visualizado, salir, count; for(t=0; t<100; t++) // se sale cuando t vale 10 { printf("%3d ", t); if(t==10) break; } printf("\n\n"); getch(); printf("Puedes esperar a visualizar %d " "o bien, pulsar salir.\n\n", VALOR_BUSCADO); t = 0; salir = 0; visualizado = 0; do { for ( ; t < 1000 ; t++ ) { printf("%d\r", t); if (!visualizado) visualizado = t == VALOR_BUSCADO ? 1 : 0 ; if(kbhit())
una
tecla
para
__________________________________________________________________________________________________________ El C como lenguaje estructurado 37
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
{ //KBHIT() devuelve 0 si no se ha pulsado una tecla salir = 1; break; } } } while (!visualizado && !salir ) ; if (visualizado) printf("Valor visualizado.\n"); printf("\n\n"); getch(); printf ("Ahora, %d veces los NUEVE primeros numeros.\n", REPETICIONES); for(t=0; t< REPETICIONES; ++t) { count = 1; for(;;) // BUCLE INFINITO { printf("%4d", count); count++; if(count==10) break; } printf("\n"); } getch(); }
continue Es utilizable en todos los bucles y no se pude usar en switch. Interrumpe el flujo del programa, evitando el resto de la iteración, y se dirige de nuevo a evaluar la expresión que condiciona la ejecución del bucle; si ésta es cierta comienza una nueva iteración. Ejemplo: for (i=0;i<4;++i) { printf(“%d\n”,I); if (i==2) continue; printf(“La variable es distinta de 2); }
APÉNDICE. Conversión de tipos. En general, en una sentencia o expresión se emplean variables y constantes de un solo tipo. Sin embargo, si en un momento dado se mezclan dichos tipos, el C no se molesta en seguir la pista del eventual error. En su lugar, utiliza una serie de reglas para efectuar automáticamente conversiones de tipo: __________________________________________________________________________________________________________ El C como lenguaje estructurado 38
I.E.S. Francisco Romero Vargas –Departamento de Informática Fundamentos de Programación __________________________________________________________________________________________________________
1º. En cualquier operación en que aparezcan dos tipos diferentes se eleva la "categoría" del operando que la tiene menor para igualarla a la del mayor. Proceso conocido como "promoción". 2º. El rango o categoría de los tipos, de menor a mayor, es: char, short, int, long, float, double. Los tipos unsigned tienen el mismo rango que el tipo a que están referidos. 3º. En una sentencia de asignación, el resultado final de los cálculos se reconvierte al tipo de la variable a que están siendo asignados. Así pues, el proceso puede ser una "promoción" o una "pérdida de rango", según que la variable a asignar sea de categoría superior o inferior. Con el fin de conservar al máximo la precisión numérica, todas las variables y constantes float se convierten en double cuando se realizan cálculos aritméticos con ellas. Así se reduce enormemente el error de redondeo. Por supuesto, la respuesta final se reconvierte a float, si ese es el tipo declarado. Cuando se convierte de enteros a caracteres, de enteros largos (long) a enteros y de enteros a enteros cortos (short), la regla básica consiste en eliminar la cantidad apropiada de bits más significativos. Cuando se utilizan enteros de 16 bits, significa que ... se perderán 8 bits cuando se convierta de entero a carácter se perderán 16 bits cuando se convierta de entero largo (long) a entero. La siguiente tabla muestra estas conversiones de tipo en asignaciones: Tipo del destino Tipo de expresión Posible pérdida signed char char char char short int short int int (16 bits) int (32 bits) float double
unsigned char short int int (16 bits) int (32 bits) int (16 bits) int (32 bits) long int long int double long double
Si valor > 127 => destino negativo. Los 8 bits de mayor orden. Los 8 bits de mayor orden. Los 24 bits de mayor orden. Ninguna. Los 16 bits de mayor orden. Los 16 bits de mayor orden. Ninguna. Precisión. Resultado redondeado. Precisión. Resultado redondeado.
Se deben recordar dos puntos importantes que pueden afectar a la portabilidad del código que se escriba: - Las conversiones de un int en un float o de un tipo float en un double, etcétera, no aumentarán ninguna precisión ni exactitud. Estos tipos de conversiones sólo modificarán el formato en que se representa el valor. - Algunos compiladores (y procesadores) de C siempre tratarán una variable char como positiva, sin importar el valor que tiene cuando se convierte en un entero o en un float. Otros compiladores pueden tratar los valores de una variable char superiores a 127 como números negativos cuando se convierten (como lo hace Borland C++). Así pues, en general se utilizarán variables char para caracteres y, para evitar un posible problema de portabilidad en esta área, se debería utilizar en cada caso el tipo adecuado: int, short int, signed char, unsigned char, etc. Para utilizar la tabla anterior y realizar una conversión no mostrada directamente, simplemente se convierte de tipo en tipo hasta alcanzar el tipo destino. Por ejemplo, para convertir un double en un int, primero se convierte de double a float y, a continuación, de float a int. Si se ha utilizado un lenguaje de programación como Pascal, que prohíbe esta conversión de tipo automática, se puede pensar que C es muy relajado. Sin embargo, recuérdese que C se diseñó para facilitar el trabajo del programador permitiendo realizar el trabajo en C en lugar de en ensamblador. Para conseguir esto, C tiene que permitir estas conversiones de tipo.
__________________________________________________________________________________________________________ El C como lenguaje estructurado 39