El lenguaje de programacion c

153
SEGUNDA EDICION EL LENGUAJE DE PROG CION Pearson Educación -- BRIAN W. KERNIGHAN DENNIS M. ruTCHIE ®

Transcript of El lenguaje de programacion c

  • 1. SEGUNDA EDICION EL LENGUAJE DE PROG CION Pearson Educacin -- BRIAN W. KERNIGHAN DENNIS M. ruTCHIE

2. EDICION EN INGLBS UNIX es una marca registrada AT & T EL LENGUAJE ])E PROGRAMACION C Traducido de la segunda edicin en ingls de: THE C PROGRAMMING LANGUAGE Prohibida la reproduccin total o parcial de esta obra, per cualquier medio o mtodo sin autorizacin escrita del editor DERECHOS RESERVADOS 1991 respecto a la segunda edicin en espaol por PRENTICE-HALL HISPANOAMERICANA, S.A. Atlacomulco Nm. 500-5 Piso Col. Industrial Atoto 53519, Naucalpan de Jurez. Edo. de Mxico Miembro de la Cmara Nacional de la Industria Editorial, Reg. Nm. 1524 ISBN 968-880-205-0 Original English language editiOn published by Copyright MCMLXXXVIll by Prentice-Hall, ine. AH Rights Reserved ISBN 0-)3-110362-8 IMPRESO EN MEXICO I PRINTED IN MEXICO Prefacio Prefacio a la primera edicin Introduccin Captulo t. Introduccin general l.l Comencemos 1.2 Variables y expresiones aritmticas 1.3 La proposicin for 1.4 Constantes simblicas 1.5 Entrada y salida de caracteres 1.6 Arreglos 1.7 Funciones 1.8 Argumentos-llamada por valor 1.9 Arreglos de caracteres 1.10 Variables externas y alcance Captulo 2. Tipos, operadores y expresiones 2.1 Nombres de variables 2.2 Tipos y tamaos de datos 2.3 Constantes 2.4 Declaraciones 2.5 Operadores aritmticos 2.6 Operadores de relacin y lgicos 2.7 Conversiones de tipo 2.8 Operadores de incremento y decremento ~9 Operadores para manejo de bits 2.10 Operadores de asignacin y expresiones 2.11 Expresiones condicionales 2.12 Precedencia y orden de evaluacin Captulo 3. Control de flujo 3.1 Proposiciones y bloques 3.2 If-else 3.3 Else-if 3.4 Switch 3.5 Ciclos-while y ror 3.6 Ciclos-do-while CONTENIDO ix xi 5 5 8 14 15 16 23 26 29 30 33 39 39 40 41 44 45 46 47 51 53 55 56 57 61 61 61 63 64 66 70 3. 3.7 Break y continue 3.8 Goto y etiquetas Captulo 4. Funciones}' la estructura del programa 4.1 Conceptos bsicos de funciones 4.2 Funcioncs que regresan valores no~enteros 4.3 Variables externas 4.4 Reglas de alcance 4.5 Archivos header 4.6 Variables estticas 4.7 Variables registro 4.8 Estructura de bloque 4.9 Inicializacin 4.10 Recursividad 4.11 El preprocesador de C Captulo 5. Apuntadores y arreglos 5. 1 Apuntadores y direcciones 5.2 Apuntadores y argumentos de funciones 5.3 Apuntadores y arreglos 5.4 Aritmtica de direcciones 5.5 Apuntadores a caracteres y funciones 5.6 Arreglos de apuntadores; apuntadores a apuntadores 5.7 Arreglos multidimensionales 5.8 Inicializacin de arreglos de apuntadores 5.9 Apuntadores vs. arreglos multidimensionales 5.10 Argumentos en la lnea de comandos 5.11 Apuntadores a funciones 5.12 Declaraciones complicadas Captulo 6. Estructuras 6.1 Conceptos bsicos sobre estructuras 6.2 Estructuras y funciones 6.3 Arreglos de estructuras 6.4 Apuntadores o estructuras 6.5 Estructuras autorreferenciadas 6.6 Bsqueda en tablas 6.7 Typedef 6.8 Uniones 6.9 Campos de bits Captulo 7. Entrada y salida 7.1 Entrada y salida estndar 7.2 Salida con formato-printf 7.3 Listas de argumentos de longitud variable 7.4 Entrada con formato-scanf 7.5 Acceso a archivos 7.6 Maneja de errores-stderr y exit 71 72 75 75 79 82 88 90 91 92 93 94 95 97 103 103 105 108 I I I 115 118 122 124 125 126 130 134 141 141 143 146 151 153 158 161 162 164 167 167 169 171 173 176 179 7.7 Entrada y salida de lneas 7.8 Otras funciones Captulo 8. La interfaz del sistema UNrx 8.1 Descriptores de archivos 8.2 E/ S de bajo nivel-read y write S.3 open, creat, clase, unlink 8.4 Acceso aleatorio-Iseek 8.5 Ejemplo-una implementacin de fopen y getc 8.6 Ejemplo-listado de directorios 8.7 Ejemplo-asignador de memoria Apndice A. Manual de referencia Al Introduccin A2 Convenciones lxicas A3 Notacin sintctica A4 Significado de los identificadores AS Objetos y valores-I A6 Conversiones A7 Expresiones AS Declaraciones A9 Proposiciones A10 Declaraciones externas AII Alcance y ligadura A12 Preprocesamiento A13 Gramtica Apndice B. Biblioteca estndar B1 Entrada y salida: < stdio.h > B2 Pruebas de clasificacin de caracteres: < ctype.h > B3 Funciones para cadenas: < string.h > B4 Funciones matemticas: 85 Funciones de utilera: B6 Diagnsticos: B7 Listas de argumentos variables: < stdarg.h > 88 Saltos no locales: B9 Seales: < signal.h > 810 Funciones de fecha y hora: 811 Lmites definidos en la implantacin: < Iimits.h > y < float.h > Apndice C. Resumen de modificaciones Indice 181 183 187 187 188 190 193 194 1'98 204 211 211 211 215 215 217 217 220 232 245 249 251 253 258 265 265 272 273 274 275 278 278 278 279 279 281 283 287 4. Prefacio El mundo de la computacin ha sufrido una revolucin desde la publicacin, en 1978, de El lenguaje de programacin C. Las grandes computadoras son aho- ra mucho ms grandes, y las computadoras personales tienen capacidades que ri- valizan con los mainframes de hace una dcada. Tambin el lenguaje e ha cambiado en ese tiempo, aunque slo en forma modesta, y se ha extendido ms all de lo que fueron sus orgenes como el lenguaje del sistema operativo UNIX. La creciente popularidad de e, los cambios en el lenguaje a lo largo de los aos, y la creacin de compiladores por grupos no involucrados en su diseo, se combinaron para demostrar la necesidad de una definicin del lenguaje ms pre- cisa y contempornea que la que proporcion la primera edicin de este libro. En 1983, el American Nalional Standards/nslilule (ANSI) estableci un comit cuyos propsitos eran producir "una definicin no ambigua del lenguaje e e, in- dependiente de la mquina", cuidando la conservacin de su espritu. El resulta- do es el estndar ANSI para el lenguaje C. El estndar formaliza construcciones sugeridas pero no descritas en la primera edicin, particularmente la asignacin de estructura y las enumeraciones. Pro- porciona una nueva forma de declaracin de funciones , que permite revisar com- parativamente su definicin y uso. Especifica una biblioteca estndar, con un conjunto extensivo de funciones para realizar la entrada y salida, la administra- cin de memoria, la manipulacin de cadenas y tareas semejantes. Precisa el comportamiento de caractersticas que no se mencionaron en la definicin origi- nal, y al mismo tiempo establece explcitamente cules aspectos del lenguaje tienen an dependencia de mquina. Esta segunda edicin de E//enguaje de programacin C lo describe tal como lo defini el estndar ANSI. (En el momento de escribir esta edicin, el estndar se encontraba en la etapa final de revisin; se esperaba su aprobacin a finales de 1988. Las diferencias entre lo que se ha descrito aqu y la forma final debern ser mnimas.) Aunque hemos hecho anotaciones en los lugares donde el lenguaje ha evolucionado, preferimos escribir exclusivamente en la nueva forma. En gene- ral esto no hace una diferencia significativa; el cambio ms visible es la !lueva forma de declaracin y defini cin de funciones. Los modernos compiladores ma- nejan ya la mayora de las posibilidades del estndar : ix 5. X PREFACIO Hemos tratado de mantener la brevedad de la primera edicin. El lenguaje C no es grande, y no le est bien un gran libro. Hemos mejorado la exposicin de caractersticas crticas, como los apuntadores, que son parte central en la progra- macin con C. Hemos redefinido los ejemplos originales y agregamos ejemplos nuevos en varios captulos. Por ejemplo. se aument el tratamiento de declaracio- nes complicadas con programas que convierten declaraciones en palabras y vice- versa. Como antes, todos los ejemplos se han probado directamente a partir del texto, el cual est diseado de manera que lo pueda leer la mquina. El apndice A, manual de referencia, 11 0 es el eSlndar, sino que nuestra intencin fue trasladar la esencia del estndar a un espacio ms pequeo. Est hecho con el nimo de que proporcione una fcil comprensin para los progra- madores, pero no como una definicin del lenguaje para quienes escriben compi- ladores -ese papel propiamente le corresponde al estndar en s. El apndice B es un resumen de las posibilidades de la biblioteca estndar. Tambin tiene el pro- psito de ser una referencia para programadores, no para implantadores. En el apndice C se ofrece un resumen de los cambios de la versin original. Como mencionamos en el prefacio a la primera edicin, e "se lleva bien, en la medida en que aumen la nuestra experiencia con l". Con una dcada ms de experiencia, aun lo sentimos as. Deseamos que este libro le ayude a aprender el lenguaje e y lambin cmo usarlo. Tenemos un profundo reconocimiento hacia los amigos que nos ayudaron a producir esta segunda edicin. Jan Bentley, Doug Gwyn, Doug Mcllroy, Peter Nelson y Rob Pike nos dieron valiosos comentarios sobre casi cada pgina del bo- rrador de este manuscrito. Estamos agradecidos por [a cuidadosa [cctura de Al Aho, Dennis Allison, Joe Campbell, G. R. Em[in, Karen Fortgang, AlIen Ho- [ub, Andrew Hume, Dave Kristol, John Linderman, Dave Prosser, Gene Spaf- ford, y Chris Van Wyk. Tambin recibimos tiles sugerencias de Bill Cheswick, Mark Kernighan, Andy Koening, Robin Lake, Tom London, Jim Reeds, Clovis Tondo y Peter Weinberger. Dave Prosser respondi muchas preguntas detalladas acerca del estndar ANSI. Utilizamos extensivamente el intrprete de C ++ de Bjarne Stroustrup, para la prueba local de nuestros programas, y Dave Kristol nos ofreci un compilador ANSI C para [as pruebas finales. Rich Drcchs[er nos ayud grandemente con la composicin. Nuestro sincero agradecimiento a todos. Brian W. Kernighan Dennis M. Ritchie Prefacio a la primera edicin C es un lenguaje de programacin de propsito general que ofrece como ven- tajas economa de expresin, control de flujo y estructuras de datos modernos y un rico conjunto de operadores. Adems, C no es un lenguaje de "muy alto ni- ve[" ni "grande", y no est especializado en alguna rea especial de aplicacin. Pero su ausencia de restricciones y su generalidad lo hacen ms conveniente y efectivo para muchas tareas que otros lenguajes supuestamente ms poderosos. Originalmente, C fue diseftado para el sistema operativo UNIX y Dennis Ritchie lo implant sobre el mismo en [a DEC PDP-ll. El sistema operativo, el compila- dor de C y esencialmente todos los programas de aplicacin de UN IX (incluyendo todo el sofware utilizado para preparar este libro) estn escritos en C. Tambin existen compiladores para [a produccin en otras mquinas, incluyendo la IBM System/370, la Honeywell 6000 y la Interdata 8/32. El lenguaje C no est ligado a ningn hardware o sistema en particular y es fcil escribir programas que corre- rn sin cambios en cualquier mquina que maneje C. La finalidad de este libro es ayudar al lector a aprender cmo programar en C. Contiene una introduccin general para hacer que los nuevos usuarios se ini- cien lo ms pronto posible, captulos separados sobre cada caracterstica impor- tante y un manual de referencia. La mayora de las exposiciones estn basadas en la lectura, escritura y revisin de ejemplos, ms que en el simple estab[ecimien- to de reglas. En su mayora, los ejemplos son programas reales y completos, no fragmentos aislados. Todos los ejemplos han sido probados directamente a partir del texto, el cual est en forma legible para la mquina. Adems de demostrar cmo hacer un uso efectivo del lenguaje, donde ha sido posible, tralamos de ilus- trar algoritmos tiles y principios de buen estilo y diseo. E[ libro no es un manual de introduccin a la programacin; se supone en l familiaridad con los conceptos bsicos de programacin, como variables, propo- siciones de asignacin, ciclos y funciones. No obstante, un programador novato deber ser capaz de leer y obtener los conceptos del lenguaje, aunque le ayudara la coOperacin de un colega ms experimentado. De acuerdo con nuestra experiencia, e ha demostrado ser un lenguaje agrada- ble, expresivo y verstil para una amplia variedad de programas. Es fcil de apren- der y se obtienen mejores resultados a medida que aumenta nuestra experiencia con l. Deseamos que este libro le ayude al lector a usarlo correctamente. xi 6. xii PREFACIO A LA PRIMERA EDICION Las crticas y sugerencias de muchos amigos y colegas han aumentado much- simo los conceptos de este libro y ha sido un placer escribirlo. En particular nues- tro agradecimiento a Mike Bianchi, lim Blue, Stu Feldman, Doug Mellroy, Bill Roome, Bob Rosin y Larry Rosler que leyeron cuidadosamente las numerosas versiones. Tambin agradecemos Al Aho, Steve Bourne, Dan Dvorak, Chuck Haley, Debbie Haley, Marion Harrris, Rick Holt, Steve 10hnson, 10hn Mashey, Bob Mitze, Ralph Muha, Peter Nelson, ElIiot Pinson, Bill Plauger, lerry Spi- vack, Ken Thompson y Peter Weinberger por sus valiosos comentarios a travs de varias etapas; a Mike Lesk y loe Ossanna, por su invaluable ayuda en la im- presin. Brian W. Kernighan Dennis M. Ritchie Introduccin C es un lenguaje de programacin de propsito general que ha sido estrecha- mente asociado con el sistema UNIX en donde fue desarrollado puesto que tanto el sistema como los programas que corren en l estn escritos en lenguaje C. Sin embargo, este lenguaje no est ligado a ningn sistema operativo ni a ninguna mquina, 'i aunque se le llama "lenguaje de programacin de sistemas" debido a su utilidad para escribir compiladores y sistemas operativos, se utiliza con igual eficacia para escribir importantes programas en diversas disciplinas. Muchas de las ideas importantes de C provienen del lenguaje BCPL, desarro- llado por Martin Richards. La influencia de BCPL sobre C se continu indirecta- mente a travs del lenguaje B, el cual fue escrito por Ken Thompson en 1970 para el primer ,istema UNIX de la DEC POP-7. BCPL y B son lenguajes "carentes de tipos". En contraste, C proporciona una variedad de tipos de datos. Los tipos fundamentales son caracteres, enteros y nmeros de punto flotante de varios tamaos. Adems, existe una jerarqua de tipos de datos derivados, creados con apuntadores, arreglos, estructuras y unio- nes. Las expresiones se forman a partir de operadores y operandos; cualquier expresin, incluyendo una asignacin o una llamada a funcin, puede ser una proposicin. Los apuntadores proporcionan una aritmtica de direcciones inde- pendiente de la mquina. C proporciona las'construcciones fundamentales de control de flujo que se re- quieren en programas bien estructurados: agrupacin de proposiciones, toma de decisiones (I-else), seleccin de un caso entre un conjunto de ellos (switch), iteracin con la condicin de paro en la parte superior (while, lar) O en la parte inferior (do), y terminacin prematura de cielos (break). Las funciones pueden regresar valores de tipos bsicos, estructuras, uniones o apuntadores. Cualquier funcin puede ser llamada recursivamente. Las va- riables locales son normalmente "automticas", o creadas de nuevo con cada in- vocacin. la definicin de una funcin no puede estar anidada, pero las variables Pueden estar deelaradas en una modalidad estructurada por bloques. las funcio- nes de un programa en C pueden existir en archivos fuente separados, que se com- PIlan de manera separada. Las variables pueden ser internas a una funcin, externas pero conocidas slo dentro de un archivo fuente, o visibles al programa Completo. 7. 2 INTRODUCCION Un paso de preprocesamiento realiza substitucin de macros en el texto del programa, inclusin de otros archivos fuente y compilacin condicional. e es un lenguaje de relativo "bajo nivel H . Esta caracterizacin no es peyora- tiva, simplemente significa que e trata con el mismo tipo de objetos que la mayo- ra de las computadoras, llmense caracteres, nmeros y direcciones. Estos pueden ser combinados Ycambiados de sitio con los operadores aritmticos y l- gicos implantados por mquinas reales. eno proporciona operaciones para tratar directamente con objetos compues- tos, tales como cadenas de caractere~, conjuntos, li~tas o arrcglo~. No existen ope- raciones que manipulen un arreglo o ulla cadena complea, aunque las estructuras pueden copiarse como una unidad. El lenguaje no define ninguna facilidad para asignacin de almacenamiento que no sea la de definicin esttica y la disciplina de pilas provista por las variables locales de funciones; no emplea heap ni re- colector de basura. Finalmente, e en si mismo no proporciona capacidades de en- trada/salida; no hay proposiciones READ o WRITE, ni mtodos propios de acceso a archivos. Todos esos mecanismos de alto nivel deben ser proporcionados por funciones llamadas explcitamente. De manera semejante, e solamente ofrece un control de flujo franco, y lineal: condiciones, ciclos, agrupamientos y subprogramas, pero no multiprogramacin, operaciones paralcla~, ~incronizacin ni cor-rutinas. Aunque la ausencia de alguna de esas capacidades puede parecer como una grave deficiencia (" significa que se tiene que llamar a una funcin para compa- rar dos cadenas de caracteres?"), el mantener al lenguaje de un tamao modesto tiene beneficios reales. Puesto que e es relativamente pequeo, se puede describir en un pequeo c.,)J1acio y arrenderse con rapidez. Un programador puede razo- nablemente e~Jlerar conocer, cntender y utilizar en verdad la totalidad del lenguaje. Por muchos aos, la definicin de e fue el manual de referencia de la primera edicin de El lenguaje de pr()~ram(/cin C. En 1983, el American Nalionol Slal/- dards InslifUle (ANSI) estableci un comite para proporcionar una moderna y comprensible definicin de C. La definicin resultante, el estndar ANSI o "ANSI e", se esperaba fuera aprobada a fines de 1988. La mayora de las carac- tersticas del estndar ya se encuentran soportadas por compiladores modernos. El estndar est basado en el manual de referencia original. El lenguaje ha cambiado relativamente poco; uno de los propsitos del estndar fue asegu"r que la mayora de los programas existentes pudiesen permanecer vlidos o, al me- nos, que los compiladores pudieran producir mensajes de advertencia acerca del nuevo comportamiento. Para la mayora de los programadores, el cambio ms importante es una nue- va sintaxis para declarar y definir funciones. Una declaracin de funcin ahora puede incluir una descripcin de los argumentos de la funcin; la sintaxis de la definicin cambia para coincidir. Esta informacin extra permite que los compi- ladores detecten ms fcilmente los errores causado? por argumentos que no coin- ciden; de acuerdo con nuestra experiencia, es una adicin muy til al lenguaje. EL LENGUAJE DE PROGRAMACION e 3 Existen otros cambios de menor escala en el lenguaje. La asignacin de estruc- turas y enumeraciones, que ha estado ampliamente disponible, es ahora parte oficial del lenguaje. Los clculos de punto Ootante pueden ahora realizarse con precisin sencilla. Las propiedades de la aritmtica, especialmente para tipos sin signo, estn esclarecidas. El preprocesador es ms elaborado. La mayor parte de esas cambios slo tendrn efectos secundarios para la mayora de los progra~ madores. Una segunda contribucin significativa dei estndar es la definicin de una bi- blioteca que acompae a C. Esta especifica funciones para tener acceso al sistema operativo (por ejemplo, leer de archivos y escribir en ellos), entrada y salida con formato, asignacin de memoria, manipulacin de cadenas y otras actividades se~ mejantes. Una coleccin de encabczadores (headers) estndar proporcionan un acceso uniforme a las declaraciones de funciones y tipos de datos. Los programas que utilizan esta biblioteca para interaclUar con un sistema anfitrin estn asegu- rados de un comportamiento compatible. La mayor parte de la biblioteca esta estrechamente modelada con base en la "biblioteca E/S estillldar" del si" cma UNIX. Esta biblioteca se describi en la primera edicin y ha sido tambin ampliamente utilizada en 'otros sistemas. De nuevo, la mayora de los prograll1a~ dores no notarn mucho el cambio. Debido a que los tipos de datos y estructuras de control provistas por e son manejadas directamente por la mayora de las computadoras, la biblioteca de ejecucin (run-lime) requerida para implantar programas autocontenido.~ C pc~ quena. Las funciones de la biblioteca estndar nicamente se llaman en forma ex- plicita, de manera que se pueden evitar cuando no se necesitan. La mayor parte puede escribirse en e, y excepto por detalles ocultos del sistema operativo, ellas mismas son porttiles. Aunque e coincide con las capacidades de muchas computadoras, es indepen- diente de cualquier arquitectura. Con un poco de cuidado es fcil escribir progra- mas porttiles, esto es, programas que puedan correr sin cambios en una variedad de mquinas. El estndar explica los problemas dc la transportabilidad, y pres- cribe un conjunto de constantes que caracterizan a la mquina en la quc ,>c ejecu- ta el programa. e no es un lenguaje fuertemente tipincado, sino que, al evolucionar, u vcrifi. cacin de tipos ha sido reforzada. La definicin original de e desaprob, pero permiti, el intercambio de apuntadores y enteros; esto se ha eliminado y el estn- dar ahora requiere la adecuada declaracin y la conversin explcita que ya ha sido obligada por los buenos compiladores. La nueva declaracin de funciones es otro paso en esta direccin. Los compiladores advertirn de la mayora de los errores de tipo, y no hay conversin automtica de tipos de datos incompatibles. Sin embargo, e mantiene la filosofa bsica de que los programadores saben lo que estn haciendo; slo requiere que eSlablezcan ,-us intencionc,> en forma explcita. Como cualquier otro lenguaje, e tiene sus defectos. Algunos de los operado- res tIenen la precedencia equivocada; algunos elementos de la sintaxis pueden ser 8. 4 INTRODUCCION mejores. A pesar de todo, e ha probado ser un lenguaje extremadamente efectivo y expresivo para una amplia variedad de programas de aplicacin. El libro est organizado como sigue. El captulo 1 es una introduccin orien- tada a la parte central de C. El propsito es hacer que el lector se inicie tan pronto como le sea posible, puesto que creemos firmemente que la forma de aprender un nuevo lenguaje es escribir programas en l. La introduccin supone un conoci- miento prctico de los elementos bsicos de la programacin; no hay una explica- cin de computadoras, de compilacin, ni del significado de una expresin como n = n + 1. Aunque hemos tratado de mostrar tcnicas tiles de programacin en donde fue posible, la intencin del libro no es la de ser un texto de consulta sobre estructuras de datos y algoritmos; cuando nos vimos forzados a hacer una elec- cin, nos hemos concentrado en el lenguaje. En los captulos del 2 al 6 se discuten varios aspectos de C en mayor detalle y ms formalmente de lo que se hace en el capitulo 1, aunque el nfasis est an en los ejemplos de programas completos, ms que en fragmentos aislados. El ca- ptulo 2 trata de los tipos bsicos de datos, operaciones y expresiones. El captulo 3 trata sobre control de flujo: il-else, switch, while, for, etc. En el captulo 4 se cubren funciones y la estructura de un programa -variables externas, reglas de alcance, archivos fuente mltiples y otros aspectos- y tambin abarca al pre- procesador. El captulo 5 discute sobre apuntadores y aritmtica de direcciones. El captulo 6 cubre estructuras y uniones. El captulo 7 describe la biblioteca estndar, la cual proporciona una interfaz comn con el sistema operativo. Esta biblioteca est definida por el estndar ANSI y se intenla que se tenga en todas las mquinas que manejan C; as, los pro- gramas que la usen para entrada, salida y otros accesos al sistema operativo se puedan transportar de un sistema a otro sin cambios. El capilulo 8 describe una interfaz enlre los programas en C y el sislema ope- rativo UNIX, concentrndose en entrada/salida, el sistema de archivos y la asig- nacin de memoria. Aunque algo de este captulo es especfico de sistemas UNIX, los programadores que usen otros sistemas de todas maneras encontrarn aqu material de utilidad, incluyendo alguna comprensin acerca de cmo est implan- tada una versin de la biblioteca estndar, as como sugerencias para obtener un cdigo porttil. El apndice A contiene un manual de consulta del lenguaje. El informe oficial de la sintaxis y la semntica de C es en s el estndar ANSI. Ese documento, sin embargo, est principalmente pensado para quienes escriben compiladores. El manual de consulta de eSle libro transmite la definicin del lenguaje en una forma ms concisa y sin el mismo estilo legalista. El apndice B es un resumen de la bi- blioteca estndar, de nuevo ms para usuarios que para implantadores. El apn- dice C es un breve resumen de los cambios del lenguaje original. Aunque, en caso de duda, el estndar y el compilador en uso quedan como las autoridades finales sobre el lenguaje. CAPITULO 1 Introduccin general Cl)IllL'IlL'L'1ll0'" COIl ulla illlrodul'l'ill r{pida a C. NUI.?'~lrll ohjL'lil.1 l':- I1Hhlrar lo.., l'k'lIlL'11 I0:-' e~el1L'iak:- del lenguajL' ell prograll1a~ reale...,. pero :-.in p('rdL'l"I1o~ en detalles, reglas o excepciones. Por el momento, no intentamos ser completos ni precisos (exceptuando en los ejemplos, que s lo son). Deseamos llevarlo tan rpido como sea posible al punto en donde pueda escribir programas tiles, y para hacerlo tenemos que concentrarnos en las bases: variables y constantes, arit- mtica, control de flujo, funciones y los rudimentos de entrada y salida. Hemos dejado intencionalmente fuera de este captulo las caractersticas de e que son importantes para escribir programas ms grandes. Esas caractersticas incluyen apuntadores, estructuras, la mayor parte del rico conjunro de operadores de e, varias proposiciones para control de flujo y la biblioteca estndar. Este enfoque tiene sus inconvenientes. Lo ms notorio es que aqu no se en- l'Ul'l1lra la descripcin completa dc ninguna c:lraL'tL'ri:-.lil'a panil'lllar dL'lll'ngllajl'. y la introduccin, por su brevedad, puede tambin ser confusa. Y debido a que los ejemplos no utilizan la potencia completa de e, no son tan concisos y elegan- tes como podran serlo. Hemos tratado de aminorar esos efectos, pero tenga cui- dado. Otro inconveniente es que los captulos posteriores necesariamente repeti- rII algu de lo expue~(o en Sle. Esperamo:-. qUl' la rL'pcliL'''."lll. Ill:-' (JUl' Illolc...,ar. ayude. En cualquier caso, los programadores con experiencia deben ser capaces de extrapolar del material que se encuentra en este captulo a sus propias necesidades de programacin. Los principiantes deben complementarlo escribiendo pequeflos programas semejantes a los aqu expuestos. Ambos grupos pueden utilizar este captulo como un marco de referencia sobre el cual asociar las descripciones ms detalladas que comienzan en el captulo 2. 1.1 Comencemos La nica forma de aprender un lluevo lenguaje de programacin es escribien- do programas con l. El primer programa por escribir es el mismo para todos los lenguajes: Imprima las palabras hola, mundo 5 9. lNTRODUCCION GENERAL CAPITULO I Este es el gran obstculo; para librarlo debe tener la habilidad de crear el texto del programa de alguna manera, compilarlo con xito, cargarlo, ejecutarlo y des- cubrir a dnde fue la salida. Con el dominio de eslOs dClalles mecnicos, ludo lo dems es relativamente fcil. En C, el programa para escribir "hola, mundo" es #include main( ) { printf("hola, mundon"); La forma de ejecutar este programa depende del sistema que se est utilizan- do. Como un ejemplo especfico, en el sistema operativo UNIX se debe crear el programa en un archivo cuyo nombre termine con" .c", como hola.c, y despus compilarlo con la orden ce hola.c Si no se ha cometido algn error, como la omisin de un carcter o escribir algo en forma incorrecta, la compilacin se har sin emitir mensaje alguno, y crear un archivo ejecutable llamado a.out. Si se ejecuta a.out escribiendo la orden a.out se cscri bir hola, mundo En otros sistemas, las reglas sern diferentes, consltelo con un experto. Ahora algunas explicaciones acerca del programa en s. Un programa en C, cualquiera que sea su tamao, consta defunciones y variables . Una funcin con- tiene proposiciones que especifican las operaciones de clculo que se van a reali- zar, y las variables almacenan los valores utilizados durante los clculos. Las fun- ciones de e son semejantes a las subrutinas y funciones de Fortran o a los procedimientos y funciones de Pascal. Nuestro ejemplo es una funcin llamada main. Normalmente se tiene la' libertad de dar cualquier nombre que se desee, pero "main" es especial -el programa comienza a ejecutarse al principio de Inain. Esto significa que todo programa debe tener un Inain en aJgn sitio. Por lo comn Inain llamar a otras funciones que ayuden a realizar su traba- jo, algunas que usted ya escribi, y otras de bibliotecas escritas previamente. La primera lnea del programa. #include < stdio.h > indica al compilador que debe incluir informacin acerca de la biblioteca estn- dar de entrada/salida; esta lnea aparece al principio de muchos archivos fuente de C. La biblioteca estndar est descrita en el captulo 7 y en el apndice B. SECCION 1.1 COIE NCHIOS 7 Un mtodo para comunicar dalOs entre las funciones es que la funcin que llama proporciona una lista de valores, llamados argumentos, a id funcin que est invocando. Los parntesis que estn de'spus del nombre de la funcin encierran a la lista de argumentos. En este ejemplo , main est definido para ser una funcin que no espera argumentos, lo cual est indicado por la lista vaca ( ). # inelude main( ) incluye informacin acerca de la biblio/eea es/l1dar define una funcin l/amada main que 170 recibe valores de argumen/os las proposiciones de main estn encerradas entre l/aves printf("hola, mundon"); main llama a la funcin de biblioteca printL para escribir esta secuencia de carae/eres; n represen/a el carcter nueva I/nea El primer programa en e Las proposiciones de una funcin estn encerradas entre llaves { }. La fun- cin main slo contiene una proposicin, printf ("hola, mundon"); Una funcin se invoca al nombrarla, seguida de una liSIa dc argumentos entrl.' pa- rntesis; de esta manera se est llamando a la funcin printf con el argumento "hola, Inundon". printf es una -undn de bibliott.'Gl qUL' L'~'-'ribl..' la ..,alida, L'1l este caso la cadena de caracteres que se encuentra entre comillas. A una secuencia de caracteres entre comillas, como "hola, mundon", se le llama carlcl/a de C(frac/eres o ('o"st(fl/te de cadel/a. Por d 11101lll'111o, 1l1lL"lll IIl1iLl uso de cadenas de caracteres ser como argumentos para printf y otras funciones. La secuencia n en la cadena representa el carcter nueva linea en la nOfacip de e, y hace avanzar la impresin al margen izquierdo de la siguiente lnea. SI se omite el n (un experimento que vale la pena), encontrar que no hay avance de lnea despus de la impresin. Se debe utilizar n para incluir un carcter nue~ va lnea en el argumento de printf; si se intenta algo como printf ("hola, mundo "); el Compilador de e producir un mensaje de error. 10. 8 INTRODUCCION GENERAL CAPITULO I printf nunca proporciona una nueva lnea automticamente, de manera que se pueden utilizar varias llamadas para const ruir una lnea de salida en etapas. Nuestro primer programa tambin pudo haber sido escrito de la siguiente manera. #include < stdio.h > main( ) { printf("hola,"); printf("mundo"); printf(" n"); ,producindose una salida idntica. Ntese que n representa un solo carcter. Una secuencia de escape como n proporciona un mecanismo general y extensible para representar caracteres invisi- bles o difciles de escribir. Entre otros que C proporciona estn t para tabula- cin, b para retroceso, " para comillas, y para la diagonal invertida. Hay una lista completa en la seccin 2.3. Ejercicio 1-1_ Ejecute el programa "hola, mundo" en su sistema. Experimente con la omisin de partes del programa, para ver qu mensajes de error se obtienen. O Ejercicio 1-2_ Experimente el descubrir qu pasa cuando la cadena del argumento de printf contiene c, en donde e es algn carcter no puesto en lista anterior- mente. O 1_2 Variables y expresiones aritmticas El siguiente programa utiliza la frmula 0(7 = (5/9) (0F-32) para imprimir la siguiente tabla de temperaturas Fahrenheit y sus equivalentes centgrados o Celsius: o - 17 20 -6 40 4 60 15 80 26 iOO 37 120 48 140 60 160 71 180 82 200 93 220 104 SECCION 1.2 240 115 260 126 280 137 300 148 VAR IABLES Y EXPRESIONES ARITMETICAS 9 En s el programa an consiste de la definicin de una nica funcin llamada main. Es ms largo que el que imprime "hola, mundo", pero no es complicado. Introduce varias ideas nuevas, incluyendo comentarios, declaraciones, variables, expresiones aritmticas, ciclos y salida con formato. #include < stdio.h > / * imprime la tabla Fahrenheit-Celsius para fahr = O, 20, ... , 300 . j main ( ) int fahr, celsius; int lower, upper, step; lower = O; upper = 300; step ~ 20; fahr = lower; / . lmite inferior de la tabla de temperaturas */ / . lmite superior . / / * tamao del incremento ./ while (fahr < = upper) { Las dos lneas celsius = 5 (fahr- 32) j 9; prinU(,'%dt%dn", fahr, celsius); fahr = fahr + step; / . imprime la tabla Fahrenheit-Celsius para fahr = O, 20, ... , 300 . j SOn un comentario, que en este caso explica brevemente lo que hace el programa. Cualesquier caracleres entre /. y . / son ignorados por el compilador, y pueden ser utilizados libremente para hacer a un programa ms fcil de entender. Los co- mentarios pueden aparecer en cualquier lugar donde puede colocarse un espacio en blanco, un tabulador o nueva lnea. En C, se deben declarar todas las variables antes de su uso, generalmente al principio de la funcin y antes de cualquier proposicin ejecutable. Una declara- cin notifica las propiedades de una variable; consta de un nombre de tipo y una liSIa de variables, como int fahr, celsius; int lower, upper, step; 11. 10 INTRODUCCION GENERAL CAPITULO I El tipo int significa que las variables de la lis!a son enteros, en contraste con float, que significa punto flotante, esto es, nmeros que pueden tener una parte fraccio- naria. El rango tanto de int como de float depende de la mquina que se est utili- zando; los inl de 16 bits, que estn comprendidos entre el -32768 y + 32767, son comUJ1c,,>, como lo son losintde 32 bits. Un numero float tpicamente es de 32 bil~, por lo menos con seis dgitos significativos y una magnitud generalmente entre 10-" y lO -l8 Adems de inl y float, C proporciona varios tipos de datos bsicos, incluyendo: char short long double carcter -un solo hVTe entero corto entero largo punto notante de doble precisin Los tamaos de estos objetos tambin dependen de la mquina. Tambin existen arreglos, estructuras y uniones de estos tipos bsicos, apuntadores a ellos y funciones que regresan valores con esos tipos, todo lo cual se ver en el momento oportuno. Los clculos en el programa de conversin de temperaturas principian con las proposiciones de asignacin. lower = O; upper = 300; step = 20; fahr = lower; que asignan a las variables sus valores iniciales. Las proposiciones individuales se terminan con punto y coma. Cada linea de la tabla se calcula de la misma manera por lo que se utiliza una iteracin que se repite una vez por cada lnea de salida; este es el propsito del ciclo while while (fahr < = upper) { El c'c1o while funciona de la siguiente manera: se prueba la condicin entre pa- rntesis . De ser verdadera (fahr es menor o igual que upper), el cuerpo del ciclo (las tres proposiciones entre llaves) se ejecuta. Luego la condicin se prueba nue- vamente, y si es verdadera, el cuerpo se ejecuta de nuevo. Cuando la prueba resul- ta falsa (fahr excede a upper) la iteracin termina, y la ejecucin contina en la proposicin que sigue al ciclo. No existe ninguna otra proposicin en este progra- ma, de modo que termina. El cuerpo de un while puede tener una o ms proposiciones encerradas entre naves, como en el convertidor de temperaturas, o una sola proposicin sin llaves, como en while (i < j) i = 2 i; SECCION 1.2 VARIABLES Y EXPRESIONES ARITMETICAS JI En cualquier caso, siempre se sangra la proposicin controlada por el while con una tabulacin (lo que se ha mostrado con cuatro espacios) par poder apreciar de un vistazo cules proposiciones estn dentro del ciclo. El sangrado acenta la estructura lgica del programa. Aunque a los compiladores de C no les importa la apariencia del programa, un sang.rado y espaL'i a mi~nl o adecuado~ ~on Illuy im- portantes para hacer programas fciles de leer. Se recomienda escribir una sola proposicin por lnea y utilizar espacios en blanco alrededor de los operadores para dar claridad al agrupamiento. La posicin de las llaves es menos importante, aunque la gente mantiene creencias apasionadas. Se eligi uno de los varios esti- los populares. Seleccione un estilo que le satisfaga y selo en forma consistente. La mayor parte del trabajo se realiza en el cuerpo del ciclo. La temperatura Celsius se calcula y se asigna a la variable celsius por la proposicin. celsius = 5 (fahr-32) I 9; La razn de multiplicar por 5 y despus dividir entre 9 en lugar de solamente multiplicar por 5/9 es que en C, como en muchos otros lenguajes, la divisin de enleros {runca el resultado: cualquier parte fraccionaria se descarta. Puesto que 5 y 9 son enleros, 5/9 sera truncado a cero y as todas las temperaturas Celsius se reportaran como cero. Este ejemplo tambin muestra un poco ms acerca de cmo funciona printL En realidad, printf es una funcin de propsito general para dar formato de sali- da, que se describir con detalle en el captulo 7. Su primer argumento es una ca- dena de caracteres que sern impresos, con cada % indicando en donde uno de los otros (segundo, tercero, ...) argumentos va a ser sustituido, y en qu forma ser impreso. Por ejemplo, %d especifica un argumento entero, de modo que la proposicin printf("%dt%dn", fahr, eelsius); hace que los valores de los dos enteros fahr y eelsius sean escritos, con una tabu- lacin (1) entre ellos. Cada construccin % en el primer argumento de printf est asociada con el correspondiente segundo argumento, tercero, etc., y deben corresponder apro- piadamente en nmero y tipo, o se tendrn soluciones in(:orrectas. Con relacin a esto, printf no es parte del lenguaje C; no existe propiamente una entrada o salida definida en C. printf es slo una til funcin de la biblioteca estndar de funciones que est accesible normalmente a los programas en C. Sin embargo, el comportamiento de printf est definido en el estndar ANSI , por lo que sus propiedades deben ser las mismas en cualquier compilador o biblioteca que se apegue a l. Para concentrarnos en e, no hablaremos mucho acerca de la entrada y la sali- da hasta el captulo 7. En particular, pospondremos el tema de la entrada con for- mato hasta emonces. Si se tiene que darle entrada a nmeros, lase la discusi n de la fUIH.:in seanf en la seccin 7.4. La funcin seanf es como printf, exceptuan- do que lee de la entrada en lugar de escribir a la salida. 12. 12 INTRODUCC10N GENERAL CAPITULO 1 Existen un par de problemas con el programa de conversin de temperaturas. El ms simple es que la salida no es muy esttica debido a que los nmeros no estn justificados hacia su derecha. Esto es fcil de corregir; si aumentamos a cada %d de la proposicin printf una amplitud. los numeras impresos sern jus- tificados hacia su derecha dentro de sus campos. Por ejemplo, podra decirse printf("%3d %6dn", fahr, celsius); para escribir el primer nmero de cada lnea en un campo de tres dgitos de an- cho, y el segundo en un campo de seis digitos, como esto: o -17 20 -6 40 4 60 15 80 26 100 37 El problema ms grave es que debido a que se ha utilizado aritmtica de en- teros, las temperaturas Celsius no son muy precisas; por ejemplo, OF es en rea- lidad aproximadamente _17.8C, no -17. Para obtener soluciones ms preci- sas, se debe utilizar aritmtica de punto flotante en lugar de entera. Esto requiere de algunos cambios en el programa. Aqu est una segunda versin: # include < stdio.h > l. imprime la tabla Fahrenheit-Celsius para fahr = O, 20, ... , 300; versin de punto flotante .1 main( ) { float fahr, celsius; int lower, upper, step; lower = O; upper ~ 300; step ~ 20; fahr = lower; l. lmite superior de la tabla de temperaturas .1 l. lmite superior .1 /. tamao del incremento . 1 while (fahr < ~ upper) { celsius ~ (5.0/9.0) (fahr-32.0); printf("%3.0f %6.lfn", fahr, celsius); fahr = fahr + step; SECCION 1.2 VARIABLES Y EXPRESIONES AR1TMETICA$ 13 Esto es muy semejante a lo anterior, excepto que fahr y celsius estn declara- dos como float. y la frmula de conversin est escrita en una forma ms natural. No pudimos utilizar 5/9 en la versin anterior debido a que la divisin entera lo truncara a cero. Sin embargo, un punto decimal en una constante indica que sta es de punto flotante, por lo que 5.0/9.0 no se trunca debido a que es una relacin de dos valores de punto flotante. Si un operador aritmtico tiene operandos enteros, se ejecuta una operacin entera. Si un operador numrico tiene un operando de punto flotante y otro ente- ro, este limo ser convertido a punto flotante antes de hacer la operacin. Si se hubiera escrito fahr - 32, el 32 sera convertido automticamente a punto flotante. Escribir constantes de punto flotante con puntos decimales explcitos, aun cuando tengan valores enteros, destaca su naturaleza de punto flotante para los lectores humanos. Las reglas detalladas de cundo los enteros se convierten a punto flotante se encuentran en el capitulo 2. Por ahora, ntese que la asignacin fahr lower; y la prueba while (fahr < ~ upper) tambin trabajan en la forma natural -el in! se convierte a flaa! antes de efec- tuarse la operacin. La especificacin de conversin %3.01 del printf indica que se escribir un nmero de punto flotante (en este caso lahr) por lo menos con tres caracteres de ancho, sin punto decimal y sin dgitos fraccionarios; %6.lf describe a otro nmero (celsius) que se escribir en una amplitud de por lo menos 6 caracteres, con 1 dgito despus del punto decimal. La salida se ver como sigue: o -17.8 20 -6.7 40 4.4 La amplitud y la precisin pueden omitirse de una especificacin: %6f indica que el nmero es por lo menos de seis caracteres de ancho; %.2f indica dos caracteres despus del punto decimal , pero el ancho no est restringido; y %1 nicamente indica escribir el nmero como punto flotante. %d escribe como entero decimal %6d escribe como entero decimal, por lo menos con 6 caracteres de amplitud %f escribe como punto notante %6f escribe como punto notante, por lo menos con 6 carac[er.es de amplitud 13. 14 INTRODUCCION GENERAL CAPITULO 1 %.21 escribe como punto Ootante, con 2 caracteres despus del punto decimal %6.21 escribe como punto flotante, por lo menos con 6 caracteres de ancho y 2 despus del punto decimal Entre otros, printf tambin reconoce %0 para octal, %x para hexadecimal, %c para carcter, %5 para cadena de caracteres y % % para % en s. Ejercicio 13. Modifique el programa de conversin de temperaturas de modo que escriba un encabezado sobre la tabla. D Ejercicio ]4. Escriba un programa que imprima la tabla correspondiente Celsius a Fahrenheit. O 1.3 La proposicin tor Existen suficientes formas distintas de escribir un programa para una tarea en particular. Intentemos una variacin del programa de conversin de temperaturas. #include < stdio.h> / . imprime la tabla Fahrenheit-Celsius ./ main( ) { int fahr; lar (Iahr = O; lahr < = 300; lahr = lahr + 20) printl("'%3d %6.lUn", lahr, (5.0/9.0)(lahr- 32)); Este produce los mismos resultados. pero ciertamente se ve diferente. Un cambio importante es la eliminacin de la mayora de las variables; slo permanece fahr y la hemos hecho in!. Los lmites inferior y superior y el tamao del avance slo aparecen como constantes dentro de la proposicin for, que es una nueva construccin, y la expresin que calcula la temperatura Celsius ahora aparece como el tercer argumento de printf en vez de una proposicin de asignacin sepa- rada. Este ltimo cambio ejemplifica una regla general -en cualquier contexto en el que se permita ulilil.ar el valor de una variable de algn tipo, c!-, posible usar una expresin ms complicada de ese tipo. Puesto que el tercer argumento de printf debe ser un valor de punto Ootante para coincidir con %6.11, cualquier ex presin de punto flotante puede estar all. La proposicin for es un ciclo, una forma generalizada del while. Si se compara con el while anterior, su operacin debe ser clara. Dentro de los parntesis existen tres secciones, separadas por punto y coma. La primera, la inicializacin lahr = O SECCION 1.4 CONSTANTES 51MBOLlCAS 15 se ejecuta una vez, antes de entrar propiamente al ciclo. La segunda seccin es la condicin o prueba que controla el ciclo: ahr < = 300 Esta condicin !)e evala; si es verdadera, el cuerpo del ciclo (en este caso un sim- ple printf) ,e ejecuta. Despus el incremento de avance lahr = ahr + 20 se ejecuta y la condicin se vuelve a evaluar. El ciclo termina si la condicin se hace falsa. Tal como con el while, el cuerpo del ciclo puede ser una proposicin sencilla o un grupo de proposiciones encerradas entre llaves. La inicializacin, la condicin y el incremento pueden ser cualquier expresin. La seleccin entre while y for es arbitraria, y se basa en aquello que parezca ms claro. El for es por lo general apropiado para ciclos en los que la inicializa- cin yel incremento son proposiciones sencillas y lgicamente relacionadas, pues- to que es ms compacto que el while y mantiene reunidas en un lugar a las propo- siciones que controlan al ciclo. Ejercicio ]5. Modifique el programa de conversin de temperaturas de manera que escriba la tabla en orden inverso, esto es, desde 300 grados hasta O. D 1.4 Constantes simblicas Una observacin final antes de dejar definitivamente el tema de la conversin de temperaturas. Es una mala prctica poner "nmeros mgicos" como 300 y 20 en un programa, ya que proporcionan muy poca informacin a quien tenga que leer el programa, y son difciles de modificar en un forma sistemtica, Una mane- ra de tratar a esos nmeros mgicos es darles nombres significativos. Una lnea #define define un nombre simblico o constante simblica como una cadena de caracteres especial: #define I/ombre lexlO de reelllp!o::u A partir de esto, cualquier ocurrencia de nombre (que no est entre comillas ni como parte de otro nombre) se sustituir por el texto de reemplazo correspon- diente. El nombre tiene la misma forma que un nombre de variable: una secuen- cia de letras y dgitosque comienza con una letra. El lex/O de reemplazo puede ser cualquier secuencia de caracteres; no est limitado a nmeros. #include < stdio.h > #dehne #define #define LOWER O UPPER 300 STEP 20 /. lmite inferior de la tabla * / /. limite superior .1 / * tamao del incremento . / 14. 16 INTRODUCCION GENERAL / . imprime la tabla Fahrenheit-Celsius / main( ) { int fahr; Jor (Jahr = LOWER; Jahr < = UPPER; fahr = fahr + STEP) printf("%3d %6.H n", fahr, (5.0/9.0). (fahr-32)); CAPrTUI O I Las cantidades LOWER, UPPER y STEP son constantes simblicas, no variables, por lo que no aparecen entre las declaraciones. Los nombres de constantes simblicas, por convencin, se escriben con letras maysculas, de modo que se puedan distinguir fcilmente de los nombres de variables escritos con minsculas. Ntese que no hay punto y coma al final de una lnea #deline. 1.5 Entrada y salida de caracteres Ahora vamos a considerar una familia de programas relacionados para el pro- cesamiento de datos de tipo carcter. Se encontrar que muchos programas slo son versiones ampliadas de los prototipos que se tratan aqu;. El modelo de entrada y salida manejado por la biblioteca c~lnrlar es muy simple. La entrada y salida de texto, sin importar dnde fue originada o hacia donde se dirige, se tratan como flujos (streollls ) de caracteres. Un fllljo de texto es una secuencia de caracteres divididos entre lneas, cada una de las cuales consta de cero o ms caracteres seguidos de un carcter nueva linea. La biblioteca es responsable de hacer que cada secuencia de entrada o salida est de acuerdo con este modelo; el programador de C que utiliza la biblioteca no necesita preocuparse de cmo estn representadas las lneas fuera del programa. La biblioteca estndar proporciona varias funciones para leer o escribir un carcter a la vez, de las cuales ge!ehar y pu!ehar son las ms simples. Cada vez que se invoca, getchar Ice el siguiente carcter de en/rada de una secuencia de tex- to y lo devuelve como su valor. Esto es, despus de e = getchar() la variable e contiene el siguiente carcter de entrada. Los caracteres provienen normalmente del teclado; la entrada de archivos se trata en el captulo 7. La funcin putchar escribe un carcter cada vez que se invoca: putchar(c) escribe el contenido de la variable entera c como un carcter, generalmente en la pantalla. Las llamadas a putehar y a printf pueden estar alternadas; la salida aparecer en el orden en que se realicen las llamadas. SECCION 1.5 ENTRADA Y SALIDA DE CARACTERES 17 1.5.1 Copia de archivos Con getehar y putehar se puede escribir una cantidad sorprendente de cdigo til sin saber nada ms acerca de entrada y salida. El ejemplo ms sencillo es un programa que copia la entrada en la salida, un carcter a la vez: lee un carcter while (carcter no es indicador de fin de archivo) manda a la salida el carcter recin leMo lee un carcter Al convertir esto en C se obtiene #include / . copia la entrada a la salida; la. versin . / m.in( ) { int c; e = getchar( ); while (e != EOF) { putch.r(e); e = getchar( ); El operador de relacin! = significa "no igual a" . Lo que aparece como un carcter en el teclado o en la pantalla es, por supues- to, como cualquier otra cosa, almacenado internamente como un patrn de bits. El tipo ehar tiene la funcin especfica de almacenar ese tipo de dato, pero tam- bin puede ser usado cualquier tipo de entero. Usamos in! por una sutil pero im- portante razn. El problema es distinguir el fin de la entrada de los datos vlidos. La solucin es que getehr devuelve un valor distintivo cuando no hay ms a la entrada, un valor que no puede ser confundido con ningn otro carcter. Este valor se llama EOF, por "end af file (fin de archivo)". Se debe declarar e con un tipo que sea lo suficientemente grande para almacenar cualquier valor que le regrese get- charo No se puede utilizar char puesto que e debe ser suficientemente grande como para mantener a EOF adems de cualquier otro carcter. Por lo tanto, se emplea in!. EOF es un entero definido en , pero el valor numrico especfico no importa mientras que no sea el mismo que ningn valor tipo chaL Utilizando la constante simblica, hemos asegurado que nada en el programa depende del valor numrico especfico. 15. 18 INTRODUCCION GENERAL CAPITULO 1 El programa para copiar podra escribirse de modo ms conciso por progra- madores experimentados de C. En lenguaje e, cualquier asignacin, tal como c ~ getchar( ) es una expresin y tiene un valor, el del lado izquierdo luego de la asignacin. Es- to signi fica que una asignacin puede aparecer como parte de una expre~ i n ms larga. Si la asignacin de un carcter a e se coloca dentro de la seccin de prueba de un cicld while, el programa que copia puede escribirse de la siguieme manera: #include < stdio.h> 1* copia la entrada a la salida; 2a. versin *1 main( ) { int e; while c = getchar( )) ! putchar(c); EOF) El while obtiene un carcter, lo asigna a e, y entonces prueba si el carcter fue la seal de fin de archivo. De no serlo, el cuerpo del while se ejecuta, escribiendo el carcter; luego se repite el while. Luego, cuando se alcanza el final de la entra- da, el while termina y tambin lo hace main. Esta versin centraliza la entrada -ahora hay slo una referencia a getehar- y reduce el programa. El programa resultante es ms compacto y ms fcil de leer una vez que se domina el truco. Usted ver seguido este estilo. (Sin embargo, es posible descarriarse y crear cdigo impenetrable. una tendencia que tratare- mos de reprimir.) Los parntesis que estn alrededor de la asignacin dentro de la condicin son necesarios. La precedencia de ! = es ms alta que la de = . lo que significa que en ausencia de parntesis la prueba de relacin! = se realizara antes de la asigna- cin De esta manera, la proposicin c ~ getchar( ) ! ~ EOF es equivalente a c = (getchar() r = EOF) Esto tiene el efecto indeseable de hacer que e sea Or1, dependiendo de si la lla- mada de getchar encontr fin de archivo. (En el capitulo 2 se trata este tema con ms detalle). Ejercicio 1-6. Verifique que la expresin getchar ( ) ! ~ EOF es O o l. O Ejercicio 1-7. Escriba un programa que imprima el valor de EOF. O SECCJON J.5 ENTRADA Y SALIDA DE CARACTERES 19 1.5.2 Conteo de caracteres El siguiente programa cuenta caracteres y es semejante al programa que copia. #include < stdio.h > 1 cuenta los caracteres de la entrada; la. versin *1 main( ) { long ne; ne = O; while (getchar( ) r = EOF) + +nc printf("%ldn", nc); La proposicin + +nc; presenta un nuevo operador, + +, que significa incrementa en uno. Es posible escribir ne = ne + 1, pero + +ne es ms conciso y muchas veces ms cficiente. Hay un operador .correspondiente -- para disminuir en 1. Los operadores + + y - - pueden ser tanto operadores prefijos (+ + nc) como postfijos (nc + +); esas dos formas tienen diferentes valores dentro de las expresiones, como se de- mostrar en el captulo 2, pero ambos + + nc y ne + + incrementan a ne. Por el momento adoptaremos la forma de prefijo. El programa para contar caracteres acumula su cuenta en una variable long en lugar de una int. Los enteros long son por lo menos de 32 bits. Aunque en algunas mquinas int y long son del mismo tamao, en otras un int es de 16 bits, con un valor mximo de 32767, Ytomara relativamente poca lectura a la entrada para desbordar un contador int. La especificacin de conversin %Id indica a printf que el argumento correspondiente es un entero long. Sera posible tener la capacidad de trabajar con nmeros mayores empleando un double (float de doble precisin). Tambin se utilizar una proposicin for en lugar de un while, para demostrar otra forma de escribir el ciclo. #include < stdio.h > l . cuenta los caracteres de la entrada; 2a. versin */ main( ) { double ne; for (nc = O; getchar( ) r = EOF; + + nc) printf("%.Of n", nc); 16. 20 INTRODUCCION GENERAL CAPITULO I printf utiliza %1 tanto para flcat como para double; %.01 suprime la impresin del punto decimal y de la parte fraccionaria, que es cero. El cuerpo de este ciclo lar est vaco, debido a que todo el trabajo se realiza en las secciones de prueba e incremento. Pero las reglas gramaticales de C requie- ren que una proposicin for tenga un cuerpo. El punto y coma aislado se llama proposicin nula, y est aqu para satisfacer este requisito. Lo colocamos en una linea aparte para que sea visible. Antes de abandonar el programa para contar caracteres, obsrvese que si la entrada no contiene caracteres, la prueba del while o del lor no tiene xito desde la primera llamada a getchar, y el programa produce cero, el resultado correcto. Esto es importante. Uno de los aspectos agradables acerca del while y del lar es que hacen la prueba al inicio del ciclo, antes de proceder con el cuerpo. Si no hay nada que hacer, nada se hace, aun si ello significa no pasar a travs del cuerpo del ciclo. Los programas deben actuar en forma inteligente cuando se les da una entrada de longitud cero. Las proposiciones while y lor ayudan a asegurar que los programas realizan cosas razonables con condiciones de frontera. 1.5.3 CQnteo de lineas El siguiente programa cuenta lneas a la entrada. Como se mencion anterior- mente, la biblioteca estndar asegura que una secuencia de texto de entrada pa- rezca una secuencia de lneas, cada una terminada por un carcter nueva linea. Por lo tanto, contar lneas es solamen te contar caracteres nueva lnea: #include /. cuenta las lneas de la entrada . / main( ) { int c, nI; ni = O; while c = getchar( != EOF) il (c = = 'In') + +nl; printf("%dn", ni); El cuerpo del while consiste ahora en un if, el cual a su vez controla el incre- mento + +nI. La proposicin il prueba la condicin que se encuentra entre pa- rmesis y, si la condicin es verdadera, ejecuta la proposicin (o grupo de propo- siciones entre llaves) que le sigue. Hemos sangrado nuevamente para mostrar lo que controla cada elemento. El doble signo de igualdad = = es la notacin de C para expresar "igual a" (como el = simple de Pascal o el .EQ. de Fortran). ESle simbolo se emplea para SECCION 1.5 ENTRADA YSALIDA DE CARACTERES 21 distinguir la prueba de igualdad del = simple que utiliza e para la asignacin. Un mensaje de alerta: los principiantes de e ocasionalmente escriben = cuando en realidad deben usar = = . Como se ver en el captulo 2, el resultado es por lo general una expresin legal, de modo que no se obtendr ninguna advertencia. Un carcter escrito entre apstrofos representa un valor entero igual al valor numrico del carcter en el conjunto de caracteres de la mquina. Esto se llama una constante de carcter, aunque slo es otra forma de escribir un pequeo ente- rO. As, por ejemplo'A' es una constante de carcter; en el conjunto ASCn de caracteres su valor es 65, esto es, la representacin interna del carcter A. Por su- puesto 'A' es preferible que 65: su significado es obvio, y es independiente de un conjunto de caracteres en particular. Las secuencias de escape que se utilizan en constallles de cadena tambin son le- gales en constantes de carcter; as, 'n' significa el valor del carcter nueva lnea, el cual es 10 en cdigo ASCII. Se debe notar cuidadosamente que 'n' es un carcter simple, y en expresiones es slo un entero; por otro lado,"n" es una constante cadena que contiene slo un carcter. En el captulo 2 se trata el tema de cadenas versus caracteres. Ejercicio 1-8. Escriba un programa que cuente espacios en blanco, tabuladores y nuevas lineas. O Ejercicio 1.9. Escriba un programa que copie su entrada a la salida, reemplazando cada cadena de uno o ms blancos por un solo blanco. O Ejercicio 1-10. Escriba un programa que copie su entrada a la salida, reemplazan- do cada tabulacin por 1, cada retroceso por b y cada diagonal invertida por. Esto hace que las tabulaciones y los espacios sean visibles sin confusiones. O 1.5.4 Conteo de palabras El cuarto en nuestra serie de programas tiles cuenta las lneas, palabras y ca- racteres, usando la definicin de que una palabra es cualquier secuencia de carac- teres que no contiene espacio en blanco ni tabulacin ni nueva lnea. Esta es una versin reducida del programa wc de UNIX. #include < stdio.h > #d~line IN 1 #deline OUT O j ... en una palabra . j l. fuera de una palabra . / /. cuenta lneas, palabras, y caracteres de la entrada . / main( ) { int c, nI, nw, nc, state; 17. 22 INTRODUCCION GENERAL state = OUT, nI = nw = ne = O; while ((e = getehar( != EOF) { + +ne, f (c = = 'n') + +nl; ii (e = = " JI e = = ,n' :: e state = OUT; else f (state = = OUT) { state = IN; + +nw; printf ("%d %d %dn", ni, nw, ne); CAPITULO 1 't') Cada vez que el programa encuentra el primer carcter de una palabra, conta- biliza una palabra ms. La variable state registra si actualmente el programa est o no sobre una palabra; al iniciar es "no est sobre una palabra", por lo que se asigna el valor OUT. Es preferible usar las constantes simblicas IN y OUT que los valores literales 1 y 0, porque hacen el programa ms legible. En un programa tan pequeo como ste, la diferencia es mnima, pero en programas ms grandes el incremento en claridad bien vale el esfuerzo extra que se haya realizado para escribir de esta manera desde el principio. Tambin se descubrir que es ms fcil hacer cambios extensivos en programas donde los nmeros mgicos aparecen slo como constantes simblicas. La lnea nI nw ne O; inicializa a las tres variables en cero. Este no es un caso especial sino una conse- cuencia del hecho de que una asignacin es una expresin con un valor, y que las asignaciones se asocian de derecha a izquierda. Es como si se hubiese escrito nI = (nw = (ne = O)); El operador :: significa "O" (o bien "OR"), por lo que la linea if (e = = ' , II e = = ,n' II e = = 't') dice "si e es un blanco o e es nueva lnea o e es un tabulador". (Recuerde que la secuencia de escape t es una representacin visible del carcter tabuladoL) Existe un correspondiente operador && para AND; su precedencia es ms alta que la de ::. Las expresiones conectadas por && O :: se evalan de izquierda a derecha, y se garantiza que la evaluacin terminar tan pronto como se conozca la verdad o falsedad. Si e es un blanco, no hay necesidad de probar si es una nue- va lnea o un tabulador, de modo que esas pruebas no se hacen . Esto no es de SECCION 1.6 ARREGLOS 23 panicular imponancia en este caso, pero es significativo en situaciones ms com- plicadas, como se ver ms adelante. El ejemplo muestra tambin un else, el cual especifica una accin alternativa si la condicin de una proposicin i/ es falsa. La forma general es if (expresin) proposicin 1 else proposicinz Una y slo una de las dos proposiciones asociadas con un i/else se realiza. Si la expresin es verdadera, se ejecuta proposicin 1; si no lo es, se ejecuta propo- sicin,. Cada proposicin puede ser una proposicin sencilla o varias entre lla- ves . En el programa para contar palabras, la que est despus del else es un if que controla dos proposiciones entre llaves. Ejercicio 1-11. Cno probara el programa para contar palabras? Qu clase de entrada es la ms conveniente para descubrir errores si stos existen? o Ejercicio 1-12. Escriba un programa que imprima su entrada una palabra por lnea. O 1.6 Arreglos Escribamo!-. un programa para contar el nmero ele Ol,.'lIITenLia~ de L'i.H.la digito. de caracteres espaciadores (blancos, tabuladores, nueva lnea), y de todos los otros caracteres. Esto es artificioso, pero nos permite ilustrar varios aspectos de C en un programa. Existen doce categoras de entrada, por lo que es conveniente utilizar un arre- glo para mantener el nmero de ocurrencias de cada dgito, en lugar de tener diez variables individuales. Esta es una versin del programa: #include / * cuenta dgitos, espacios blancos, y otros */ main( ) { int e, i, nwhite, nother; int ndiqitIIO]; nwhite = nother = O; for (i = O; i < !O; + +i) ndiqitli] = O; 18. 24 INTRODUCCION GENERAL while ((e = gelehar( )) ! = EOF) ti (e > = 'O' && e < = '9') + + ndigit[e-'O'); else if (c = = ' , :; c = = ,n' JI c + +nwhite; else + + nother; printf ("dgitos = "); lor (i = O; i < 10; + + i) printf(" %d", ndigit[i]); printf(", eGpacios blancos nwhite, nother); %d, otros '1') %dn", La salida de este programa al ejecutarlo sobre s mismo es digilos = 93 OO OOOOO 1, espacios blancos = 123, otros = 345 La declaracin inl ndigil [101; CAPITULO 1 declara ndigit como un arreglo de 10 enteros. En e, los subndices de arreglos comienzan en cero, por lo que los elementos son ndigit[O], ndigit [1], ... , ndi- git[9]. Esto se refleja en los ciclos lor que inicializan e imprimen el arreglo. Un subndice puede ser cualquier expresin entera, lo que incluye a variables enteras como i, y constantes enteras. Este programa en particular se basa en las propiedades de la representacin de los dgitos como caracteres. Por ejemplo, la prueba il (e > = 'O' && e < = '9') ... determina si el carcter en c es un dgito. Si lo es, el valor numrico del dgito es e - 'O' Esto slo funciona si 'O', '1', ... , '9' tienen valores consecutivos ascendentes. Por fortuna, esto es as en todos los conjuntos de caracteres. Por definicin, los char son slo pequeos enteros, por lo que las variables y las constantes char son idnticas a las int en expresiones aritmticas. Esto es natural y conveniente; por ejemplo, e-'O' es una expresin entera con un valor entre O y 9, correspondiente a los caracteres 'O' a '9' almacenados en e, por 10 que es un subndice vlido para el arreglo ndigit. La decisin de si un carcter es dgito, espacio en blanco u olra cosa se realiza con la secuencia il (e > = 'O' && e < = '9') + + ndigil[e-'O'); SECCION 1.6 else if (e = = " :: e + +nwhite; else + + nother; El patrn if (condicin l ) proposicinI else if (condicin~) proposicil11 else proposicinl/ ARREGLOS 2S 'n' :: e '1') se encuentra frecuentemente en programas como una forma de expresar una deci- sin mltiple. Las condiciones se evalan en orden desde el principio hasta que se satisface alguna condicin; en ese punto se ejecuta la proposicin correspon- dicntc. y la cOll~lrllL:ill Cllll1plcla termina. (Cualquier I'I'(J/}()S!c'hJll puede constar de varias proposiciones entre llaves.) Si no se satisface ninguna de las condiciones, se ejecuta la proposicin que est despus del else final. si sta existe. Cuando se omiten el else y la proposicin finales, tal como se hizo en el programa para COIl- lar palabras, no tiene lugar ninguna accin. Puede haber cualquier nmero de grupos de else if (condicin) proposicin entre el if inicial y el else final. Se recomienda, por estilo, escribir ~sta construccin tal como se ha mostrado; si cada if est uviesc sangrado despus del else anterior. una larga secuencia de de- cisiones podra rebasar el margen derecho de la pgina. La proposicin switch, que se tratar en el captulo 3, proporciona otra forma de escribir una decisin mltiple, que es particularmente apropiada cuando la condicin es determinar si alguna expresin entera o de carcter corresponde con algn miembro de un conjunto de constantes. Para contrastar, se presentar una versin ele este programa, usando switch, en la seccin 3.4. Ejercicio 1- J3. Escriba un programa que imprima el histograma de las longitudes de ti'. palabras de:-.u e!ltrada. E:-. fL'i dibujar el hi:-.togralll int power(nt m, int n); l . prueba la funcin power . 1 main( ) { int i; for (i = O; i < 10; + +i) printf("%d %d %dn", i, power(2,i), power(-3,i)); return O; l o. power: eleva la base a la n'3sima palencia; n > = 0.1 int power(int base, int n) { int i, p; p -= 1i for (i = 1; i < = n; + +i) P = P o. base; return p; SECCION 1.7 FUNCIONES 27 Una definicin de funcin tiene la forma siguiente: fipo-de-reforno nombre-de-funcin (declaracin de parmetros. si los hay) { declaraciones proposiciones Las definiciones de funcin pueden aparecer en cualquier orden y en uno O varios archivos fuente, pero una funcin no puede separarse en archivos diferentes. Si el programa fuente aparece en varios archivos, tal vez se tengan que especificar ms cosas al compilar y cargarlo que si estuviera en uno solo, pero eso es cosa del sistema operativo, no un atributo del lenguaje. Por ahora supondremos que ambas funciones estn en el mismo archivo y cualquier cosa que se haya aprendi- do acerca de cmo ejecutar programas en e, an funcionarn. La funcin power se invoca dos veces por main, en la lnea printf("%d %d %dn", i, power(2,i). power{-3,i)); Cada llamada pasa dos argument os a power, que cada vez regresa UIl cntcro, al quc se pone formato y se imprime. En una expresin , power(2,i) es un cl1lero tal como lo son 2 e i. (No todas las funciones producen un valor cntero; lo que se ve- r en el capitulo 4.) La primera lnea de la funcin power, int power(in base, int n) declara los tipos y nombres de los parmetros, as como el tipo de resultado que la funcin devuelve. Los nombres que emplea power para sus parmetros son lo- cales a la funcin y son invisibles a cualquier otra funcin: otras rutinas pueden utilizar los mismos nombres sin que exista problema alguno. Esto tambin es cier- to para las variables i y p: la i de power no tiene nada que ver con la i de main. Generalmente usa.remos parmetro para una variable nombrada en la lista en- tre parl1lcsis de la defi nicin de una funcin, y argumen/o para el valor emplea- do al hacer la llamada de la funcin. Los trminos argumento formal y argumen- lo real se emplean en ocasiones para hacer la misma distincin. El valor que calcula power se regresa a main por medio de la proposicin re- turn, a la cual le puede seguir cualquier expresin: return expresin Una funcin no necesita regresar un valor; una proposicin return sin expresin hace que el control regrese al programa, pero no devuelve algn valor de utilidad, como se hara al "caer al final" de una funcin al alcanzar la llave derecha de terminacin. Adems, la funcin que llama puede ignorar el valor que regresa una funcin. Probablemente haya notado que hay una proposicin return al final de main. Puesto que main es una funcin como cualquier otra, tambin puede regresar un valor a quien la invoca, que es en efecto el medio ambiente en el que el programa 20. l 28 INTRODUCCION GENERAL CAPITULO 1 se ejecuta. Tpicamente, un valor de regreso cero implica una terminacin nor- mal; los valores diferentes de cero indican condiciones de terminacin no comu- nes o errneas. Para buscar la simplicidad, se han omitido hasta ahora las propo- siciones return de las funciones main, pero se incluirn ms adelante, como un recordatorio de que los programas deben regresar su estado final a su medio am- biente. La declaracin int power(int m, int n); precisamente antes de main, indica que power es una funcin que espera dos ar- gumentos inl y regresa un int. Esta declaracin, a la cual se le llama funcin pro- totipo, debe coincidir con la definicin y uso de power. Es un error el que la defi- nicin de una funcin o cualquier uso que de ella se haga no corresponda con su prototipo. Los nombres de los parmetros no necesitan coincidir; de hecho, son optati- vos en el prototipo de una funcin, de modo que para el prototipo se pudo haber escrito int power(int, int); No obstante, unos nombres bien seleccionados son' una buena docu~entacin, por lo que se emplearn frecuentemente. Una nota histrica: La mayor modificacin entre ANSI C y las versiones ante- riores es cmo estn declaradas y definidas las funciones. En la definicin origi- nal de C, la funcin power se pudo haber escrito de la siguiente manera: /. power: eleva la base a n-sima potencia; n > = O . / /. (versin en estilo antiguo) */ power(base, n) int base, n; int i, p; p = 1; for (i = 1; i < = n; + +i) P = P * base; return p; Los parmetros se nombran entre los parntesis y sus tipos se declaran antes de abrir la llave izquierda; los parmetros que no se declaran se toman como int. (El cuerpo de la funcin es igual a la anterioL) La declaracin de power al inicio del programa pudo haberse visto como sigue: int power( ); No se permiti ninguna lista de parmetros, de modo que el compilador no pudo revisar Can facilidad que power fuera llamada correctamente. De hecho, puesto SECCION 1.8 ARGUMENTOS-LLAMADA POR VALOR 29 que por omisin se poda suponer que power regresaba un entero, toda la decla- racin podra haberse omitido. La nueva sintaxis de los prototipos de funciones permite que sea mucho ms fcil para el compilador detectar errores en el nmero o tipo de argumentos. El viejo estilo de declaracin y definicin an funciona en ANSI C, al menos por un periodo de transicin, pero se recomienda ampliamente que se utilice la nueva forma si se tiene un compilador que la maneje. Ejercicio 1-15. Escriba de nuevo el programa de conversin de temperatura de la seccin 1.2, de modo que utilice una funcin para la conversin. O 1.8 ArgumentQs-lIamadas por valor Hay un aspecto de las funciones de C que puede parecer poco familiar a los programadores acostumbrados a otros lenguajes, particularmente Fortran. En C, todos los argumentos de una funcin se pasan "por valor". Esto significa que la funcin que se invoca recibe los valores de sus argumentos en variables tempo- rales y no en las originales. Esto conduce a algunas propiedades di rerentes a las que se ven en lenguajes con "llamadas por referencia" como Fortran o con par- metros varen Pascal, en donde la rutina que se invoca tiene acceso al argumento original, 110 a una copia local. La diferencia principal es que en C la funcin que se invoca no puede alterar directamente una variable de la funcin que hace la llamada; slo puede modifi- car su copia privada y temporal. Sin embargo, la llamada por valor es una ventaja, no una desventaja. Por lo comn, esto conduce a elaborar programas ms compactos con pocas variables extraas, debido a que los parmetros se tratan en la funcin invocada como va- riables locales convenientemente inicializadas. Por ejemplo, he aqui una versin de power que utiliza esta propiedad. / * power: eleva la base a la n-sima potencia; n> =O; versin 2 ./ int power(int base, int n) int p; for (p = 1; n > O; --n) p = p base; return p; El parmetro n se utiliza como una variable temporal, y se decrementa (Ul' ciclo for que se ejecuta hacia atrs) hasta que llega a cero; ya no es necesaria la variable i. Cualquier cosa que se le haga a n dentro de power no tiene efecto sobre el argu- mento con el que se llam originalmente power. 21. 30 INTRODUCCION GENERAL CAPITULO I Cuando sea necesario, es posible hacer que una funcin modifique una va- riable dentro de una rutina invocada. La funcin que llama debe proporcionar la direccin de la variable que ser cambiada (tcnicamente un apuntador a la va- riable), y la funcin que se invoca debe declarar que el parmetro sea un apunta- dor y tenga acceso a la variable indirectamente a travs de l. Los apuntadores se tratarn en el captulo 5. La historia es diferente con los arreglos . Cuando el nombre de un arreglo se emplea como argumento, el valor que se pasa a la funcin es la localizacin o la direccin del principio del arreglo -no hay copia de los elementos del arreglo. Al colocarle subndices a este valor, la funcin puede tener acceso y alterar cual- quier elemento del arreglo. Este es el tema de la siguiente seccin. 1,9 Arreglos de caracteres El tipo de arreglo ms comn en e es el de caracteres, Para ilustrar el uso de arreglos de caracteres y funciones que los manipulan, escriba un programa que lea un conjunto de lneas de texto e imprima la de mayor longitud. El pseudoc- digo es bastante simple: while (hay otra lInea) if (es ms larga que la anterior ms larga) gurdala guarda su longitud imprime la I[nea ms larga Este pseudocdigo deja en claro que el programa se divide naturalmente en par- tes. Una trae una nueva lnea, otra la prueba y el resto controla el proceso, Puesto que la divisin de las partes es muy fina, lo correcto ser escribirlas de ese modo. As pues, escribamos primero una funcin getline para extraer la siguiente lnea de la entrada. Trataremos de hacer a la funcin til en otros con- textos, Al menos, getline tiene que regresar una seal acerca de la posibilidad de un fin de archivo; un diseo de ms utilidad deber retornar la longitud de la l- nea, o cero si se encuentra el fin de archivo. Cero es un regreso de fin de archivo aceptable debido a que nunca es una longitud de lnea vlida, Cada lnea de texto tiene al menos un carcter; incluso una lnea que slo contenga un carcter nueva lnea tiene longitud 1, Cuando se encuentre una lnea que es mayor que la anteriormente ms larga, se debe guardar en algn lugar. Esto sugiere una segunda funcin copy, para co- piar la nueva lnea a un lugar seguro. Finalmente, se necesita un programa principal para controlar getline y copy. El resultado es el siguiente: SECCION 1.9 ARREGLOSDECARAcrERES 3] #include #define MAXLINE 1000 / " tamao mximo de la lnea de entrada */ int getline(char line[J, int maxline); void eopy(ehar to[], ehar from[]); / " imprime la linea de entrada ms larga ,,/ main( ) { int len; int max; ehar line[MAXLINEJ; ehar longest[MAXLINE]; max = O; / * longitud actual de la lnea ./ / ...xima longitud vista hasta el momento ../ / .. lnea de entrada actual ../ /.. la lnea ms larga se guarda aqu */ while ((len = getline(line, MAXLINE)) > O) if (len > max) { max = len; eopy(longest, line); if (max > O) /" hubo una lnea ,,/ printf("%s", longest); return O; /" getline: lee una lnea en s, regresa su longitud int getline(char s[]. int lim) { int e, i; for (i=O; i #define MAXUNE 1000 / .. maximo tamao de una lnea de entrada .. / int max; ch.r lineIMAXLINE]; char longestlMAXUNEI; / .. mxima longitud vista hasta el momento .. / / .. lnea de entrada actual .. / / .. la linea ms larga se guarda aqu .. / int getline(void); void copy(void); / .. imprime la lnea de entrada ms larga; versin especializada"/ m.in( ) { int len; extern int max; extern char longest[]; max = O; while len = gelline( )) > O) il (len > max) { max = len; cOPY( ); il (m.x > O) / .. hubo una lnea ../ prinU("%s", longest); return O; / .. gelline: versin especializada ./ int getline(void) { int e , i; extern char line[]; for (i = O; i < MAXLlNE-l && (c = getchar( )) ! = EOF && c ! = lineli} = c; ,n'; ++i) SECCION 1.10 il (c = = 'n') { line[i] = e; + +i; lineli} = '0'; return i; / .. copy: versin especializada .. / void cOPy(void) { int i; extern char lina [], longest[]; i = O; while ((longestli} + +i; lineli]) != ,0') VARIABLES EXTERNAS Y ALCANCE 3S Las variables externas de main, gelline y copy estn definidas en las primeras lneas del ejemplo anterior, lo que establece su tipo y causa que se les asigne espa- cio de almacenamiento. Desde el punto de vista sintctico, las definiciones exter- nas son exactamente como las definiciones de variables locales, pero puesto que ocurren fuera de las funciones, las variables son externas. Antes de que una fun- cin pueda usar una variable externa, se debe hacer saber el nombre de la variable a la funcin. Una forma de hacer esto es escribir una declaracin extem dentro de la funcin; la declaracin es la misma que antes, excepto por la palabra reser- vada externo Bajo ciertas circunstancias, la declaracin exlem se puede omitir. Si la defini- cin de una variable externa ocurre dentro del archivo fuente antes de su uso por una funcin en particular, entonces no es necesario el uso de una declaracin ex- lem dentro de la funcin . La declaracin exlern en main, gelline y copy es, por lo tanto, redundante. De hecho, una prctica comn, es poner las definiciones de todas las variables externas al principio del archivo fuente y despus omitir to- das las declaraciones externo Si el programa est en varios archivos fuente y una variable se define en archi- vol y se utiliza en arch;vo2 y archivo3, entonces se necesitan declaraciones extern en archivo2 y archivo3 para conectar las ocurrencias de la variable. La prctica comn es reunir declaraciones extern de variables y funciones en un archivo sepa- rado, llamado histricamente header, que es incluido por #include al principio de cada archivo fuente. El sufijo .h se usa por convencin para nombres de hea- ders. las funciones de la biblioteca estndar, por ejemplo, estn declaradas en headers como . Este tema se trata ampliamente en el capitulo 4, y la biblioteca en el capitulo 7 y en el apendice B. 24. 36 INTRODUCCION GENERAL CAPITULO 1 Puesto que las versiones especializadas de getline y copy no tienen argumen- tos, la lgica sugerira que sus prototipos al principio del archivo deben ser getli- ne( l y copy( l. Pero por compatibilidad con programas de C anteriores, el estn- dar toma a una lista vaca como una declaracin al viejo estilo, y suspende toda revisin de listas de argumentos; para una lista explcitamente vaca debe emplearse la palabra void. Esto se discutir en el captulo 4. Se debe notar que empleamos cuidadosamente las palabras definicin y decla- racin cuando nos referimos a variables externas en esta seccin. La palabra "de- finicin" se refiere al lugar donde se crea la variable o se je asigna un lugar de almacenamiento; "declaracin" se refiere al lugar donde se establece la naturale- za de la variable pero no se le asigna espacio. A propsito, existe una tendencia a convertir todo en variables extern, debido a que aparentemente simplifica las comunicaciones -las listas de argumentos son cortas y las variables existen siempre, cuando se les requiere. Pero las variables externas existen siempre, aun cuando no hacen falta. Descansar fuertemente so- bre variables externas es peligroso, puesto que lleva a programas cuyas cone- xiones entre datos no son completamente obvias -las variables pueden cambiar- se en forma inesperada e inadvertida, y el programa es dificil de modificar. La segunda versin del programa de la lnea mayor es inferior a la primera, en parte por las anteriores razones y en parte porque destruye la generalidad de dos tiles funciones, introduciendo en ellas los nombres de las variables que manipula. Hasta este punto hemos descrito lo que podra llamarse los fundamentos con- vencionales de C. Con estos fundamentos, es posible escribir programas tiles de tamao considerable, y probablemente sera una buena idea hacer una pausa sufi- cientemente grande para realizarlos. Estos ejercicios sugieren programas de com- plejidad algo mayor que los anteriores del capitulo. Ejercicio 1-20. Escriba un programa detab que reemplace tabuladores de la en- trada con el nmero apropiado de blancos para espaciar hasta el siguiente paro de tabulacin. Considere un conjunto fijo de paros de tabulacin, digamos cada n columnas. Debe ser n una variable o un parmetro simblico? O Ejercicio 1-21. Escriba un programa entab que reemplace cadenas de blancos por el mnimo nmero de tabuladores y blancos para obtener el mismo espaciado. Considere los paros de tabulacin de igual manera que para detab. Cuando un tabulador o un simple espacio en blanco fuese suficiente para alcanzar un paro de tabulacin, a cul se le debe dar preferencia? O Ejercicio 1-22. Escriba un programa para "doblar" lneas grandes de entrada en dos o ms lneas ms cortas despus del ltimo carcter no blanco que ocurra an- tes de la n-sima columna de entrada. Asegrese de que su programa se comporte apropiadamente con lneas muy largas, y de que no hay blancos o tabuladores antes de la columna especificada. O Ejercicio 1-23. Escriba un programa para eliminar todos los comentarios de un programa en C. No olvide manejar apropiadamente las cadenas entre comillas y las constantes de carcter. Los comentarios de e no se anidan. O SECCION 1.10 VARIABLES EXTERNAS Y ALCANCE 37 Ejercicio 1-24. Escriba un programa para revisar los errores de sintaxis rudimen- tarios de un programa en e, como parntesis, llaves y corchetes no alineados. No olvide las comillas ni los apstrofos, las secuencias de escape y los comentarios. (Este programa es difcil si se hace completamelHe genera1.) D 25. CAPITULO 2: Tipos, operadores y expresiones Las variables y las constantes son los objetos de datos bsicos que se manipu- lan en un programa. Las declaraciones muestran las variables que se van a utilizar y establecen el tipo que tienen y algunas veces cules son sus valores ini- ciales. Los operadores especifican lo que se har con las variables. Las expresio- nes combinan variables y constantes para producir nuevos valores. El tipo de un objeto determina el conjunto de valores que puede tener y qu operaciones se pue- den realizar sobre l. Estos son los temas de este captulo. El estndar ANSI ha hecho muchos pequeos cambios y agregados a los ti- pos bsicos y a las expresiones. Ahora hay formas signad y unsigned de todos los tipos enteros, y notaciones para constantes sin signo y constantes de carcter hexadecimales. Las operaciones de punto flotante pueden hacerse en precisin sen- cilla; tambin hay un tipo long double para precisin extendida. Las constantes de cadena pueden concatenarse al tiempo de compilacin. Las enumeraciones son ya parte del lenguaje, formalizando una caracterstica pendiente por mucho tiempo. Los objetos pueden ser declarados const, lo que impide que cambien. Las reglas para conversin aUlomlica enlre tipos aritmticos se aumentaron pa- ra manejar el ahora ms rico conjunto de tipos. 2.1 Nombres de variables Aunque no lo mencionamos en el captulo 1, existen algunas restricciones en los nombre de las variables y de las constantes simblicas. Los nombres se com- ponen de letras y dgitos; el primer carcter debe ser una letra. El carcter de sub- rayado" -_ti cuenta como una letra; algunas veces es til para mejorar la legibili- dad de nombres largos de variables. Sn embargo, no se debe comenzar los nombres de variables con esle carcter, puesto que las rutinas de biblioteca con frecuencia usan tales nombres. Las letras maysculas y minsculas son distintas, de tal manera que x y Xson dos nombres diferentes. La prctica tradicional de e es usar letras minsculas para nombres de variables, y todo en maysculas para Constantes simblicas. 39 26. I 40 TIPOS, OPERADORES y EXPRESIONES CAPITULOZ Al menos los primeros 31 caracteres de un nombre interno son significativos. Para nombres de funciones y variables externas el nmero puede ser menor que 31, puesto que los nombres externos los pueden usar los ensambladores y los car- gadores, sobre los que el lenguaje no tiene control. Para nombres externos, el es- tndar garantiza distinguir slo para 6 caracteres y sin diferenciar maysculas de minsculas, Las palabras clave como if, else, int, float, etc., estn reservada:.-: no se pueden utilizar como nombres de variables. Todas ellas deben escribirse Con minsculas. Es conveniente elegir nombres que estn relacionados con el propsito de la variable, que no sea probable confundirlos tipogrficamente, Nosotros tende- mos a utilizar nombres cortos para variables locales, especialmente ndices de ite- raciones, y nombres ms largos para variables externas, 2.2 Tipos y tamaos de datos Hay unos cuantos tipos de datos bsicos en C: char un solo byte, capaz de contener un carcter del conjunto de caracteres local. int Hoo! double un entero, normalmente del tamai'io natural de los enteros en la mquina en la que se ejecuta, punto flotante de precisin normal. punto flotante de doble precisin. Adems, existen algunos calificadores que se aplican a estos tipos bsicos. short y long se aplican a enteros: short int sh; long int counter; La palabra in! puede omitirse de tales declaraciones, lo que tpicamente se hace. La intencin es que short y long puedan proporcionar diferentes longitudes de enteros donde sea prctico; in! ser normalmente el tamafio natural para una mquina en particular. A menudo short es de 16 bits y long de 32; in! es de 16 ode 32 bits. Cada compilador puede seleccionar libremente los tamafios apropia- dos para su propio hardware, sujeto slo a la restriccin de que los shor!s e in!s son, por lo menos de 16 bits, los longs son por lo menos de 32 bits y el short no es mayor que int, el cual a su vez no es mayor que feng. El calificador signed o unsigned puede aplicarse a char o a cualquier entero. Los nmeros unsigned son siempre positivos o cero y obedecen las leyes de la aritmtica mdulo 2", donde n es el nmero de bits en el tipo. As, por ejemplo, silos char son de 8 bits, las variables unsigned char tienen valores centre Oy 255, en lanto que las variables signed char tienen valores entre -128 'JI 127 (en una mquina de complemento a dos). El hecho de que los chars ordinarios sean con SECCION 2.3 CONSTANTES 41 signo o sin l depende de la mquina, pero los caracteres que se pueden imprimir son siempre positivos. El tipo long double especifica punto flotante de precisin extendida. Igual que con los enteros, los tamaos de objetos de punto flotante se definen en la implantacin; /loal, double y long double pueden representar uno, dos o tres ta- maos distintos. 1.0. an..:hi'o..... de ~Ili..'abc/ado lIeuders estndar y ron- ticllell (OI1...[al1tl' :.-illlbliL'as para lodo.... c!-to.... tamaflo..,. jullto l'OI1 otra... propic- dat!l's dc la mquina y del cOIll[Jilador, lo... cuaJe.'" !-oC' cli"CLlICIl ell l'l ap~'l1diL'l' B. Ejercicio 2-1. Escriba un programa para determinar los rangos de variables char, short, int y long, tanto signed como unsigned, imprimiendo los valores apropia- dos de los /caders estndar y por clculo dircclo. Es ms dificil si los calcula: de- termine los rangos de los varios tipos de punto flotante. O 2.3 Constantes Una constante entera como 1234 es un int. Una constante long se escribe con una 1 (ele) o L terminal, como en 123456789L; un entero demasiado grande para caber dentro de un int tambin ser tomado como long. La:.. constantes sin ~ig no se escriben con una u o U, terminal y el sufijo ul o UL indica unsigned long. Las constantes de punto rIotante eonricnen un punto decimal (123.4) o un ex- ponente (le-2) o ambos; su tipo es double, a menos que tengan sufijo. Los sufi- jos f o F indican una constante /loa!; 1 o L indican un long double. El valor de un entero puede especificarse en forma octal o hexadecimal enJu- gar de decimal. Un O (cero) al principio en una constante entera significa octal; Ox OX al principio significa hexadecimal. Por ejemplo, el decimal 31 puede es- cribirse como 037 en octal y Oxlf OX1F en hexadecimal. Las constantes octales y hexadecimales tambin pueden ser seguidas por L para convertirlas en long y U para hacerlas unsigned: OXFUL es una constante unsigned long con valor de 15 en decimal. Una ('onSlal1le de carcter es un Cillero, csnilo C0l110 un carcter dentro de aros- trofos, lal como 'x'. El valor de una constante de caracter es el valor nUIlH:rico del carcter en el conjunto de caracteres de la mquina, Por ejemplo, en el conjunto de caracteres ASCII el carcter constante 'O' tiene el valor de 48, el cual no est relacionado con el valor numrico O. Si escribimos '0' en vez de un valor numrico como 48 que depende del conjunto de caracteres, el programa es independiente del valor particular y ms fcil de leer. Las conSlantes de car":lcr participan en ope- raciones numricas tal como cualesquier otros enteros, aunque se utilizan ms co- mnmente en comparaciones con otros caracteres. Ciertos caracteres pueden ser represcnlado~ en con~tantc de caraclcr y de cadena, Por medio de secuencias de escape como n (nueva lnea); esas secuencias se ven 27. ~ 42 TIPOS, OPERADORES Y EXPRESIONES CAPITULO 2 como dos caracteres, pero representan slo uno. Adems, un patrn de bits ar- bitrario de tamao de un byte puede ser especificado por '000' en donde 000 son de uno a tres dgitos octales (0...7) o por 'xhb' en donde hh son uno O ms digitos hexadecimales (0...9, a.. .f, A...F). Asi podria- mas escribir #define VTAB '