Sie sind auf Seite 1von 153
_ LENGUAJE DE / PROGRAMACION EDICION EN INGLES UNIX es una marca repistrada AT & T ENGUAJE DE PROGRAMACION © ‘Traducido de la segunda edicion en inglés de: ‘THE C PROGRAMMING LANGUAGE 1.0 parcial de esta obra, por cualquier medio o método sin autorizacion escrita del editor DERECHOS RESERVADOS © 1991 respecto a la segunda edicion en espaol por PRENTICE-HALL HISPANOAMERICANA, S.A. Alacornulco Nim, 500-5? Piso Col Industrial Atowo 53519, Naucalpan de Jur, Edo. de México Miembro de Ia Camara Nacional de la Industria Editovial, Reg. Nim, 1524 ISBN 968-880-205-0 ISBN 0-13-110362-8 IMPRESO EN MEXICO / PRINTED IN MEXICO Prefacio Prefacio a ta primera edicion Introduccién lo 1, Introduceiin general ‘Comencemss Variables y expresiones aritméticas La proposiién for CConstantes simbdlicas Entrada y salida de caracteres Arreglos Funciones ‘Argumento;—llamada por valor 9 Arreplos de caracteres 10 Variables externas y alcance Capi 1 1 1 1 1 1 1 1 1 Capitulo 2, Tipos, operadores y expresiones 2 Nomibres de variables 22 Tipos y tamaiios de datos 23 Constanes 214 Dectaraciones 2S. Operadores aitméticos 26 Operadores de relacion y Wgicos 2.7 Conversions de tipo 28 Operadores de incremento y decremento 39. Operadores para manejo de bits 2.10 Operadores de asignacion y expresiones 2.11 Expresiones condiconales 212 Precedencia y orden de evaluacign lo 3. Control de flujo 3.1 Proposiciones y bloques CONTENIDO ix 3.7. Break y continue 3.8 Goto y etiquetas lo 4. Funciones y ta estructura del programa 4.1. Conceptos basicos de funciones 42 alores no-enteros 43 44 4.11 EI preprocesador de C Capitulo 5. Apuntadores y arreglos 5.1 Apuntadores y direcciones 5.2. Apuntadores y argumentos de funciones 5.3. Apuntadores y arreglos 5.4 Ariumética de direcciones 5.5 Apuntadores a caracteres y funciones 5.6 Arreglos de apuntadores; apuntadores a apuntadores 5.7. Arreglos multidimensionales 5.8 Inicializacion de arreglos de apuntadores 5.9 Apuntadores vs. arreglos multidimensional 5.10 Argumentos en la linea de comandos 5.11 Apuntadores a funciones 5.12 Declaraciones complicadas 5. Estructuras Conceptos basicos sobre estructuras Estructuras y funciones Arreglos de estructuras Apuntadores 0 estructuras Estructuras ferenciadas Buisqueda en tabl Typedet Uniones ‘Campos de bits 105 de longitud variable to—seanf 7.6 Manejo de errores—stderr y exit Alcance y ligadura Preprocesamiento Gramatica 9 Senales: Funciones de fecha y hora: Apéndice C. Resumen de nodificaciones Prefacio El mundo de la computacién ha suftido una revolucién desde la publicacién, en 1978, de £! lenguaje de programacién C. Las grandes computadoras son aho- ra mucho mas grandes, y las computadoras personales tienen capacidades que ri- valizan con los me cambiado en ese La creciente popularidad de C, los cambios en el lenguaje a lo largo de los aios, y la creacién de compiladores por grupos no involucrados en su diseito, se combinaron para demostrar la necesidad de una definicidn del lenguaje mas pre- cisa y contempordnea que la que proporcioné la primera edicién de este libro. En 1983, el Americas National Standards Institute (ANSI) estableci6 un cor ‘cuyos propésitos eran producit “una definicion no ambigua del lenguaje Cc, dependiente de la maquina’, cuidando la conservacion de su es do es el estandar ANSI para el lenguaje C i pero no descritas en la primera cedicion, particularmente la asigna nal, y al mismo tiempo establece expliciamente cuales aspectos del lenguaje tienen aim dependensia de maquina. Esta segunda edicion de El lenguaje de programacién C lo describe tal como lo defini6 el estandar ANSI. (En el momento de escribir esta edicidn, el esténdar se encontraba en la eta: 3¢ esperaba su aprobacién a finales Tenguaje forma. En gene- isible es la nueva jodernos compiladores ma- ral esto no hace una forma de declaras nejan ya la may! x PREFACIO Hemos tratado de mantener la brevedad de lz primera edicién. Bl lenguaje C no es grande, y no le esta bien un gran libro. Hemos mejorado la exposicion de ccaracteristicas criticas, como los apuntadores, que son parte central en la progra- macién con C. Hemos redefinido los ejemplos criginales y agregamos ejemplos nuevos en varios capitulos. Por ejemplo, se aumexté el tratamiento de declarac nes complicadas con programas que convierten ceclaraciones en palabras y vice- versa, Como antes, todos los ejemplos se han probado directamente a partir del texto, el cual estd diseniado de manera que lo pueda leer la maquina. EI ape ‘manual de referencia, no es el estandar, sino que nuestra rencidn fue trasladar la esencia del esténdar a un espacio més pequeiio. Esté hecho con el dnimo de que proporcione una facl comprensién para los progra- adores, pero no como una definicidn del lenguaje para quienes escriben compi- ladores —ese papel proj es un resumen de las pos tiene el pro- pésito de ser una referencia para programadores, no para implantadores. En el apéndice C se offece un resumen de los cambios de la versién original. ‘Como mencionamos en el prefacio a la primera edicién, C “se lleva bien, en cen que aumenta m una década mas de expe ain lo sentimos asi. Descamos que ro le ayude a aprender et enguaje C y también c6 ‘Tenemos un profundo reconocimiento hacia los amigos que nos ayudaron a producir esta segunda edicidn. Jon Bentley, Doug Gwyn, Doug Mcllroy, Peter Nelson y Rob Pike nos dieron valiosos comentarias sobre casi cada pagina del bo- rrador de este manuscrito, Estamos agradecidos por la cuidadosa lectura de Al Aho, Dennis Allison, Joe Campbell, G. R. Emin, Karen Fortgang, Allen Ho- lub, Andrew Hume, Dave Kristol, John Linderman, Dave Prosser, Gene Spat- ford, y Chris Van Wyk. También recibimos tities sugerencias de Bill Cheswick, Mark Kernighan, Andy Koening, Robin Lake, Tom London, Jim Reeds, Clovis Tondo y Peter Weinberger. Dave Prosser respondi6 muchas preguntas detalladas acerca del estandar ANSI. Utilizamos extensivamente el intérprete de C++ de Bjarne Stroustrup, para la prueba local de nuestros programas, y Dave Kristol nos ofrecid un compilador ANSI C para las prucbas finales. Rich Drechsler nos la Brian W. Kernighan Dennis M. Ritchie Prefacio a la primera edicion Ces un lenguaje de programacién de propésito general que ofrece como ven- tajas economia de expresiGn, control de flujo y estructuras de datos modernos conjunto de operadores. Ademas, C no es un lenguaje de “muy alt “grande’’, y no est especializado en alguna érea especial de aplicacién, Pero su ausencia de restricciones y su generalidad lo hacen mas conveniente y cfectivo para muchas tareas que otros lenguajes supuestamente mas poderosos. Originalmente, C fue disefiado para el sistema operativo UNIX y Dennis Ritchie Jo implanté sobre el mismo en la DEC PDP-11, El sistema operativo, el compila- dor de Cy esencialmeate todos los programas de aplicacién de UNIX Gincluyendo todo el sofware utilizado para preparar este libro) estan escritos en C. También cxisten compiladores para la produccién en otras méquinas, incluyendo la IBM System/370, la Honeywell 6000 y la Interdata 8/32. El lenguaje C no esta ligado € ningin hardware o sistema en particulary es fécil eseribir programas que corre- sin cambios en evalquier maquina que mangje C. 1a finalidad de este libro es ayudar al lector a aprender cémo programat . Contiene una introduccién general para hacer que los nuevos usuarios se in cien lo mas pronto posible, capitulos separados sobre cada caracteristica impor- tante y un manual de referencia. La mayoria de las exposiciones estén basadas ‘nla lectura, escrituray revisién de ejemplos, mas que en el simple establecimien- to de reglas. En su mayoria, los ejemplos son programas reales y completos, no fragmentos aislados. Todos los ejemplos han sido probados directamente a partir del texto, el cual esta en forma legible para la maquina. Ademas de demostrar ‘c6mo hacer un uso efectivo del lenguaje, donde ha sido posible, tratamos de ilus- trar algoritmos utiles y principios de buen estilo y disefio. El libro no es un manual de introduccién a la programacién; se supone en él familiaridad con los conceptos basicos de programacidn, como variables, propo- siciones de asignaci6n, ciclos y funciones. No obstante, un programador novato deber ser capaz de leer y obtener los conceptos del lenguaje, aunque le ayudaria la cooperacién de un colega més experimentado, De acuerdo con nusstra experiencia, C ha demostrado ser un lenguaje agrada- ble, expresivo y versatil para una amplia variedad de programas. Es facil de apren- Aer y se obtienen mejores resultados a medida que aumenta nuestra experiencia on él. Deseamos que este libro le ayude al lector a usarlo correctamente n xii PREFACIO A LA PRIMERA EDICION Las criticas y sugerencias de muchos amigos y colegas han aumentado much simo los conceptos de este libro y ha sido un placer escribirlo. En par tro agradecimiento a Mike Bianchi, Jim Blue, Stu Feldman, Doug Mellroy, Roome, Bob Rosin y Larry Rosler que leyeron cuidadosamente las numerosas versiones. También agradecemos Al Aho, Steve Bourne, Dan Dvorak, Chuck Haley, Debbie Haley, Marion Harrris, Rick Holt, Steve Johnson, John Mashey, Bob Mize, Ralph Muha, Peter Nelson, Elliot Pinson, Bill Plauger, Jerry Spi- vack, Ken Thompson y Peter Weinberger por sus valiosos comentarios a través de varias etapas; a Mike Lesk y Joe Ossanna, por su invaluable ayuda en la im- presién, Brian W. Kernighan Introduccién Ces un lenguaje de programacién de propésito general que ha sido estrecha- ‘mente asociado con el sistema UNIX en donde fue desarrollado puesto que tat el sistema como los programas que corren en él estan escritos en lenguaje C. Sin embargo, este lenguae no esté ligado a ningin sistema operativo ni a ninguna maquina, y aunque ima “lenguaje de programacién d su utilidad para escribir compiladores y sistemas operativos, se cficacia para escribir importantes programas en diversas di ‘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 través del lenguaje B, el cual fue escrito por Ken Thompson en 1970 para cl primer sistema UNIX de la DEC PDP-7, una variedad di yntimeros de punto fiotante de varios tamafios. Ademés, existe una jerarqufa de tipos de datos derivados, creados con apuntadores, arreglos, estructuras y unio- roposicién. Los apuntadores proporcionan una aritmética de direcciones inde- Pendiente de la maquina C proporciona las tonstrucciones fundamentales de control de flujo que se re- guieren en programas bien estructurados: azrupacién de proposicione: lecciOn de un caso entre un conjunto de ell while, for) 0 en la pat estructuras, uniones ivamente. Las va- ymalmente ‘*automaticas”, o creadas de nuevo con cad idn de una funcién no puede estar anidada, pero las variables wadas en una modalidad estructurada por bloques. Las funcio- es de un programa enC pueden existir en archivos fuente separados, que se com- ‘an de manera separada, Las variables pueden ser internas a una funcién, ‘temas pero conocides s6lo dentro de un archivo fuente, o visibles al programa completo, 2 INTRODUCCION Un paso de preprocesamiento realiza substitucién de macros en el texto del programa, inclusién de otros archivos fuente y compilacién condicional.. Ces un lenguaje de relativo “bajo nivel ". Esta caracterizacién no es peyora- tiva, simplemente significa que C trata con el miso tipo de objetos que la mayo- ria de las computadoras, Ilamense caracteres, mimeros y direcciones. Estos pueden ser combinados y cambiados de sitio con !os operadores aritméticos y 16- gicos implantados por maquinas reales. CC no proporciona operaciones para tratar directamente con objetos compues- tas 0 arreglos, No existen ope inque las estructuras pueden copiarse como una unidad. El lenguaje no define ninguna facilidad para de almacenamiento que no sea la de definicién estatica y la disciplina de pilas provista por las variables locales de func’ no emplea heap ni re- colector de basura. Finalmente, C en si mismo no proporciona capacidades de en- trada/salida; no hay proposiciones READ 0 WRITE, ni métodos propios de acceso a archi por funciones De manera semejante, C solamente ofrece un control de flujo franco, y lin condiciones, ciclos, agrupamientos y subprogramas, pero no multiprogramacién, operaciones paralelas, sincronizacion ni corn ‘Aunque la ausencia de alguna de esas capacidades puede parecer como una rave deficiencia (“significa que se tiene que llamar a una funeién para compa rar dos cadenas de caracteres?”), el mantener al lenguaje de un tamafto modesto tiene beneficios reales. Puesto que C es relativamente pequefto, se puede deseribir -. Un programador puede razo- izar en verdad la totalidad del nguaje Por muchos alos, Ia definicién de C fue el manual de referencia de la primera edicion de E1 lenguaje in C. En 1683, el American National Stan- dards Institute (ANSW) establecio moderna ¥ compret in resultante, el estandar ANSI 0 le definicion de C. La defi “ANSI C”, se esperaba fuera aprobada a fines d: 1988. La mayoria de las carac~ teristicas del estandar ya se encuentran soportadas por compiladores modernos. El estdndar esta basado en el manual de referencia original. El lenguaje ha ‘cambiado relativamente poco; uno de los propésitos det estandar fue asegurar que la mayoria de los programas existentes pudiesen permanever validos o, al me- nos, que los compiladores pudieran producir mensajes de advertencia acerea del nuevo comportamiento. Para la mayorfa de los programadores, el cambio ms para declarar y definir funciones. Usa declaracién de funcién ahora \eluir una descripeién de los argumentos de la funcién; la sintaxis de la n cambia para coincidir. Esta informacion extra permite que los compi- ladores detecten mas facilmente los errores causados por argumentos que no coin- ciden; de acuerdo con nuestra experiencia, es una adicion muy dil al Lenguaje, importante es una nue- LENGUAJE DE PROGRAMACIONC 3 Existen otros cambios de menor escala en el lenguaje. La asignacién de estr turas y enums n sencilla, Las propiedades de la aritmé signo, estin esclarecidas. El preprocesador es laborado. La mayor parte de esos cambios solo endran efectos secundarios para la mayoria de los progra madores. ‘Una segunda contribuci6n significativa dei estindar es la definicién de una bi- blioteca que acompaie 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, asignacién de memoria, manipulacién de cadenas y otras actividades se- mejantes. Una coleccion de encabezadores (headers) estandar proporcionan un acceso uniforme a las declaraciones de funciones y tipos de datos. Los programas que utilizan esta biblioteca para interactuar con un sistema anfitri6n estan asegu- rados de un comportamiento compatible, La mayor parte de estrechamente modelada con base en la “biblioteca E/S idan otros sistemas. De nuevo, la mayoria de los pr dores no notaran mucho el cambio. Debido a que los tipos de datos y estructuras de control provistas por C son manejadas directamente por la mayoria de las computadora jecucion (run-time) requerida para implantar programas autoco 0s es pe- queha. Las funciones ce la biblioteca estndar Unicamente se llaman en forma ex: de manera que se pueden evitar cuando no se necesitan. La mayor parte Puede eseribirse en C, y excepto por detalles ocultos del sistema operativo, ellas ‘mismas son portdtiles Aunque C coincidecon las capacidades de muchas computadoras, es indepen- diente de cualquier arquitectura. Con un poco de cuidado es Facil escribir progra- ‘mas portatiles, esto es, programas que puedan correr sin cambios en tna variedad de méquinas. El estandar explica los problemas de la tran tun conjunto de constantes que caracterizan a la maquina en la programa, Cno cs un lenguajefuertemente tipificado, sino que, cacién de tipos ha side reforzada, La definicién original de C desaprob ermitid, el intercambio de apuntadores y enteros; esto se ha dar ahora requiere la edecuada declaraciGn y la conversién explicita que ya ha sido obligada por los buenos compiladores. La nueva declaracién de funciones £$ otro paso en esta direccién. Los compiladores advertirin de la mayoria de los Ettores de tipo, y no hay conversién automatica de tipos de datos incompatibles. Sin embargo, C mantiene la filosofia basica de que los programadores saben lo ue estan haciendo; silo requiere que establezcan sus intenciones en forn 10 cualquier otro lenguaje, C tiene sus defectos. Algunos de los operado- 1en Ja precedencis equivocada; algunos elementos de la sintaxis pueden ser 4 wwtropuccion mejores. A pesar de todo, yy expresivo para una ampl ha probado ser un lenguaje extremadamente efectivo variedad de programas de aplicacién El libro esta organizado como sigue. El capitulo 1 ¢s una introduccién orien- tada a la parte central de C. El propésito es hacer que el lector se inicie tan pronto como le sea posible, puesto que creemos firmemeate que la forma de aprender ‘un muevo lenguaje es escribir programas en él. La introduccién supone un conoci- ‘miento prctico de los elementos basicos de la pros 19 hay una explica- in de computadoras, de compi ficado de una expresién como n=n-+1. Aunque hemos donde fue posibl estructuras de datos y al cién, nos hemos conee En los capit pectos de C en mayor detalle y mas formalmente de lo que se hace en el ‘aunque el énfasis esté aim fn los ejemplos de programas completos, més queen fragmentos aislados. El tulo 2 trata de los tipos basicos de datos, operaciones y expresiones. El capi ructura de un programa —variables externas, reglas de alcance les y otros aspecios— y también abarca al pre~ ocesador. El capitulo 5 discute sobre apumtadores y aritmética de direcciones. pitulo 6 cubre estructuras y uniones. se cubren funciones y la rativo UNIX, concentrandose en entrada/saida, el sistema de archivos y la nacién de memoria. Aunque algo de este capitulo es especifico de sistemas UNIX, los programadores que usen otros sistemas de todas maneras encontrarén aqui de utilidad, incluyendo alguna comprension acerca de como esta implan- aver as{ como sugerencias para obtener un. cédigo portat El apéndice A contiene un manual de consulta del lenguaje. El informe oficial de la sinta embargo, esti ‘manual de const sta. El apéndice B es un resumen de la blioteca estandar, de nuevo mas para usuarios que para implantadores. El apén- dice C es un breve resumen de los cambios del lenguaje original. Aunque, en caso de duda, el estandar y el compilador en uso quedan como las autoridades finales sobre el lengt capitu.o 1: Introduccion general rapido como sea posible al punto en donde pueda escribir programas para hacerlo tenemos que concentrarnos en las bases: variables y constantes, a mmética, control de flujo, funciones y los rudimentos de entrada y salida, Hemos fuera de este capitulo las cara -as de C que son importantes para escribir programas més grandes, Esas caracteristicas incluyen intadores, estructuras, la mayor parte del rico conjunto de operadores de C, ias proposiciones para control de flujo y la bil Este enfoque tiene sis inconvenientes. Lo En cualquier caso, los programadores con experie ‘apolar del material que se encuentra en. rogramacién. Los principiantes deben complementatlo Programas semejantes a los aqui expuestos. Ambos grupos pueden utilizar e: lo como un marco de referencia sobre el cual asociar las descripciones mis idas que comienzen en el capitulo 2. Comencemos in nuevo lenguaje de programaci rograma por escribir ¢s el mismo para todos habilidad de crear el texto lo, ejecutarlo y des- nevanicos, 16do lo Este ¢s el gran obstaculo; para librarlo debe tener del programa de alguna manera, compi cubrira donde Con el dor demas es relativ En C, el programa para escribir “hola, mundo" es #include rmain( ) { priatl("hola, mando\n"); ' La forma de ejecutar este programa depende del sistema que se esté utilizan- do, Como un ejemplo especi ,0 UNIX se debe crear el programa en un archivo cuyo nombre termine con “.c"”, como hola.c, y después larlo con la orden ce hola. Sino se ha cometido algiin error, como la omisiin de un eardcter o escribir algo en forma incorrecta, la compilacién se hard sin emitir mensaje alguno, y creard un archivo ejecutable Hamado a.out. Si se ejecuta a.out escribiendo la orden a.out se eseribira hola, mundo En otros sistemas, las reglas seran diferentes, constltelo con un experto. Ahora algunas explicaciones acerca del programa en si. Un programa en C, cualquiera que sea su tama, consta de funciones y variables . Una funcién c¢ tiene proposiciones que especifican las operaciones de célculo que se van a re zar, ¥las variables almacenan los valores utilizados durante los cdlculos, Las fun- ciones de C son semejantes a las subrutinas y funciones de Fortran 0 a los procedimientos y funciones de Pascal. Nuestro ejemplo es una funcién 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 main. Esto significa que todo programa debe tener un main en algtin sitio. Por lo comin main llamara a otras funciones que ayuden a realizar su traba- jo, algunas que usted ya escribié, y otras de bibliotecas escritas previamente. La primera linea del programa. Wsnclade indica al compilador que debe incluir informacion acerca de la bibl dar de entrada/salida; esta linea aparece al principio de muchos archivos fuente de C. La biblioteca estindar estd deserita en el capitulo 7 y en el apéndice B. Un método para comunicar datos entre las funciones es que la llama proporciona une lista de valores, llamados argumenios, a ta invocando. Los parériesis que estan después del nombre de la fun ala lista de argumentos. En este ejemplo, main esta definido para ser que no espera argumentos, lo cual esti indicado por la lista vacia ( ). # include incluye informacion acerca de la biblioteca estindar main( ) define una funcion llamada main que 10 recibe valores de argumentos ( as proposiciones de main esti encerradas entre ilaves “hola, mundo\n"); ‘main tlama a la funcién de biblioteca print. t ara escribir esta seewencia de caracteres; \a representa el cardeter nueva linea EI primer programa en C _ Las proposiciones de una funcién estiin encerradas er cién main sélo contiene una proposici ola, mundo\n") réntesis; de esta maneca se est “hola, mundo\n"’, prntf es una funcidn de biblioteca que eve este caso la cadena de caracteres que se encuentra entre comil A una secuencia de caracteres entre co 1 cadena dle caracteres © constante de cade luso de cadenas de caracteres serd como argumentos para print! y otras fui La secuencia \n en la cadena representa el caracter nueva linea e! de C, y hace avanzar la impresién al margen izquierdo de la siguiente linea. S: Sc omite el \n (un experimento que vale la pena), encontrar que no hay avance le linea después de la impresion. Se debe utilizar \n para incluir un carcter nuue= ¥a linea en el argumerio de printf; sise intenta algo como “hola, mundo pri ” 6! compilador de C producicé un mensaje de error ULOL printf nunca proporciona una nueva linea automét se pueden utilizar varias llamadas para construir una Nuestro primer programa también pudo haber sido escrit imente, de manera que ea de salida en etapas, de la siguiente manera. include -produciéndose una salida idéntica, [Notese que \n representa un solo cardecter. Una secwencia de escape como \n proporciona un mecanismo general y extensible para representar caracteres in de escribir. Entre otros que C proporciona estén \t para tabula- in, \b para retroceso, \” para comillas,y \\ para la diagonal invertida, Hay una lista completa en la seccién 2.3. Ejercicio 1-1. Ejecute el programa “hola, mundo" en su sistema, Experimente con Ia omisidn de partes del programa, para ver qué mensajes de error se obtienen. Cr Experimente el descubrir qué pasa cuando la cadena del argumento i contiene \e, en donde c es algtin cardcter no puesto en lista anterior mente, O 1.2. Variables y expresiones aritméticas El iente programa utiliza la férmula °C = (5/9) (°P-32) para imprimir wiente tabla de temperaturas Fahrenheit y sus equivalentes c Is: SB2SR° 140 60 160 71 180 82 200 23 220 104 SECCION 12 VARIABLES V EXPRESIONES ARITMETICAS 240 115 ° int ahr, celsius; int lower, upper, step) lower = 0; J+ limite inferior de upper = 300; / limite superior step = 20; J+ tamano del incremento «/ abe while Las dos lineas Js imprime la tabla Fahrenhei para fahr = 0,20, ..., 300 +/ son un comentario, que en este caso expli Cualesquier caracteres entre /. brevemente que hace el programa. / son ignorados por el compilador, y pueden ados libremente para hacer a un programa mas entender. Los co- irios pueden aparecer en cualquier lugar donde puede colocarse un espacio en blanco, un tabulador o nueva linea, En C, se deben declarar todas las variables antes de su uso, generalmente al Drincipio de la funcién y antes de cualquier proposicién ejecutable. Una declara- idn notifica las propiedades de una variable; consta de un nombre de tipo y una ‘a de variables, como int fabr, celsius; int lower, upper, step; 10 INTRODUCCION GENERAL carrruto1 EI tipo int significa que las variables de la lista so1 enteros, en contraste con float, a punto fotante, esto es, mimeros que pueden tener una parte fraccio- por lo menos con seis digitos significativos y ura magnitud generalmente entre 10 y 10°*. ‘Ademds de int y loat, C proporciona varios tipos de datos basicos, incluyendo: char ccardcter —un solo bute short entero corto long entero largo double punto flotante de doble precisien Los tamafis de estos objetos también dependen de la mquina. También existen arreglos, estructuras y uniones de estos tipos basicos, apuntadores a ellos y funciones {que regresan valores con esos tipos, todo lo cual « verd en el momento oportuno. Los caleulos en el programa de conversién de temperaturas principian con las, proposiciones de asignacién, lower = 0; supper = 300; step = 20; fabr = 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 iteracién que se repite una vez por cada linea de salida; este es el propésito del ciclo while while (fahr <= upper) { , El ciclo while funciona de la siguiente manera: se prueba la condicién entre pa- réntesis, De ser verdadera (fahr es menor o igual que upper), el cuerpo del ciclo (las tres proposiciones entre llaves) se ejecuta. Luego la condicién se prueba nue- vamente, y sies verdadera, el cuerpo se ejecuta de nuevo. Cuando la prueba resul- ta falsa (lahr excede a upper) la iteracién termina, y la ejecucién continiia en la proposicién que sigue al ciclo. No existe ningunaotra proposicion en este progra- ma, de modo que termina, El cuerpo de un while puede tener una o mas proposiciones encerradas entre Hlaves, como en el convertidor de temperaturas, ¢ una sola proposicién sin Haves, como en wb 1+ imprime la tabla Fahrenheit-Celsius para fakr = 0, 20, ..., 300; versién de punto flotante «/ main( ) { float far, celsius; iahr~22.0); fahr, celaue); fabs = fahr + step; SECCION \VARIABLES Y EXPRESIONES ARITMETICAS 13 Estoes dos como No pudimos truncaria 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 relacién de dos valores de punto flotante. ‘ene operandos enteros, se ejecuta una operacién ‘un operador numérico tiene un operando de punto flotante y otro ente- libimo sera cenvertido a punto flotante antes de hac Las reglas detalladas de cuindo los enteros se convierten a punto flotante se encuentran en el capitulo 2, Por ahora, nétese que la asignacién 1 = lower; y la prueba while hr <= upper) umbién trabajan en la forma natural —el int se convierte a float antes de efec- tuarse la operacién. La especificaciOn de conversion %3.0f del printf indica que se escribiré un iimero de punto flotente (en este caso fahr) por lo menos con tres caracteres de ancho, sin punto dedmal y sin digitos fraccionarios; %6.1f describe a otro ‘iimero (celsius) que se eseribird en una amplitud de por lo menos 6 caracteres, con 1 digito después del punto decimal. La salida se vera como sigue: 0 -178 202-67 40 44 amplitud y la precision pueden omitise de una espe n: 966f indica que Lmimero es por lo menos de seis earacteres de ancho; %.2{ indica dos caracteres, después del punto decimal, pero el ancho no esta restringido; y % indica escribir el mimero como punto flotante. Sa 66d et 688 14. INTRODUCCION GENERAL caprruLo+ wat scribe como punto flotante, can 2 caracteres después del punto decimal %96.2i —_escribe como punto flotante, per lo menos con 6 earacteres de aancho y 2 después del punto des mbién reconoce 9%o para octal, %x para hexadecimal, %e para cardcter, %s para cadena de caracteres y %% para % en si 1.3. Modifique el programa de conversion de temperaturas de modo iba un encabezado sobre la tabla. Ejercicio 1-4, Escriba un programa que imprima la tabla correspondiente Celsius a Fahrenheit. 1.3. La proposicién for Existen suficientes formas particular. Intentemos una vari tas de escribir un programa para una tarea en del programa de conversién de temperaturas, include int fahr, for (fahr = 0; fahr <= 300; fahr = fakr + 20) rint("$63d %6.16/n", far, (6.0/9.0)+(fahr 32); acdos, pero ciertamente se ve dil de la mayoria de las variables; s6lo permanece fahr y la hemos hecho ites inferior y superor y el tamafto del avance slo ‘aparecen como constantes dentro de la proposicién for, que es una nueva construccién, y la expresién que calcula la temperatura Celsius ahora aparece como el tercer argumento de printf en ver de una proposicién de asignacién sepa- rada. Este tltimo cambio ejemplifica una regla general —en cualquier contexto en cl que se flotante puede estar all uuna forma generslizada del wu operacién debe ser clara. Dentro de los paréntesis existen -ciones, separadas por punto y coma. La primera, la inicializacion fahr = 0 La propo con el seccION.4 CONSTANTESSIMBOLICAS. 15, se cjecuta una vez, antes de entrar propiamente al ciclo. La segunda seccién es la condicién 0 prueba que controla el fahr <= 300 Esta condicién se evahiz; sies verdadera, el cuerpo del ciclo (en este caso un sim- ple printf) se ejecuta. Después el ineremento de avance hr = fabs + 20 se ejecuta y la condicién se vuelve a evaluar. El ciclo termina sila condicién se hace alsa, Tal como con el while, el cuerpo del ciclo puede ser sencilla 0 un grupo de proposiciones encerradas entre llaves. La incremerto pueden ser cualquier expresién. mn entre while y for es arbitraria, y se basa en aquello que pi for es por lo general apropiado para ciclos en los que la ini més claro. cidn y el inererhento son proposiciones sencillas y Idgicamente rel to que es mas compactoque el while y mantiene reunidas en un lugar @ las propo- siciones que controlan al ciclo, Ejercicio 1-8. Modifique el programa de conversién de temperaturas de manera que escriba la tabla en orden inverso, esto es, desde 300 grados hasta 0. 0 1.4 Constantes simbélicas Una observacién final antes de dejar definitivamente el tema de la conversién. de temperaturas. Es una mala prictica poner ‘“nimeros magicos”” como 300 y 20 en un programa, ya que proporcionan muy poca informacién a quien tenga que leer el programa, y son cificiles de modificar en un forma sistem: ra de tratar a esos mimeros 1 nombre tiene la misma forma que un nombre de variable: una secuen- ras y digitos que comienza con una I texto de reemplazo puede Ser cualquier secuencia de caracteres; no esté limitado a niimeros. include 16 xTRODUCCION GENERAL J+ imprime la tabla Fahrenheit-Colsius + main() { int far; for (lahr = LOWER; printi("%43d %66 tidades LOWER, UPPER y STEP son constantes simbdlicas, no variables, Jue no aparecen entre las declaraciones. Los nombres de constantes encién, se escriben con waytisculas, de modo que se iimente de los nombres de variables escritos con mindsculas. [Nétese que no hay punto y coma al final de una linca #define. 1.5. Entrada y salida de caracteres Ahora vamos a considerar una familia de progrzmas relacionados para el pr. 10 cardcter. Se encontrar que muchos programas s6lo es ampliadas de los prototipos que se ‘ratan agus simple. La entrada y sal importar dénde fue originada 0 hacia donde se dirige, se tr ijos (streams) de caracteres. Un fi de caracteres divididos entre lineas, cada una de las cuales consta de cero 0 més caracteres seguidos de un cardcter nueva responsable de hacer que cada secuencia de entrada o salida e roporciona varias funciones para leer o escribir un getchar y putchar son las mas simples. Cada vez liente cardcter de enirada de una secuencia de tex- sde cardcter a la vez, de las eu getchar lee el 10 y lo devuelve como su valor. Esto es, d © = geichar( la variable c con! jente cardcter de entrada. Los caracteres provienen normalmente del teclado; la entrada de archivos se trata en el capitulo 7. La funcién putchar escribe un cardcter cada vez que se invoca: putchar(e) contenido de la variable entera c Las llamadas a putchar y a printi pueden estar alternadas; mo un caracter, generalmente en salida SECCION S| ENTRADA YSALIDADECARACTERES 17 4.5.1 Copia de archivos Con getchar y putchar se puede escribir una cantidad sorprendente de eédigo itil sin saber nada més acerca de entrada y salida. El ejemplo més sencillo es un programa que copia la entrada en la salida, un cardcter a la vez: lee un cardeter while (cardcter no es indicador de fin de archivo) ‘manda a ta salds el cardcter recién leido lee un cardctor Al convertir esto en C se obtiene include J+ copla la entrada a la salida; 1a, versién «/ main() into; como tn cardcter en otra cosa, almacenado bien puede ser usado cualquier tipo de entero. Usamos int por una sutil pero im- portante razén, EL problema es distinguir el fin de la entrada de los datos vilidos. La solucién 8 que getchar devuelve un valor distintivo cuando no hay mas a la entrada, un valor que no puede ser confundido con ningiin otro cardcter. Es EOF, por ‘end of file (fin de archivo)". Se debe declarar ¢ ino sea lo suficientemente grande para almacenar cualquier valor que le regrese get- char. No se puede utilizar char puesto que c debe ser suficientemente grande como para mantener a EOF ademas de cualquier otro caracter. Por lo imbélica, hemos asegurado que nada en el programa depende del valor numérico especifico 18 INTRODUCCION GENERAL carmuto1 EI programa para copiar podria escribirse de modo més conciso por progra- ‘madores experimentados de C. En lenguaje C, cualquier asignacién, tal como c= getchar() in puede aparecer cono pari cardcter a ¢ se coloca dent 16 while, el programa que copia puede es:ribirse de include J+ copia la entrada a la salida; 2a. versién «/ while (c = getchar putchar(c); = FOF) } El while obtiene un cardcter, lo asigna a e, y entonces prueba si el cardcter fue la sefial de fin de archivo. De no serlo, el cuerpo del while se cjecuta, escribiendo (¢ el while, Luego, cuando se alcanza el final de la entra- termina y también lo hace main. Esta versién centraliza la entrada —ahora hay solo una referencia a getchar— ante es mas compacto y mas facil de leer 4 seguido este s que estan alrededor de la de! = cs, ‘en ausencia de paréntesis la prueba de cin =. De esta manera, la proposicién © = gelchar( )!= EOF jdn != se realizaria antes de la asigna- s equivalente a © = (getchax( )! EOF) Esto tiene el efecto indeseable de hacer que c sea0 mada de getchar encontré fin de ar mas detall Ejer Ejercicio 1-7. Escriba un programa que imprima el valor de BOF. © dependiendo de si la 2 se trata este tema vio 1-6. Verifique que la expresién getchar ()!= EOF es 001. 0 ENTRADA YSALIDA DECARACTERES. 19 1.5.2 Conteo de caracteres El siguiente programs cuenta caracteres y es semejante al programa que copia, include / cuenta los care main() { s do la entrada; La. version «/ Jong ne; ac = 0; while (getch: 1= EOF) iene = me + 1, pero + +nees mas conciso y muchas veces més elieiente opetador correspondiente ~ para disminuir en 1. Los operadores ++ y jos (+ +-ne) como postfijos (n+ +); esas eites valores dentro de las expresiones, como se de- 102, pero ambos ++ne y ne-+ + incrementan a nc. Por el momento adoptaremo: la forma de prefijo. El programa para cortar caracteres acumula su cuenta en una variable long en lugar de una int. Los enteros long son por Jo menos de 32 Aunque en algunas méquinas int y long son del mismo tamano, en otras un int es de 16 con un valor maximo de 32767, y tomaria ivamente poca lectura a la entrada para desbordar un contador int. La especificacién de conversién %ld indica @ jorrespondiente es un entero long. para demostrar otra forma de escribir el #include J» cuenta los caracteres de la entrada; 2a. version «/ smain( ) { double ne; for (nc = 0; getchar( ) != EOF; + +nc) 4 '%.08\n’, ne); 20. metRODUCCION GENERAL carrruto joat como para double; %.0f suprime la impresion del punto decimal y de la parte fraccionaria, que ¢s cero. El cuerpo de este ciclo for estd vacio, debido a que todo cn las secciones de prueba ¢ incremento. Pero las reglas gramaticales de C requic- ren que una proposicién for tenga un cuerpo. Ei punto y coma aislado se llama proposicién nula, y esta aqui para satisfacer este requisito. Lo colocamos en una -a aparte para que sea visible. ‘Antes de abandonar el programa para contar caracteres, obsérvese que si la prueba del while o del for no tiene éxito desde programa produce cero, Esto es importante. Uno de los aspectos agradables acerca, y del for es que hacen la prueba al inicio del ciclo, antes de proceder con el cuerpo. Sino hay nada que hacer, nada se hace, aun si ello significa no pasar a través del cuerpo del ciclo. Los programas deben actuar en form: snte cuando se les da una ‘entrada de longitud cero, Las proposiciones while y for ayudan a asegurar que los programas realizan cosas razonables con condiciones de frontera. printf utiliza %f tanto pare trabajo se realiza 1.5.3 Conteo de El siguiente programa cuenta lineas a la entrada. Como se meneioné anterior teca estandar asegura que una secuencia de texto de entrada pa- rezca una secuencia de ‘ada una terminada por un caracter nueva linea. ar caracteres nueva linea: include J+ cuenta las lineas de Ia entrada +/ El cuerpo del while con: mento + +nl. La proposic rentesis c que cont El de id = = es la notacin de C para expresar “igual a’ (como el = simple de Pascal o el .EQ. de Fortran). Este simbolo se emplea para SECCION 1 ENTRADA YSALIDA DECARACTERES 21 distingui uueba de igualdad del = simple liza C para la asignacion, Un mensaje de alerta: los principiantes de C ocasionalmente escriben = cuando cen realidad deben usar = =. Como se verd en el capitulo 2, el resultado es por neral una expresidn legal, de modo que no se obiendra ninguna advertencia, Un cardcter escrito entre apéstrofos representa un valor entero igual al valor numérico del cardcter en el conjunto de caracteres de la maquina, Esto se llama tuna constante de cardcier, aunque s6lo es otra forma de escribir un pequeno ente- ro. Asi, por ejemplo ‘A’ es una constante de cardcter; en el conjunto ASCII de car -est0 es, la representacidn interna del caracter A. Por su- puesto ‘A’ es preferible que 65: su significado es obvio, y es independiente de un conjunto de caracteres en particular. es de cadena tambien son le: simple, y en ex constante cadena que contiene s6lo un cardcter. En el capi de cadenas versus caracteres. Ejercicio 1-8. Eser y nuevas lineas. 6 Ejercicio 1.9. Escriba un programa que copie su entrada a la salida, reemplazando cada cadena de uno o més blancos por un solo blanco. 5 tun programa que cuente espacios en blanco, tabuladores Ejercicio 1-10, Escriba un programa que copie su entrada ala sal do cada tabulacién por \t, cada retroceso por \b y cada di \. Esto hace que las tabulaciones y los espacios sean visibles sin confusiones. 0) 1.5.4 Conteo de palabras El cuarto en nuestre serie de programas tities cuenta las lineas, palabras y ca- racteres, usando la definicién de que una palabra es cualquier secuencia de carac- teres que no contiene espacio en blanco ni tabulacién nj nueva linea. Esta es una versién reducida del programa we de UNIX. include define IN 1 /+ en una palabr define OUTO —_/ fuera de una palabra «/ J» cuenta lineas, palabras, y caracteres de la entrada «/ main() { int, al, nw, ne, state; 22. INTRODUCCION GENERAL eaprTuto1 state = OUT; al = nw = ne = 0; while ((c = getchar( )) != EOF) ( if(e == \n) rintf ("96d 9d d\n", nl, nw, ne); , Cada vez que el programa encuentra el primer cardcter de una palabra, conta- biliza una palabra més. La variable state registra si actualmente el programa est © no sobre una palabra; al iniciar es "*no esta sobre una palabra”, por lo que se asigna el valor OUT. Es preferible usar las constantes simbélicas IN y OUT que los valores literales 1 y 0, porque hacen el programa mas legible. En un programa inima, pero en programas mas grandes cl esfuerzo extra que se haya realizado para escribir de esta manera desde el io. También se descubrira que es més Facil hacer cambios extensivos en programas donde los mimeros magicos aparecen sélo como constantes simbélicas. La linea al = nw = ne = 0; inicializa a las tres variables en cero. Este noes un caso especial sino una conse- cueneia del hecho de que una asignacién es una expresi6n con un val las asignaciones se asocian de derecha a izquierda. Es como si se hul al = (nw = (nc = ‘sic ¢s un blanco 0 ¢ es nueva linea o ¢ es un tabuladoi secuencia de escape \t es una representacién vis se evaltan de izquierda a derecha, y se garantiza que la evaluacién terminard tan pronto como se conozca la verdad o falsedad. Si c ¢s un blanco, no hay necesidad de probar sies una nue- va linea o un tabulador, de modo que esas pruebas no se hacen. Esto no es de SECCION 6 ARREGLOS 23 particular importaneia en este caso, pero es significativo en situaciones mas com- das, como se Yerd mas adelante. .jemplo muestra también un else, el cual especifica una accién alternativa sila condicién de ina proposicién if es falsa. La forma general es it (expresicn) proposicién, else roposicién, én puede ser una proposi el programa para contar palabras, la que estd d que controla dos proposiciones entre laves, iCémo probaria el programa para contar palabras? {Qué clase més conveniente para descubrir errores si éstos existen? 0 Ejercicio 1-12, Escriba un programa que imprima su entrada una palabra por linea. 0 1.6 Arreglos F de caracteres espaciadores ‘otros caracteres. Esto es Cen un programa. Existen doce categorias de entrada, por lo que es conveniente utilizar un arre- slo para mantener el mimero de ocurrencias de cada digito, en lugar de tener diez variables individuales. Esta es una versin del program: deo, lancos, tabuladores, nueva linea), y de todos los icioso, pero nos permite ilustrar varios aspectos de include + cuenia digios, espacios blancos, y otros */ rain( ) 9, nother; 24 TRODUCCION GENERAL caprTuLoy while ((c = getchar( )) 1= EOF) (ce >= Uae <= 9) + +ndigitle~07;, ele if (¢ = = =e + +nwhite; else print for (= 0; 1 < 10; ++!) printi(* %d”, ndigitli); sppacios blances = %éd, otros = Séd\n", , nother); La salida de este programa al ejecutarlo soore si mismo es digitos = 93.000.0.0001, espacios blancos = 123, otros = 345, La declaracién int ndigit (10); declara ndigit como un arreglo de 10 enteros. En C, los subindices de arreglos snzan en cero, por lo que los elementos son ndigit{0], ndigit (1), ..., ndi- Esto se refleja en los ciclos for que inicializan ¢ imprimen el arreglo. Un subindice 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 representacién de los digitos como caracteres. Por ejemplo, la prueba f(e>= Va&e <= 9) determina si el cardcter en ¢ es un digito. Si lo ¢s, el valor numérico del digito es °-0 Esto solo funciona si ‘9! tienen valores consecutivos ascendentes. Por fortuna, esto es asi en todos los conjuntos de caracteres.. Por definicién, los char son sélo pequetios enteros, por lo que las variables y las constantes char son idénticas a las int er expresiones aritméticas, Esto es natural y conveniente; por ejemplo, ¢~’0' es una expresién entera con un valor entre 0 y 9, correspondiente a los caracteres °C’ a °9" almacenados en ¢, por lo ue es un subindice valido para el atreglo ndigit. La devisin de si un caracter es digito, espado en blanco u otra cosa se realiza con la secuencia We >= 0 8&e <= 9) + +ndigit(o~ SECCION Ls ARREGLOS 25 ==) El patron sf (condicién,) ropesicién, lee if (condicisn) also proposicién, frecuentemente en programas como una forma de expresar una deci le. Las condiciones se evalian en orden desde el principio hasta que se satisface alguna coniticién; en ese punto se ejecuta la proposicién correspon- varias proposiciones entre Cuando se como se hizo en el programa para con- Puede haber cualquier mimero de 3, proporciona of larmente apropiada cuando la Qn ¢s determinarsi alguna expresidn entera o de cardcter corresponde con miembro de un ccnjunto de constantes. Para contrastar, se presentara un version de este programa, usando switch, en la seccidn 3.4, ygrama de las frecuen- da. 26 INTRODUCCION GENERAL carrtuto1 1.7 Funciones En Ienguaje C, una furién es el equivalente a una subrutina o funcién en Fortran, 0 a un procedimiento 0 funcién en Pascal. Una funcién proporciona tuna forma conveniente de encapsular algunos célculos, que se pueden emplear después sin preocuparse de su implantacién. Con funciones diseadas adecuada- mente, es posible ignorar como se realiza un trabsjo; es suliciente saber qué se hace. El lenguaje C hace que el uso de funciones s conveniente y eficien- te; es comiin ver una funci6n corta definida y empleada una sola vez, inicamente porque eso esclarece alguna Hasta ahora ‘m,n), que eleva un entero m a una potencia entera y post Esto cs, el valor de power(2,6) es 32. Esta funcion no es una rutina de exponen- ‘ica, puesto que sdlo maneja potencias positivas de enteros pe- ustios, pero es funcidn pow(x,y) que cale ‘A continuacion se presenta la funci6n power y un programa main para util zarla, de modo que se vea la estructura completa de una vez. include int power(int m, int 2); ‘ prusba la funcién power «/ main( ) J+ power: eleva la base a la n-dsima potencia; n >= 0 «/ int power(int base, int n) { sECCION 17 FuNciones 27 Una definicién de funciéa tiene la forma siguiente: tipo-de-retorno nomre-de-funcién (declaracién de pardmetres, si los hay) { declaraciones roposiciones jones de funcién pueden aparecer en cualquier orden y en uno 0 pero una funcién no puede separarse en archivos diferent pareve en varios archi ‘vez se tengan que especificar mas cosas al compilar y carg: Fa en uno solo, pero es0 es cosa del sistema operativo, no un atributo del lenguaje. Por ahora supondremos que ambas funciones estén en :1 mismo archivo y cualquier cosa que se haya aprendi- do acerea de cOmo ejecutar programas en C, alin funcionaran a funcién power se invoca dos veces por main, en la linea ‘bd %d Mala", pasa dos formato y rl c mai que se por inea de la funcién power, int power(in base, iat 2) is rutinas pueden Lutilizar los mismos nombres sin que exista problema alguno. Esto también es cier- 0 para las variables i y p: la i de power no tiene nada que ver con la i de main. Generalmente usaremos pardmetro para una variable nombrada en la lista en- re parémtesis de la definicén de una funcion, y a do al hacer la llamada de la funcién. Los términos return expresion Una funcién no necesita regresar un valor; una propo: fa al “‘caer al final’ de una funcién al alcanzar la llave derecha de ién. Ademés, la funcién que llama puede ignorar el valor que regresa Probablemente haya notado que hay una proposicién return al final de main, Puesto que main es una furcién como cualquier otra, también puede regresar un valor a quien la invoca, qu: es en efecto el medio ambiente en el que el programa 28 INTRODUCCION GENERAL capituto se ejecuta, Tipicamente, un valor de regreso cero implica una terminacién nor- mal; los valores diferentes de cero indican condiciones de terminacién no comu- nes 0 erréneas. Para buscar la simplicidad, se han cmitido hasta ahora las propo- siciones return de las funciones main, sn mas adelante, como un recordatorio de que los programas deben regresar su estado final a su medio am- biente La detlaracién int power(int m, int 0); totipo, debe coincidir con la definicién y uso de power. Es un error el que la defi- nicién de una funcién o cualquier uso que de elle se haga no corresponda con su prototipo. Los nombres de los parémetros no necesitan caincidir; de hecho, son optat vos en el prototipo de una funcién, de modo que para el prototipo se pudo haber escrito {int power(in No obstante, unos nombres bien seleccionados son! una buena documentacién, por lo que se emplearan frecuentemente. Una nota histérica: La mayor modificacién entre ANSI C y las versiones ante- ses c&mo estn declaradas y definidas las funciones. En la definicién origi- nal de C, la funcién power se pudo haber escrito de la siguiente manera: J+ power: olova la baco a n-teima potencia; n >= 0 +/ h (versién en estilo antiguo) +! power(base, 1) int base, { Pp for(i = ji <= a; ++) P= ps base; return pi los paréntesis y sus tipos se declaran antes de anterior.) del programa pudo haberse visto como sigue: uerpo de la funcién es igual a de power; No se permitié ninguna lista de parémetros, de modo que el compilador no pudo revisar con facilidad que power fuera llamada correctamente. De hecho, puesto SECCION 1 |ARUMENTOS—LLAMADA POR VALOR 29 que por omisién se podia suponer que power regresaba un entero, toda la decla~ racion podria haberse onitido. La nueva sintaxis de ‘os prototipos de funciones permite que sea mucho més para el compilador detectar errores en el niimero 0 tipo de argumentos. El estilo de declaracién y definicin ain funciona en ANSI C, al menos por 1.8 Argumentos—llamadas por valor Hay un aspecto de las funciones de C que puede parecer poco familiar a los progratiadores acostumbrados a otros lenguajes, par § argumentos de una funcidn se pasan “‘por val fica que funcidn que se invoca recibe los valores de sus argumentos en variables tempo- nales. Esto conduce a algunas propiedades diferentes a las umadias por referencia" como Fortran o con paré- (0s varen Pascal, en donde la rutina que se invoca tiene acceso al argumento iginal, to a una copia local. La diferencia principel ¢s que en C Ia funcién que se invoca no puede alterar fectamente una variable de la funcidn que hace la llamada; s6lo puede moi car su copia privada y temporal llamada por valor es una ventaja, no una desventaja. Por lo ‘a eaborar programas més compactos con pocas variables lizadas. Por ejemplo, he aqui una versién de power que utiliza este propiedad. J+ power: eleva la base a la n-6sima potoncia; n> =0; versién 2 +/ int power(int base, int n) { int p; for (p = 1; 2 > 0; —-n) pa peb temporal, y se decrementa (un ue se ejecuta hacia “ga a cero; ya no es necesaria la variable 4, Cualquier cosa que se Ie haga a n dentro de power no tiene efecto sobre el argu: mento con el que s¢ Ilam6 originalmente power. 30 1NTRODUCCION GENERAL caprTuto es posible hacer que una funcién modifique una va- ‘ina invocada. La funcién que llama debe proporcionar lun apuntador a la va- invoca debe declarar que el parametro sea un apuni le indirectamente a través de él. Los apuntador La historia es diferente con los arreglos. Cuando el nombre de un arreglo se emplea como argumento, el valor que se pasa a la funcién es la localizacién o rreglo —no hay copia de los elementos del arrealo. lor, la funcién puede tener acceso y alterar cual- es el tema de la siguiente seccién. digo es bastante simp bile (hay otf i (es mds larga que fa anterior més larga) udrdata ‘guarda su longitud Jimprime ta linea més larga Este pseudocddigo deja en claro que el programa szdivide naturalmente en par- tes. Una trae una nueva linea, otra el proceso. lad deberd retornar la lor 10. Cero es un regreso de id de linea valida. Cada linea de texto yea que séle contenga un cardcter nueva do a que nunca es una lor menos un cardeter; incluso una spccioN 19 ARREGLOS DECARACTERES 31 include (J+ imprime la Kea ds entrada mds larga «/ rain( ) { int len; /+ Tongitud actual de la linea -/ Int max; J + Axima longitud vista hasta el momento +/ ‘char line(MAXLINE]; linea de entrada actual «/ ‘char longest(MAXLINE]; \nea més larga se guarda aqut +/ J» ubo una linea «/ longest , regresa ou longitud / =EOF && ol="\n'; + +3) 32. INTRODUCCION GENERAL carro: Las funciones getline y copy estn declaradas al principio del programa, que se supone esta contenido en un archivo. ‘main y getline se comunican a través de un par de argumentos y un valor de retorno. En getline los argumentos se declaraa por I Int gatline(char im) que especifica que el primer argumento, s, es un arreglo, y el segundo, lim, es un entero. El propésito de proporcionar 1 regresa un int; puesto que iat es el valor de retorno por omi- sidn, puede suprimirse ‘Algunas funciones regresan un valor itl; otras, como copy, se emplean éini- ccamiente por su efecto y no regresan un valor. El tipo de retorno de copy es void, cl cual establece explicitamente que ningtin valor se regresa. En getline se coloca el cardcter “\O’ (cardeter nulo, cuyo valor es cero) al final del arreglo que estd creando, para marcar el fin de la cadena de caracteres. Esta con- ion también se utiliza por el lnguaje C: cuando una constante de cardcter como “hola aparece en un programa en C, se almacena como un arre ccaracteres de la cadena y termi La especificacién de formato %s dentro de print espera que rrespondiente sea una cadena represemtada de «ste modo; copy también se basa en el hecho de que su argumento de entrada se termina con '\0’, y copia este ca- rdcter dentro del argumento de salida. (Todo ica que ‘\0', no es parte de un texto normal.) Es ttl mencionar de paso que aun un programa tan pequeno como éste pre- senta algunos problemas de diseito. Por ejemplo, ;qué debe hacer main si en- cuentra una linea que es mayor que sy ine trabaja en forma segura, en ese caso detiene la recopilacién cuando el arreglo 7 argumento co- iar) cual es el tamafio de la cadena, por lo que decidimos no agregar comprobacién de errores en ella VARIABLES EXTERNASYALCANCE 33 modo que imprima ccrrectamente la longitud de lineas de entrada arbitrariamen- largas, y tanto texto como sea posible. Fjereieo 1-17, Escrita un programa que imprima todas ls lineas de entrada que sean mayores de 80 caracteres. 0 jercicio 1-18, Escriba un programa que elimine los blancos y los tabuladores aque estén al final de cada linea de entrada, y que borre completamente las lineas en blanco. © Ejercicio 1-19, Escriba una funcin reverse(s) que invigrta la cadena de caracte- +. Usela para escribir un programa que invierta su entrada, linea a linea. © funcién, y desaparece cuando la fun por lo que tales variables son conocidas como variables automét inologia de otros lenguajes. Aqui se utilizard en adelante ‘mético para hacer referencia a esas variables locales. (En el cay categoria de almacenamiento estética, en la que las variables locales si eo vvan sus valores entre llamadas.) Debido a que las variables locales aparecen y desaparecen con la invocacién de ‘que son externas a tocas las funciones, esto es, variables a las 4) Puede tener acceso por stu nombre. (Este mecanismo es parecido al COMMON de Fortran o a las varicbles de Pascal declaradas en el bloque mas exterior.) Debi- do a que es posible tener acceso global a las variables externas, éstas pueden ser sadas en lugar de listas de argumentos para comunicar datos entre funcioncs. Ademés, puesto que ‘as variables externas se mantienen permanentemente en cia, en lugar de aparecer y desaparecer cuando se laman y terminan las inciones, mantienen sus valores aun después de que regresa la funcién que los sup Variable externa debe denise, exactamente una vez, fuera de cualquler luncién; es 34 INTRODUCCION GENERAL ‘carr programa de la linea més larga con line, longest y max como variables externas, Esto requiere cat #include smano de una linea de entrada «. ‘#doline MAXLINE 1000 /+ maxim .axima longitud vista hasta el momento «/ 1a de entrada actual « larga se guarda aqui +/ le entrada més larga; versién especializada */ exten int max; extern char long ft ois extern char line(]; for (i = 0; 1 < MAI 6 (c= getchar( )) != EOF 8c != “\n'; + +i) jar las Hamadas, declaraciores y cuetpos de las tres funciones. SECCION 110 YVARIABLESEXTERNAS YALCANCE 35 Iineli] = 0% retum j } (+ copy: versiée especializada +/ void copy(void) { int cextem char line(], longest{]; } as son exactamente como las definiciones de variables locales, pero puesto que ‘curren fuera de las funciones, las variables son externas. Antes de que una fun- \6n pueda usar una variable externa, se debe hacer saber el nombre de la variable la funeién. Una forma de hacer esto es escribir una declaracién extern dentro la funcién; la declaracién es la misma que antes, excepto por la palabra reser- vada extern, Bajo ciertas circunstancias, la declaracién se puede omitir. Si la defini- \6n de una variable externa ocurre dentro del archivo fuente antes de su uso por ina funcién en particular, entonces comiin, es poner ternas al principio del archivo fuente y después omit tern. junciones en un archivo sepa- sader, que es incluido por #include al principio de cada archivo fuente, El sufijo .h se usa por convei ‘ers. Las funciones de la biblioteca estindar, por ejem| 'eaders como . Este tema se trata ampliamente en el capitulo 4, y la foteca en el capitulo 7 y en el apéndice B. 36 TRODUCCION GENERAL ‘caprrutoy Puesto que las versiones especializaclas de getline y copy no tienen argumen- tos, la I6gica sugeriria que sus prototipos al principio del archivo deben ser getli- y copy( ). Pero por compatibilidad con programas de C anteriores, cl estan~ dar toma a una lista vacia como una declaracién al viejo estilo, y suspende toda revision de listas de argumentos; para una lista explicitamente vacia debe emplearse la palabra void. Esto se discutird en el capituio 4. Se debe notar que empleamos cuidadosamente las palabras definicién y decla- racién cuando nos referimos a variables externes en esta seccién. La palabra ‘de- ” se refiere al lugar donde se crea la variable 0 se le asigna un lugar de almacenami ‘declaracién"’ se reflere al lugar donde se establece la naturale- za de la variable pero no se le asigna espacio. ‘A proposit una tendencia a convertir todo en variables extern, debido a que aparentement las comunicaciones —Ias 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 e5 peligroso, puesto que lleva a programas cuyas cone- xiones entre datos no son completamente obvias —las variables pueden cambiar- se en forma inesperada ¢ inadvertida, y el programa es dificil de modificar. La segunda versién del programa de la linea mayo: es inferior a la primera, en parte por las anteriores razones y en parte porque destruye la generalidad de dos titles funciones, introduciendo en ellas los nombres de las variables que manipula. Hasta este punto hemos descrito lo que podria lamarse los fundamentos con- vencionales de C. Con estos fundamentos, es posible escribir programas itiles de tamafio considerable, y probablemente seria uma buena idea hacer una pausa sufi- cientemente grande para realizarlos. Estos ejercicios sugieren programas de com- plejidad algo mayor que los anteriores del cari Ejercicio 1-20. Escriba un programa detab que reemplace tabul trada con el nimero apropiado de blancos para espaciar hasta el de tabulacién. Considere un conjunto fijo de paros de tabulacién, digamos cada zn columnas. ;Debe ser n una variable o un parémetro simbslico? 0 Ejercicio 1-21. Escriba un programa entab que reemplace cadenas de blancos por el minimo niimero de tabuladores y blancos para obtener el mismo espaciado. re los paros de tabulacién de igual manera que para detab. Cuando un tabulador 0 un simple espacio en blanco fuese suficiente para aleanzar un paro de tabulacidn, ja cudl se le debe dar preferencia? 0 ‘doblar"” lineas grandes de entrada en \ocardcter no blanco que ocurra an- ina de entrada. Aseguirese de que su programa se comporte ymente con lineas muy largas, y de cue no hay blancos o tabuladores antes de la columna especificada. 0 Ejercicio 1-23. Escriba un programa para elirtinar todos los comentarios de un programa en C. No olvide manejar apropiadamente las cadenas entre comillas y las constantes de cardcier. Los comentarios de C no se anidan. SECCION 0 YARIABLESEXTERNAS VALCANCE 37 jercicio 1-24. Escriba un programa para revisar los errores de rudimen- ineados. No comentarios, capituLo 2: Tipos, operadores y expresiones Las variables y las constantes son los objetos de datos basicos que se manipu- un programa. Las declaraciones muestran las variables que se van izar y establecen el tienen y algunas veces cuales son sus valores i ciales. Los operadores especifican lo que se hard con las variables. nes combinan variables y constantes para producir nuevos valores. El ‘objeto determina el coajunto de valores que puede tener y qué operaciones se pue- den realizar sobre él. Estos son los temas de este capitulo. El estindar ANSI ha hecho muchos pequeflos cambios y agregados a los ti- pos basicos y a las expresiones. Ahora hay formas signed y unsigned de todos los tipos enteros, y notaciones para constantes sin signo y constantes de cardcter hhexadecimales. Las op:raciones de punto flotante pueden hacerse en precision sen- la; también hay un tipo long dout mn extendida. Las constantes de cadena pueden concatenarse a pendiente por mucho impide que cambien. ipos aritméticos se aumentaron pa- ipos. ra manejar el ahora mas rico conjunto d 2.1 Nombres de variables Aunque no lo mensionamas en el capit los nombre de las vat 1, existen algunas restrieciones en s y de las constantes simbi + Puesto que las rutinas de bi as may(isculas y mintisculas son 40 1170S, OPERADORES Y EXPRESIONES caprrutoz ‘Al menos los primeras 31 caracteres de un nombre interno son si para nombres de funciones y variables externa ‘0 puede sei 31, puesto que los nombres externos los pueden usar los ensambladores y los ear~ sgadores, sobi iene control, Para nombres externos, el es- anda sminiscul se pueden uti rmimisculas. Es conveniente elegir nombres que izar nombres corios para variables locales, especialmente indices de jomibres mas largos para variables externas. 2.2 Tipos y tamafios de datos Hay unos cuantos tipos de datos basicos en C: char um solo byte, eapaz de contener un cardcter del conjunto de local int tun entero, normalmente del tama natural de fos enteros en la rmiquina en la que se ejecuta. float punto flotante de precsién normal double punto flotante de doble precisién. Ademés, existen algunos calificadores que se aplican a estos tipos basicos. short y long se aplican a enteros: short int sh; long int counter; Lapalabra int puede omitirse de tales declaraciones, lo que tipicamente se hace. jencién es que short y long puedan proporcionar diferentes longitudes ée enteros donde sea préctico; int ser normalnente el tamafo natural para una ‘miquina en particular. A menudo short es de16 bits y long de 32; int es de 16 ‘ode 32 bits. Cada compilador puede seleccionar libremente los tamafios apropia~ su propio hardware, sujeto slo a la restriccién de que los shorts e ints son, por Io menos de 16 bits, los longs son por lo menos de 32 bits y el short no, ‘a su vez no es mayer que long. ‘ex tanto que las variables signed char tienen valores entre ~128 y 127 (en una ndquina de complemento a dos). El hecho de que los chars ordinarios sean con seccioN.3 CONSTANTES. 41 a, pero los cat fes que se pueden impri especifica punto flotante de precision extendida. Igual los tamafios de objetos de punto flotante se definen en la double y long double pueden representar uno, dos o tres ta- ba us programa para determinar los rangos de variables char, nto signed como unsigned, imprimiendo los val 10s de punto flotante. termine los rangos de los varios 2.3. Constantes centera como 1234 ¢s un int. Una constante long se escribe con somo en 123456789L; un entero demasiado grande para también seré tomado como long. Las co ie 10 se escriben con una uo U, terminal y el sufijo ul o UL indica unsigned long, Las constantes de punto flotante contienen un punto decimal ponente (1e~2) 0 ambos; su ‘a menos que tengan jos {0 F indican una constant El valor de un entero puede especiti gar de decimal. Un 0 (cero) al princi -a hexadecimal. Por ejem y Oxlf 6 OXIF en hexadeci ibiéa pueden ser seguidas por L para conver U para haverlas unsigned: OXFUL es una constante unsigned long con valor de 15 en decimal. 1o L indiean un long double. ccardcter en el conjunto ée caracteres de la maquina. Por ejemplo, en el de caracteres ASCII el cardcter constante ‘0' tiene el valor de 48, el cual no esta relacionado con el valor 1umérico 0. Si escribimos ‘0’ en vez de un valor numérico como 48 que depende conjunto de caracteres, el programa es independiente ‘como cualesquier otros enteros, au ‘mtinmente en comparaciones con ot Ce 42. _T1P0s, OPERADORES Y EXPRESIONES caprruoa como dos caracteres, pero representan s6lo uno. Ademas, un patron de bits ar~ bitrario de tamafo de un byte puede ser espesificado por Nove! en donde o00 son de uno a tres digitos octales (0...7) 0 por "hat en donde Ad son uno o mas digitos hexadecimales (0...9, a...f, A...F). Asi podria- ‘mos escribir #doline VTAB \O13'—_/+ tab vertical ASCII «/ ‘#deline BELL \007 + cardcter campana A‘ ©, en hexadecimal, #dofine VIAB ‘\xb' + tab vertical ASCI ‘Pdefine BELL \x7" J+ cardcter campana ASCII «/ El conjunto completo de secuencias de escape es \a_ cardcter de alarma (campana) \\ diagonal invertida Yb retroceso \E avance de hoja \n nueva linea \r-_regreto de carro Mt tabulador ado er ‘\O" representa el caracter con val ibe en vez de 0 para anfati e algunas expresiones, pero el valor numérice es precisamente 0. Una expresién constante es una expresién que s6l 1ye constantes. Ta- les expresiones pueden ser evaluadas durante la compilacién en vez de que se haga fen tiempo de ejecucién, y por tanto pueden se- utilizadas en cualquier Iugar en que pueda encontrarse una constante, como en ‘#dofine MAXLINE 1000 cchar line [MAXLINE + 1); ‘#deline LEAP 1+ en afios bisiestoss/ int days (31+28+LEAP +31 +30+31 + 90+31+31+30+31+30+31); Una constante de cadena o cadena literal, es una secuencia de cero 0 mas ca- racteres encerrados entre comillas, como en "Soy una cadona”” ‘js Ta cadena vacia «/ secci0n23 CCONSTANTES 43, Las comillas no son parte de Ia cadena, sélo sirven para delimitarla. Las mismas clas de escape utilizadas en constantes de caracter se aplican en cadenas; \ representa el car las. Las constantes de cadena pueden ser concate nadas en tiempo de compi “hola,” mundo” es equivalence a “hola, mundo” lo ‘\0 al final, de modo que $ uno mas del niimero de caracteres escritos significa que no hay limite en cuanto a los programas deben estdndar regresa la I cluyendo el ‘\O' terminal. Aqui esta nuestra versién: ="0) strlen y otras funciones para cadenas estan declaradas en el header estindar . Se debe ser cuidadoso al distinguir entre una vor te de cardcter y una cadena lerucién. Una enumeracion a de valores enteros constantes, como en ‘enum boolean (110, YES}; El primer nombre en un enum tiene valor 0, el siguiente 1, y asi sucesivamente, ‘4 menos que sean especificados valores explicitos. Si no todos los valores son es- 44 1170s, oPERADORESY EXPRESIONES carcruio 5 no especificados continiian io fue, como en el segunclo de eso: enum escapes { BELL = ‘\e', RETROCESO = ‘\b', TAB = "\!, NVALIN = ‘\n’, VIAB = ‘\v, RETURN = ‘\r}, a partir det enum months {ENE = 1, FEB, MAR, ABR, MAY, JUN, FEB es 2, MAR es 3, Los nombres que estén en enumeraciones diferentes deben ser distintos, Los va- lores no necesitan ser di Las enumeraciones proporcionan una manera conveniente de asociar valores constantes con nombres, un menudo mejor que #de! depurador puede ser mir los valores de variables de enumeracién en su forma simb 2.4 Declaraciones Todas las variables deben ser declaradas antes de su uso, aunque ciertas decla- raciones pueden ser hechas en forma implicita por el contexto. Una declaracién ‘specifica un tipo, y contiene una lista de una o mis variables de ese tipo, como en int lower, upper, step: char ¢, line (10; fas entre las declaraciones en cualquier forma; ta de arriba podria igvalmente ser escrita como har line (000); ‘ima forma ocepa mas espacio, pero es conveniente para agregar un co- io cada declaracién © para modificaciones subsecuentes Una variable tamtién puede ser inicializada en su declara ¢s seguido por um signo de igual y una expresin la expresion sirve como un ador, como en secciona.s OPERADORES ARITMETICOS. 48 ese = WV i=G; limit = MAXLINE+1; dda cada vez que se entra a la funcién o bloque en dor puede ser cualquier expresion. Las vari eas y externas son para las que no hay unis basura. -ador const puede aplicarse a la declaracién de cualquier variable pa: ‘ar que su valor no sera cambiado. Para un arreglo, el calificador efectiia un intentode cambiar un const, el resultado est definido por la implantacién. 2.5 Operadores aritméticos Los operadores aritméticos binarios son +, ~, «, /, vel operador médulo %. La divisidn entera trunca cualquier parte fraccionaria. La expr x%y produce el residuo cuando x es dividido entre y, por lo que es cero cuando y divi- de a x exactamente, Por ejemplo, un afio es bisiesto si es divisible entre 4 pero no entre 100, excepto aquellos afios que son divisibles entre 400, qui bisies- 105, Por lo tanto if (Gear % 4 == 0.88 year % 100 != printf("%d os un ano bisiesto\n, else printi("%éd no os un afio Disiosto\ year % 400 = = 0) “ yoar); El operador % no puede aplicarse a operandos truncamiento para / y elsigno del resultado de % si para operandos negatives, asi como la accién que se toma en caso de sobreflujo © subflujo. 46 1170s, OPERADORES Y EXPRESIONES. carrruto2 s + y— tienen la misma precedencia, la cual es menor 'y %, que a su vez esmenor que + ¥ ~ unarios, Los sha Los operadores binat que la precedencia de ‘operadores aritméticos se asocian de izquierda a der La tabla 2-1 que se encuentra al final de este capi y asociatividad para todos los operadores. resume la precedencia 2.6 Operadores de relacién y ldgicos Los operadores de relacién son > >e= n= lens return a; <= 9) +49) Tal como se discutié en el capitulo 1, la expresién da el valor numérico del cardcter almacenado en s[i], debido a que los valores etc., forman una secuencia ascenderte contigua. ejemplo de conversion de chara intes le f lower, que cor a miniiscula para el conjunto de caracteres ASCIT. Si el cardcter mayiscula, lower lo regresa sin cambio. convierte ¢ a miniscule; solamente ASCII «/ int lower(int ¢) { if(e >= /A' 880 <= 7) return © + 'a!— else return ' Esto funciona para ASCII debido a que las co-respondientes letras mayusculas y minisculas estin a una distancia fija como valores numéricos y cada alfabeto es contiguo ino letras entre A y Z. Sin embargo, esta altima observa- cidn no es cierta para el conjunto de caracteres EBCDIC, asi que este codigo eo= Uae c= puede reemplazarse por indigit(c) Nosotros utilizaremos las funciones de en adelante. Existe un sutil punto acerca de la conversiéa de caracteres a enters: El len- ‘guaje no especifica si las variables de tipo char son valores con o sin signo. Cuan- do un char se convi La respuesta varia de una maquina a oti ra. Bn algunas méquinas un char cuyo iza que ningiin caract {andar de caracteres de impresion de la es siempre seran cani esté en el conjunto ive, de modo que secc10n27 CONVERSIONES DE TIPO. 49, parecer como negativos en algunas maquinas, aunque sean positivos en otras. sransporiabilidad, se debe especi mned o unsigned si se van a almace- 1 datos que no son caracteres en variables tipo char. Las expresiones de relacién como i > j y las expresiones légicas conectadas por && y || estén definidas para tener un valor de 1 siendo verdaderas, y 0 al ser falsas. De este modo, la asignacién dsc>=USbcc= 9 y0si no lo es. Sin embargo, las funciones como isdi- rrente de cero como verdadero. En la parte "es sélo “diferente de cero”, por hace ladsicesun deval Jo que esto no hace Las conversiones ait un operador como + 0 + ai operandos de diferentes tipos, el tipo '*menor’” es promo’ antes de que la operacén proceda. El resultado es el del tipo mayor. La seccién 6 del apéndice A estatleve las reglas de conversién en forma precisa. Si no hay operandos unsigned, sin embargo, el siguiente conjunto informal de reglas bas- tard: Si cualquier operando es long double, conviértase el otro a long double. De otra manera, si cualquier operando es double, conviértase el otro a double. De otra manera, si cualquier operando es float, conviértase cl otro a float. De otra manera, conviértase char y short a Después, si cualquier operando es long, conviértase el otro a long. Nétese que los float que estan en una expresién no se convierten automética- to ¢s un cambio de la definiciGn original. En general, las fun- de utilizardn doble precisién. La razon horrar espacio de almacenamiento en arreglos gran- suencia, ahorrar tiempo en méquinas en donde la aritme- a de doble preci particularmente costosa. Las realas de conversion son més complicadas signed. El problema es que las comparaciones de valores con signo y sin signo nt dependientes de Ia maquina, debido a que dependen de los tamaiios de varios tipos de entsros. Por ejemplo, supdngase que int es de 16 bits y long de 32, Entonces ~1L < 1U, debido a que 1U, que es un int, es promovido a yned long. Pero-1L > 1UL, debido a que ~1L es promovido @ unsigned long Y asi parece ser un gran niimero positive. des 0, con menor ‘50 TIPOS, OPERADORES Y EXPRESIONES carrruLo2 jen Tugar en las asignaciones; el valor det lado 0 de Ia izquierda, el cual es el tipo del resultado. jo a un entero, tenga o no extensién de signo, como, Las conversiones tam! derecho es convertido al Un cardcter es conver se describié anteriormente. Los enteros mis largos son convertidos a cottos o a char desechando el exceso. de bits de mas alto orden. Asi en int i; char ¢; l valor de © no cambia, Esto es verdadero ya sea que se inmiscuya 0 no la ex tension de signo. Sin embargo, el invertir el orden de las asignaciones podria producir pérdida de informacién. Sixes float e ies int, entonces x = iei = xproducirdn conversiones; de float 2 int provoca el truncamiento de cualquier part: fraccionaria, Cuando double se convierte a float, el que se redondee o trunque el valor es dependiente de la im- plantacién. Puesto que un argumento de la lamada a una funcién es una expresion, tam- bien suceden conversiones de tipo cuando se pasan argumentos a func ausencia del ipo de una funcién, char y short pasan asser int, y hace doble, Esta es la razn por la que hemos declarado los arguimentos a funcio- nes como int y double, aun cuando la funcidn se llama con char y float. Finalmente, 1a conversin explicita de tipo puede ser forzada (“‘coacciona- da”) en cualquier expresién, con un operacor unario llamado cast. En la construccién (nombre-de-tipo) expresin la expresién es convertida al tipo nombrado, por las reglas de conversién anterio- - -adoprecisode un cast es como si a expresién fuera asignada a una variable del tipo especificado, que se utiliza entonces en lugar de la construc- i (eca sqrt espera un argumento de jo si maneja inadvertidamente es un entero, pode- sqrt(double) 1) el valor den a doble antes de pasarlo a sqrt. Notese que la conver- duce el valor de n en el tipo apropiado; nen si no se altera. El ia que otros operadores unarios, co- SeCCION2 (OPERADORES DEINCREMENTOYDECREMENTO $1 double sqri(dout llamada aie = sqrt(2) obliga al entero 2 a ser el valor double 2.0 sin necesidad de ningin cast. La biblioteca estindar incluye una implantacidn transportable de un genera- dor de niumeros pseudoaleatorios, y una funcion para inicializar la semilla; lo mero ilustra un cast: ‘unsigned long int next = 1; Js rand: regress un entero psoudoaleatorio en 0.22767 +/ rand(void) next = next + 1109515245 + 12345; rolurn (unsigned int)(nex/65536) % 32768; : + srands void srand(unsigied int seed) { Ja semilla para rand() «/ next = seed; ; jercicio 2-3. Escriba a funcién htoi(s), que convierte una cadena de di hhexadecimales (incluyendo Ox 6 OX en forma optativa) en su valor entero equiva ie. Los digitos permitidos son del 0 al 9, de laa ala, y dela A ala F, O 2.8 Operadores de incremento y decremento El ienguaje C proporciona dos operadores poco comunes para inerementar y decrementar variables. 5! operador de aumento + + agrega 1 a su operando, en tO que el operador dz disminucién ~~ le resta 1. Hemos usado frecuentemen- ++ para inerementar variables, como en if(e == \n) + +n; El aspecto poco comin es que + + y ~~ pueden ser utilizado como pr s de la variable, como en + +n), 0 como pos + +). En ambos casos, el efecto es incrementar n. Pero la expresion + + "menta a n antes de que su valor se utilice, en tanto que n+ + incrementa a 52. TIPOS, OPERADORES V EXPRESIONES carrrunos to don- a desptés de ques valor se ha empleado. Eso significa que en un eon jo utilizado, y no sélo el clecto, + +n y n+ +son Sines 5, entonces xentty asigna 5 a x, pero hace que x sea 6. En ambos casos, n se hace 6. Los operadores de increment ¥y decremento s6lo pueden aplicarse a variables; una expresién como (i+})+ + es de un contexto en donde no se desea ningin valor, sino solo el efecto jos y postfijos son iguales. Pero existen situaciones en donde se requicre umente uno u otro. Por ejemplo, considérese la funcién squeeze jina todas las ocurrencias del cardcter ¢ de una cadena s. = NOt +) ++] = lily sli] = NO , Cada ver que se encuentra un valor diferente de ¢, éste se copia en la posicion actual j, y s6lo entonces j es inerementada para prepararla para el siguiente cardc- Otro ejemplo de construceién semejante viene de la funcién getline que eseri- bimos en el capitulo 1, en donde podemos reemplazar seccion29 OPERADORESPARAMANEJODE BITS $3 por algo mas compacto como J+ encuentra ol fin de s «/ while ((l:+ +1 = t+ +)) 120) f+ copia t of } Como cada cardcter se copia de t a s, el ++ postfijo se aplica tam ma de squeeze(sl,s2) eardcter de la eadena # Fjereicio 2-8. Escrita la funcién any(sl,s2), que regresa la Ja cadena sl en donde se encuentre cualquier cardcier de la cadena #2, 0 ~1 sl no contiene caracteres de 62. (La funcién de biblioteca estandar strpbrk hace €l mismo trabajo pe‘o regresa un apuntador a la posicién encontrada.) © ccomplemento a uno (natio) ‘S4_“1P0S, OPERADORES Y EXPRESIONES carruto2 El operador AND de bits & a menudo es usado para enmascarar algiin con- junto de bits; por ejempl a= 2.80177; hace cero todos los bits de m, menos los 7 de menor orden. El operador OR de bits | es empleado para encender bits: x = x| SET_ON; fija en uno a todos los bits de x que son uno en SET_ON. El operador OR exclusivo * pone un uno en cada posicién en donde sus ope- iemplo, six es 1 y y es 2, entonces x & y es cero en tanto que x && y Los operadores de corrimiento << y >> realizan corrimientos a la izquier- derecha de su operando que esta a la izquierda, ‘dado por el operando de la derecha, el cual debe << 2 desplaza el valor de x a la izquierda dos po unsigned siempre 's Vacantes con cero, El correr lad signada llenara con bits de signo (‘‘corrimiento aritmé- en algunas maquinas y con El operador nario ~ da el complemento 2 uno de un ent te cada bit I en un bit O y viceversa. Por eiemplo, x= x07 de algunos de los operedores de cidn getbits(x,p,n) que regresa el campo de a bits de x (ajustado a la derecha) que principia en la posicién p. Se supone que la posicién del bit 0 esta en el borde derecho y que n y por sositivos adecuados. Por ejemplo, g regresa los tres bits que estan en la posicion 4 3 y 2, ajustados a la de Ja posicién p «/ ‘unsigned getbits(unsigned x, int p, { rotum (x >> (p+1-n)) &°(0 < > (p+ 1—n) mueve el campo deseado al borde derecho de la pa- SECCION2 10 OPERADORES DE ASIGNACIONYEXPRESIONES 85 Ejercieio 2-6. Escriba una funci que principian en la posicion p iguales a cambio. O ,Pyn,y) que regresa x con los n bits sa la derecha de y, deja 1 cambiado a0 y viceversa), dejando Ejereicio 2-8. Es in rightrot(x,n) que rearesa el valor del entero x rotado a la derecha 1 posiciones de bits. 2.10 Operadores de asignacién y expresiones Las expresiones tales como iaiee cen las que la variable del lado izquierdo se repite inmediatamente en el derecho, pueden ser escritas en ‘a forma compacta ite? Bl operador + = se llama operador de asignacién. La mayoria de los operadores binarios (operadores como + que tienen un coperando izquierdo y otto derecho) tienen un correspondiente operador de asig- nacién op=, en donde op es uno de Foi > BT! expr) y expr; son exsresiones, entonces expr, op= expr 6s equivalente @ expr, = (expr) op (expr) ;pluando que expr, se c en su argu- 56 TIPOS, OPERADORES V EXPRESIONES carrruLo2 cuenta bite Len x +/ [+ bitcount: int b; for (b = 0; x!= 0;x>>= 1) if & 0) bests i Declarar al argumento x como unsigned asegura que cuando se corre a la dere cha, los bits vacantes se llenardn con ceros, no con bits de signo, sin importar Ja méquina en la que se ejecute el programa, Muy aparte de su concisién, los operadores de asignacién tienen bs ventaja de después pon el r icai= i+ 2, Ademas, para una, yyvallyypvlp3+p4] + yypelpl+p2]] += 2 el operador de asignacién hace al cédigo més faci: de entender, puesto que el tor no tiene que verificar arduamente que dos expresiones muy largas son en reali dad iguales, o preguntarse por qué no lo son, y un operador de asignacién puede incluso ayudar al compilador a producir eédigo mas eficiente. ‘Ya hemos visto que la proposicién de asignacion tiene un valor y puede estar dentro de expresion jemplo mas comiin es while ((¢ = getchax( )) != EOF) Los otros operadores de asignaci6n (+ =,— =, etc.) también pueden estar dentro de expresiones, aunque esto es menos frecuente. En todas esas expresiones, el tipo de una expresién de asignacién es el tipo de su operando del lado izquierdo, y su valor es elvalor después de la asignacién, Ejercicio 2-9. En un sistema de nimeros de complemento a dos, x 6&= (x-1) bo- rra el bit | de més a la derecha en x. Explique el Forqué. Utilice esta observacién para escribir una versién mas répida de bitcount. 1] 2.11. Expresiones condicionales Las proposiciones (a> b) else : a=b PRECEDENCIA YORDENDEEVALUACION ST jonal, escrita con el opera- fa para escribir ésta y otras dor ternario " sciones semejantes. En la expresién expr, 2 expry : expry za (@>btark fez = max(a, Se debe notar que la expresién condicional es en si una expresién, y se puede cs de tipo # Los parénté presidn condicional, pueso que la precedencia de ?: es de la asignacién. De cualquier modo son recomendables, puesto que hacen mas La expresion condicioral frecuentemente lleva a un c6digo conciso. Por ejem= plo, este ciclo imprime m elementos de un arreglo, 10 por linea, con cada columna separada por un blanco, y con cada linea (incluida la ditima) terminada por una nueva linea, después de ‘imo. Todos los otros clementos son seguidos por cure, pero es mas Lo buen ejemplo es Ejercicio 2-10. Reescriba la fu ‘mimisculas, con una expresién con 2.12 Precedencia y orden de evalu resume las reglas de precedencia y asociatividad de todos los ope- los que ain no se han tratado. Los operadores que caprruto2 ‘58 IPOS, OPERADORESY EXPRESIONES ntador) y & (direct la precedencia de los operadores de bits &, = = y |=. Esto implica que las expresiones de prueba de bi ((x & MASK) == 0) deben ser completamente colocadas entre parértesis para dar los resultados apro- piadas. ‘TABLA 21, PRECEDENCIA Y ASOCIATIVIDAD DE OPERADORES ‘ASOCIATIVIDAD, izquierda a derecha derecha a inquierda iequierda a derecha inquierda a derecha ' inquierda a derecha 8a igquierda a derecha inquierda a derecha 2 derecha a izquierda Ste ce ee [ee eos f= <= pee | derecha a izquierda iquierda a derecha Los +,-.¥ snen mayor precedencia ‘Como muchos lenguajes, C no especifica el orden en el cual srin evaluados. (Las excepciones son &&, en proposiciones como SECCION2.22 PRECEDENCIA Y ORDEN DEEVALUACION 59 Js EQUIVOCADO « puede produ tos compiladores, dependiendo de sin es incrementada antes de que se llame a power. La solucién, por supuesto, cs escribir print cma Mala", Las llamadas a funciones, proposiciones de asignacién anidadas, y los opera dores de ineremento y decremento provocan a fectos colaterales" —alguna varia- ble es modificada como producto de la evaluacién de una expresin. En cualquier involucra efectos colaterales, pueclen exi s dependencias pr puesto que el mejor orden depende grandemente de la arquitectura de la maquina. «printf mostrada anteriormente.) moraleja es que escribir un cédigo que depencda del orden de evaluacién es lenguaje. Naturalmer capituco 3; Control de flujo rden et gue se realiza de las construcciones de control de flujo en ejemalos anteriores; aqui completaremos el conjunto, y sere: ‘mos mas precisos aceree de las discutidas con anterioridad. 3.1. Proposiciones y bloques Una expresin como x = 061+ + o printi(...) se convierte en una proposi- cin cuando va seguida de un punto y coma, como en x=0; printl (5 En C, el punto y coma ¢s un terminador de proposicién, en lugar de un separa- dor, como lo es en un lenguaje tipo Pascal. laves { y } se emalean para agrupar declaraciones y proposiciones dentro de una propasicidn compuesta 0 bloque, de modo que son sintaeticamente equi: valentes a una proposic On laves que encierran las proposiciones de una funcién son un e-emplo obvio; otros ejemplos son | proposiciones milltiples después de un if, else, while o for. variables dentro de cwalquier bloque; esto se expondra en el capi Dunto y coma después ce la Have derecha que termina un bloque lo 4.) No hay 3.2 Itelse La proposicion if-else se sintaxis es para expresar decisions. Formalmente 1 (expresién) roposicién, ole proposiciéns a © CONTROL DE FLUO caprtutoa Puesto que un if simplemente prueba el valor numérico de una expresién, son posibles ciertas abreviaciones de cédigo. Lo mas obvio es escribir if (expresion) cen lugar de if (expresion |= 0) Algunas veces esto es claro y natural; ot Debido a que la parte else de un if-else 2s optai cuando un else se omite de una secuencia if anidada, Esto se resuelve al asociar terior sin else mas cereara. Por ejemplo, en z=b; el else va con el if més interno, como se muestra con el sangrado. Si eso no lo que se desea, se deben utilizar Ilaves para forzar la asociacién correcta: f(a > 0) (a> b) else Je MAL + ror ~~ n es negativo \n"); El sangrado muestra en forma inequivoca lo que se desea, pero el compilador no | mensaje y asocia el else con el if mas interno. Puede ser dificil encom tar esta clase de errores; es una buena idea utilizar Haves cuando hay varios if anidados, secc10ns.3 ELSEIF 63 A propésito, nétese que hay un punto y coma después de z = a en (a> b) else nab; debe a que gramaticalmente alfiile sigue una proposicién, y una expresion, " siempre se termina con punto y coma. La construccién if (expresién) propasicion lee if (expresién) roposicion (expresién) proposicion else proposicion ccurre de modo tan f-ecuente que bien vale una pequefia discusién aparte. Es secuencia de proposiciones if es la forma mas general de escribir una deci le. Las expresicnes se evaliian en orden; si cualquier expresidn es verdade- ra, la proposicién asociada con ella se ejecuta, y esto termina toda la cadena. Co- in es una proposicidn simple 0 un grupo dentro rte del maneja Pe jon cuando ninguna de las otras condiciones se satisface. En algunos ca 0s no hay una accidr explicita para la omisiOn; en ese caso el else proposicisr 2, 0 puede utilizarse para deteccién de errores al atrapar ‘ion de tres vias, se muestra una Tuncidn de busqueda bi- aria que decide si un valor particular de x se encuentra en el arreglo ordenado ¥. Los elementos de vdeben estar en orden ascendente. La funcién regresa la po- sicién (un niimero en:re 0 y nl) si x esté en v, y ~ La busqueda binaria primero compara el valor de Medio del x es menor que el valor del medio, la biisqueda se enfoca bla; de otra manera lo hace nte paso es comparar a x con el 64 CONTROL DEFLUIO caprrutos Ja mitad seleccionada, Este proceso de dividir en dos continiia hasta que se en- cuentra el valor o ya no hay elementos. * binsearch: encuentra xen v[0] <= y[l] <=... <= vin—I]*/ nt binsearch(int x, in int low, high, mids low = 0; high = 2-1; while (low <= man ) La decisi6n fundamental dio v{mid] en cada pa es menor que, mayor que o igual al elemento me- sto es un else natural. Ejercicio 3-1. Nuestra busqueda binaria realiza dos pruebas dentro del cicl podria ser suficiente (al precio de mis pruebas en el exterior). Escriba on solo una prueba dentro del cico y mida l diferencia en tiempo de ejecucién. 3.4 Switch La proposicién switch es una decisién miltiple que prueba si una expresién ide con uno de un nimero de valores constantes enteros, y traslada el con- 1 adecuadamente, switch (expresién) { case exp-const: proposiciones case exp-const: proposiciones default: proposiciones ) Cada Ease] se etiqueta con uno 0 mas valores zonstantes enteros o expresiones id un programa para contar las ocurrencias de cada dos los demas caracteres, usando una secuencia de std el mismo programa con un switch: include main) /+ cuenia digitos, espacios blancos, y otros” { int c, i, nwhile, nother, ndigit 1H white = nother = 0; for (1 = 0; 1 < 10; i+ +) 0; = cotchar( )) != EOF) { sncos ='%d, otros = %6d\n", ! 66 CONTROL DEFLUIO caprmutos sece1ons.s cictosWiteYFOR 67 Pasar a través d parte buene y en parte no. Por el lado positivo, expansidn. Si la prueba expr, no esta presente, se toma como permanentemente verdadera, asi que { for ' y emplear comer break despues del es una iteracién “‘infinita”, que presumiblemente sera interrumpida por otros medios, como un break 0 un return. E] usar while o for es principalmen ejemplo, en ‘Como formalidad, cologue default) aun si es [case] al final, esta practica de programacién defensiva lo salvara. agregue otro cuestién de preferencia personal, Por while (Cc = getchar( )) = 1 fe ignors caracteres espaciadores «/ copia la cadena t a s. Utilice un switch. Escriba también una funcién para la di- reccién inversa, convirtiendo secuencias de escape en caracteres reales. () no hay iniializacion o reincializacion, Elforse prefiere cuando existe una ini que mantiene las propesiciones de control del ciclo juntas y visibles al principio del mismo. Bsto es més obvio en 3.5. Ciclos—while y for epee Sear ay Ya hemos encontrado los os while y for. En que es la forma caracteristica de procesar los primeros n elementos de un arreglo ‘en C, lo andlogo al cicle DO de Fortran o al for de Pascal. Sin embargo, la anal sia no es perfecta puesto que tanto el indice como cl pueden ser alterados deste dentro del ciclo, y la variable del indice iretiene su valor uando las iteraciones terminan por cualquier raz6n. Debido a que las componen- tes del for son expresiones arbitrarias, sus ciclos no estan restringidos a progres nes aritméticas. Por otra parte, considere que es un mal estilo ineluir en las secciones de inicializacién incremento operaciones no relacionadas con esas ac tividades, que mas bier se reservan para acciones de control del ciclo. Como un ejemplo rds amplio, aqui esta otra version de atoi para conver cadena a su equivalente numérico. Esta es ligeramente mas general que la del 2; trata también los espacios en blanco previos al nimero, y los signos + 0-. (Elcapitulo 4 muestra atof, que realiza la misma conversién para niime- 10s de punto flotante.) La estructura del programa refleja la forma de la entrada wile (expresion) roposicion la expresion se evalita, Si es diferente de cero, se ejecuta la proposicion y se reeva- lia la expresidn. Este ciclo continua hasta que la expresién se hace cero, punto en el cual se suspende la ejecucion para cont nuar después de la propasicién. La proposicién for for (expr; exprss expr) proposicién cs cquivalente a expr: hile (expr) ( w expacios en blanco, silos hay toma et signa, si hay excepto por el comportamiento de continue que se describe en la seccién 3.7. ‘oma ta parte entera y conviérela Gramaticalmente, las tres componentes de un ciclo for son expresiones. Por Jo comuin, expr; y expr, son asignaciones o Hlamadas a funciOn y expr, es una eX presidn de relacién. Cualquiera de las tres paites se puede omitir, aunque deben permanecer los punto y coma. Si expr, © expr; se omite, slo se desecha de la Cada paso realiza su parte, y deja las cosas en forma clara para el siguiente. La ‘alidad del proceso termina con el primer cardcter que no pueda ser parte de Un numero. 68 CONTROL DEFLUIO carrTutog include sign for (; = 0; isspace(s[il); i+ +) /+ ignora espacio en blanco « Jo gnora ol signo «/ ye en forma gr cfectivamente un mé int gap, 5, j, emp; for (gap = n/2; gap > 0; gap /= 2 for (1 = gap; i < mj 1+ +) for (j=i-gap; i> =0 84 viil>vli+gapl: I-=9ap) { } Existen tres ciclos anidados. El mas externo controla el espacio entre los elemet tos comparados, reduciéndolo desde n/2 por un factor de dos en cada paso ha sECCIONS.S clcLos_WiLEYFOR 69 que llega a cero. El intermedio recorre los elementos. El ciclo mas interno compara cada pareja de elementos que esta separada por el espacio gap e invierte lo mas externo coincida con Ia forma de los otros, aun cuando no es tuna progresién aritméica, Un tltimo operador de C es la coma **,”, que frecuentemente encuentra uso en la proposicién for. Una pareja de expresiones separadas por una coma se eva- hia de izquierda a derecha, y el tipo y valor del resultado son el tipo y valor del operando derecho. Asi, en una proposicién for es posible colocar expresiones ies en las diferentes partes, por ejemplo, para procesar dos indices en para- te a la cadena s en el mis- mo lugar. include Js reverse: invierte la cadena s en el mismo lugar +/ void reversa(cha: Las comas que separana los argumentos de una funcién, las variables en declara- ciones, etc., 20 son operadores coma, y no garantizan evaluacién de izquierda a derecha, Los operadores coma deberdn utilizarse poco. Los usos mis adecuados son en construcciones fuertemente relacionadas una con la otra, como en el ciclo for de reverse, y en macros en donde un céleulo de paso multiple debe ser una expre- sién simple. Una expresién coma podria también ser apropiada para el intercam- bio de elementos en reverse, donde el intercambio puede ser a través de una operacién simple: hittin) Ejereicio 3-3, Escriba la funcién expand(sl,22) que expande notacién abreviada noa~z, que viene en la cadena sl, en equivalente completa abe...x¥2, en 82, Permita letras maylisculas y Para manejar casos como a-b-e y a~20-9 y ~a~2. Haga que los guiones al ‘nicio 0 al final se tomen literalmente. 0 70 CONTROL DE FLUJO capirutoa 3.6 Ciclos—do-while ‘Como ya se expuso en el capitulo 1, I la condicién de término. En contrast al final después de realizar cada paso a ta siempre por lo menos una vez. La sintaxis del do es y for verifican al principio prueba el cual se ejecu- do roposicién while (expresién); La proposiciOn se ejecuta y después se evalia la expresién. Si es verdadera, la proposicién se cvalia de nuevo, y asi sucesivamente. Cuando la expresién se hace falsa, el ciclo termina. Excepto por el sentido de la prueba, el do-while es equiva- lente a la proposicién repeat-until de Pasc La experiencia demuestra que el do-while es mucho menos I for. Aunque de cuando en cuando es valioso, como fierte un nimero a una cena de caracteres (lo inverso de. rabajo es ligeramente ms complicado de lo que podria pensarse en un. debido a que los métodos fécil itos los generan en if ((sign = 2) < 0) 1 vuelve a n positivo «/ i=0; J+ genera digitos en orden inverso */ 2% 10 +0; /+ toma al siguiente digito «/ while ((2 /= 10) > 0); 1+ borralo «/ El do-whilo es necesario, o al menos conveniente, puesto que por lo menos be instalar un cardcter en el arreglo s, aun sin es cero. También empleamos llaves alrededor de la proposicién simple que hace el cuerpo del do-whi inmecesarias, y asi el lector apresurado no confandira la seccién del while con el principio de un ciclo whil En una representacién de niimeros en complemento a di joa no maneja el niimero negativo mas grande, esto es, el valor de nuestra | secc10N3.7 BREAKYCONTINUE 71 . Escriba una version defitoa) que acepte tres argumen tercer argumerto es un ancho minimo de campo; n agregar blancos a la izquierda fe ancho. 3.7 Break y continue Algunas veces es conveniente tener la posibilidad de abandonar un tra manera que no sea probando al inicio o al final. La proposicién bre porciona una salida y do, tal como lo hace el cas al final de una cadena, utilizando un break p: se encuentra el no-blanco, no-tabulador 0 no-nuevs =~ 1=°\¢ 88 sfn] != x) slat] = \0" return 2; } mada con el break, pero se utiliza menos lo for, while o do que la de whilo y do, esto significa que la parte de la prueba se ejecuta 'nmediatamente; en el for, el control se traslada al paso de incremento. La propo- 72 CONTROL DEFLUIO ‘caprtutoa sicion continue se aplica solamente a ciclos, no a switch. Un continue dentro de lun switch que esta a su vez en un ciclo, provoca la siguiente iteracién del ciclo, Como un ejemplo, el siguiente fragmento procesa s6lo los elementos no_ negativos que estan en el arreglo a; los valores negativos son ignorados. bemiteyd <0) /+ ignora elementos negatives +/ ‘continue; Js trabaja con elementos postves «/ La proposicién continue se emplea a menudo cuando la parte del ciclo que sigue es complicada, de modo que invertir Ia prueba y sangrar otro nivel podria anidar profundamente el programa. 3.8 Goto y etiquetas C proporciona la infinitamente abusable proposicién goto, y etiquetas pi saltar hacia ellas. Formalmente, el goto nunca es necesario, y en la préctica casi siempre mds facil escribir cédigo sin éL. En este libro no se ha usado alguno. ‘Sin embargo, hay dlgunas situaciones donde los goto pueden encontrar un I gar. La més comnin es abandonar el procesamiento en alguna estructura proft damente anidada, tal como salir de dos 0 mas ciclos a la vez. La proposici break no se puede utilizar directamente, puesto que solo sale del ciclo mas no. Asi: for(...) for vu (desastre) carregla ef desorden los Esta organizacién es itil si el eédigo de mansjo de error no es trivial y res pueden ocurrir en varios lugares. Una etiqueta tiene la misma forma que un nombre de variable y es seg por dos puntos. Puede ser adherida a cualquier proposicién de la misma funci de determinar si dos a lidad es yb, tienen un elemento en comin. Una pe SECCIONS coro vEniQueras 73 goto encontrado; J+ no se encontré ningin elemento en comin « ‘encontrado: /*setiene wo: ali] = = El c6digo que involucra un goto siempre puede escribirse si ver al precio de algunas pruebas repetidas 0 variables extra. Por ejemp! queda en los arreglos quedaré ‘encontrado = 1; f (encontrado) 1+ 60 tone uno: ali] = = bli} «/ else J+ no s» encontré algun elemento en comin +! Con pocas excepeiones, como las codigo que se basa en pro- posiciones goto es generalmente mas dificil de entender y de mantener que el e6di- go sin ellas, Aunque no somos dogmaticos acerca del asunto, se ve que las proposiciones goto deben ser utilizadas raramente, si acaso. captuto 4: Funciones y la estructura del programa Las funciones dividen tareas grandes de com nen varias mas pequenas, sobre lo que otros y: en lugar de comenzar deste cero. Las funciones apropiadas ocultan los detalles de on de las partes del programa que no necesitan saber acerca de le s6lo algunas grandes. Un programa puede residir en uno o més archi- vos fuente, los cuales pueden compilarse por separado y cargarse junto con f ciones de biblioteca previamente compiladas. No trataremos aqut tales procesos, puesto que los detalles varian de un sistema a otro. La declaraci6n y definicién de funciones es el area donde el estindar ANSI ha hecho los cambios mas visibles a C. Tal como mencionamos en cl capitulo ahora es posible declarar los tipos de los argumentos cuando se declara una fi cidn, La sintaxis de la definicién de funciones también cambia, de modo que las, dcclaraciones y las definiciones coincidan. Esto hace posi pueda detectar muchos més errores de 1o que podia ant 's argumentos se declaran con propiedad, se real El preprocesador de C tambi Procesador incluyen un con; 4.1 Conceptos bésicos de funciones Para comenzar, disefiemos y escribamos un programa que imprima cada linea de su entrada que contenga un “patron” o cadena de caracteres en particular. 8 16 FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA caprruto 4 I del programa grep de UNIX.) Por ejemplo, al buscar en el conjunto de lineas (Este €s un caso es cl patrén de letras “oul ‘An Love! could you and I with rate conspire To grasp this sorry Scheme of Things entire, Would not we shatter it to bits -- and then| Re-mould it nearer to the Heart’s Desire! producird la salida ‘Ah Love! could you and I with Fate conspire Would not we ehatter it to bits -- and then Re-mould it nearer to the Hear:’s Desire! El trabajo se ajusta ordenadamente en tres partes: while (hay otra tinea) Imprimeta proporcioné. Esto significa que ina rutina para decidir si la linea contiene una ocurren- cia del patrén, Podemos resolver ese problema escribiendo una funcién strindex(s,t), que re- _gresa la posicién o indice en la cadena s en donde comienza la cadena t, 0 ~1 st 5 n0 contiene ¢. Debido a que los arreglos en C principian en la posicion cero, Jos indices serén cero 0 positivos, y asf un valor negativo como 1 es conveniente para sefalar una alla. Cuando posteriormen:e se necesite una coincidencia de pa- lo se debe reemplazar strindex; el resto del cédigo pue- igual. (La biblioteca estndar provee una funcidn strs dex, excepto en que regresaun apuntador en lugar de w jo este defo, nar los detalles del programa es sin |, de modo que se puede ver como las piezas quedan juntas. Por ahora, el patron que se buscara es una cadena lo cual no es ‘mecanismo mas general. Regresaremos en breve a una di sobre eémo icilizar arreglos de caracteres, yen el capitulo 5 mostraremos cémo hacer qué el patron de caracteres'sea un pardmetro fijado cuando se ejecuta el programa. ‘Tambien hay una version ligeramente diferente de getline, que se podr4 compa- rar con la del capi SECCION 4.1 CCONCEPTOS BASICOS DE FUNCIONES 77 # include #define MAXILINE 1000 _/+ longitud maxima por linea de entrada */ int getlino(char lief J, int max); int slrindex(char source(], char searchfor cher patie [] = "ould"; (+ patrén por buscar tra todas las lineas que coincidan con 6] char line(MAXLINE]; int found = 0; while (getine(line, MAXLINE) > 0) =f inea y la pone en ¢, regresa su longitud +/ > 0 && (c=getchar()) != EOF && ¢ !="\n’) ++) 2\0 88 sli] = =t0kl; i+ +, k++) TB FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA carrmute d(k > 06 tk] == 1) Cada definicién de funcién tiene la forma tipo-regresado nombre-de-funcidn(declaraciones de argumentos) { declaraciones y proposiciones ) Varias partes pueden uuna funcién minima es nada( ue no hace ni regresa nada. Una funcién hacer-nada, como ésta, es algunas ces ttil para reservar lugar al desarrollar un programa. Si el tipo que regresa omite, se supone int Un programa es sélo un conjunto de definiciones de variables y funciones. comunicacién entre funciones es por argumentos y valores regresados por las ciones, y a través de variables externas. Las funciones pueden presentarse en ct uier orden dentro del archivo fuente, y el programa fuente se puede dividir varios archivos, mientras las funciones no se dividan. q La proposicién return es ef mecanismo para que la funcidn que se llama r se un valor a su invocador. Al return e puede seguir cualquier expresién: return expresién La expresidn se convertira al tipo de rtorno de la funcién si es necesario. frecuencia se utilizan paréi ara encerrar la expresién, pero son optatives. La funcién que llama tiene la libertad de ignorar el valor regresado. Incluso, hno hay nevesidad de una expresién después de return; en tal caso, ningiin v regresa al invocador. También el control regresa, sin valor, cuando la ejecuci “cae al final” de la funcién al alcanzar la llave derecha que cierra, No es ilegal, aunque probablemente un signo de protlemas, el que una funcidn regrese un va” lor desde un lugar y ninguno desde otro. En cualquier caso, si una funcién no regresa explicitamente un valor, su “valor” es ciertamente basura, El programa de busqueda del patrén regresa un estado desde main, el nimero de coincidencias encontradas. Este valor esta disponible para ser empleado pot el medio ambiente que llamé al prograna El mecanismo de cémo compilar y cergar un programa en C que reside en va~ ios archivos fuente varia de un sistema z otro. En el sistema UNIX, por ejemplo, la orden ce mencionada en el capitulo 1 hace el trabajo. Supdngase que las SECCION 42 FUNCIONES QUE REGRESAN VALORES NO-ENTEROS 79 tres funciones se zlmacenan en tres archivos llamados main.c, getline.c, y strin- dex.c. Entonces la oF co main. qlle.ceuinden.c ita of ehdigo objeo reins ex Inn archivos compila los tres archivos, sitia et cbigo objet 4 getline.o, y strindex.o, y después los carga todos dentro de un archivo evunbielamadd aout Si existe un error, games en main, ex archivo Pe SENatverse a compitr por st mismo ye resultado cargo con ls archivos ob jee preros, on fa orden ‘oc main.¢ geiline.o strindex.o vos fuente de contra “.0" para distinguir los ar ‘ce emplea la convencién. los archivos objec. Ejerciclo 4-1. Escriba la funcion strrindex< rrencia de més ala derecha de t en s, 6 que regresa la posicién dela ocu- no hay alguna. 4.2 Funciones que regresan valores no enteros Hasta ahora los ejemplos de funciones han regresado o ningin valor (void) oun int. Qué pssa si una funcién debe regresar algo de otro tipo? Muchas fun- ciones numéricas como sqrt, sin y cos regresan double; otras funciones especiali- zadas regresan tipos diferentes. Para ilustrar como tratar con esto, escribamos y rte la cadena s a su valor equivalente de punto flotante de doble precisién. La funcién atof es una extensidn de atoi, de la {que mostramos versiones en los capitulos 2 y 3. Maneja signo y punto decimal optativos, y presencia 0 ausencia de p: maria. Nuestra version noes una ratina Ge conversion de alta calidad; tomaria mas expacio del ave pode- mos dedicarle. La biblioteca estdndar incluye un atof; el header la declara. Primero, atofpor si misma debe declarar el tipo del valor que regresa, puesto {que no es int. El nombre del tipo precede al nombre de la funcién: #include double val, power; int 4, sign; for (i = 0; isspace( f+ tgnora espacios blances */ 80 FUNCIONES ¥ LA ESTRUCTURA DEL PROORAMA carmute secci0N 42 FUNCIONES QUE REGRESAN VALORES NO-ENTEROS 81 reqresaria un valor double que main trataria como int, y se producirian resulta- dos incongruentes. a ‘Aa luz de lo que Hemos mencionado acerca de cémo deben coinciir las de- laraciones con las definiciones, esto podria ser sorprendente. La razén de que Storra una falta de coincidencia es que, si no existe el prototipo de una funcién, Ssraes declarada impliitamente la primera vez que aparece en una expresién, co- ‘mo for (power = 1.0; isdigit(oil); i+ +) { val = 10.0 val + ( power += 10.0; sum += atof(line) fa expresidn se encuentra un nombre que no ha sido declarado previamen- izquierdo, se declara por cont } elurn sign * val / powers } Segundo, ¢ igualmente importante, la rutina que llama debe indicar que at regresa un valor que no es int. Una forma de asegurar esto es declarar atof ex ‘tamente en la rutina que la llama, La declaracién se muestra en esta pi culadora (apenas adecuada para un balance de chequera), que lee un mtimero, linea, precedido en forma optativa por un signo, y lo acumula, imprimiendo, suma actual después de cada entrada: #include ‘argumentos como en double atoi(); también es tomada de modo que no se supone nada atof; se desactiva toda -evisidn de pardmetros, de argumentos vacia se hace para permitir que los pr rea de los argumentos de icado especial de la amas en C viejos se com- Vactica usar esto con pro si no los toma, use ofaains MALE |, propianvente declarado, podemos escribir atoi(convierte una cade- J+ calculadora radimentaria */ =n términos de él: main() ( double sum, atof{char [ 1); cchar line|MAXLINE]; int getline(char line| convierte la cadena s a entero usando atof +/ thar s{ 1) double atof(char return (int) atolls); ' Notese la estructura de las declaraciones y la proposicién return. El valor de la expresién en ; rotum expresién La declaracién double sum, atol(char []}: seala que sum es una variable double, y que atof es una funcién que toma y regresa un double. s® convierte al tipo de la funcién antes de que se tome el return. Por lo tanto, elval eneste return, puesto que la funcién ‘ign potencialme Vienen, El east est las advertencias Ejercicio 4-2, Extienda atof para que maneje notacién cientifica de la forma 123.480-6 82 FUNCIONES ¥ LA ESTRUCTURA DEL PROGRAMA carrruto | donde un mimero de punto flotante pued ‘un exponente con signo, 5 ‘guido por e o E y opcionalment 4.3 Variables externas Un programa en C consta de un conjunto de objetos externos, que son v riables 0 funciones, El adjetivo ‘“externo”” se eriplea en contraste con "interno" que describe los argumentos y las variables definidas dentro de las funciones. L¢ variables externas se definen fuera de cualquier funcidn, y por lo tanto, estén tencialmente disponibles para muchas funciones. Las funciones en sf mismas siempre externas, puesto que C no permite definir funciones dentro de otras fi ciones. Por omisién, las variables y funciones externas tienen la propiedad de q todas las referencias a ellas por el mismo nombre, incluso desde funciones com, ladas separadamente, son referencias a la misma cosa. (El estandar llama a propiedad ligado externo.) En este sentido, las variables externas son andl los bloques COMMON de Fortran o a las variables del blogue més externo Pascal. Mas adelante veremos cémo definir variables y funciones externas que Visibles s6lo dentro de un archivo fuente. Debido a que las variables externas son accesibles globalmente, proporci alos valores de retorno para’ ion puede tener acceso a variable te ha si declarado de alguna manera. Si un gran nimero de variables se debe compartir entre funciones, las bles externas son més covenientes y eficientes que las largas listas de argu 105. Sin embargo, como se sefalé en el capitulo 1, este razonamiento se del aplicar con precaucién, pues Las variables externas son también tities debido a su mayor aleance y ti de vida. Las variables automaticas son interna: a una funcién y su existencia inicia cuando se entra a la funcién y desaparecen cuando ésta se abandona. Pt tro lado, las variables externas son permanentes, de modo que retienen sus ¥ conveniente que los datos compartidos se mantengan en variables externas, ef star de que sean pasados como argumentos de entrada y salida, Examinemos mas a fondo este tema con un ¢) ¢s escribir el programa de una calculadora que provea los operadores : /, Por ser mas fécil su implantacién, la calculadora utilizard notacién polaca versa en lugar de infija. (La polaca inversa es uilizada por algunas calculads de bolsillo, y en lenguajes como Forth y Po: se0c10N 43 VARIABLES EXTERNAS 83 En notacién polaca inversa, cada operador sigue a sus operandos; una expre- infija como -294+9) ‘¢ introduce como 12-45 ++ Los paréntesis no son necesarios; la notacién no es ambigua mientras sepamos cvintos operandos espera cada operador. La implantacion ¢s smple. Cada operando sc introduce en una pila o | nlimero correct de operandos (dos para ¢ aplica el operador y el resultado se regresa introducen 1 y 2, después se reemplazan por sroducen 4 y 5 y luego se reemplazan por su suma, 9, que es ~9, los reemplaza en la pila. El valor que se encuentra en el tope dela pila se extrae ¢ imprime cuando se encuentra el fin de la linea de entrada, La estructura del programa es asé un ciclo que realiza las operaciones adecua- das sobre cada operador y operando que aparece: while (siguiente operidor u operando no es fin de archivo) Introducir el resultado eva lea) irae e imorime el tope de la pila Las operaciones de introducir (push) y extraer de una pila Pero cuando se les agrega deteccion y recuperacion de errores, son 'e largas como para que sei mejor ponerlas en funciones separadas en lugar del ‘ include ——/+ par /+ max tamaio de operando u operador */ J+ gonal do que un mimero se encontré */ J+ calouladora polaca inversa */ main( ) { int type; double op2; char s{MAXOP]; while ((type = switch (type) ‘case NUMB push(atol break; push(pop( ) + Pop( break; SECCION 43 VARIABLES EXTERNAS 85 push(pop( ) * pop ))s broak: ep2 = pop( push(pop( ) ~ op2); case ‘a’ srinti("\(%.B9\n", pop ): reak; defauit: srint{(Yerror: comande desconocido %8\n”, 8); break; I Puesto que + y + son operadores conmutativos, el orden en el que se combinan los operandos extraidos es irrelevante, pero para ~ y / deben distinguirse los ope- andos izquierdo y derecho. En push(pop( ) ~ Fop( Js INCORRECTO +/ ‘no se define el orden en el que se evaliian las dos llamadas de pop. Pat 2ar el orden correcto, es necesario extraer el primer valor en una variable tempo- como se hizo en main. ‘#dofine MAXVAL 100 /+ maxima profundidad de la pia val «/ int sp = 0; s siguiente posicién libre en la pila «/ double vallMAXVAL]; _/+ valores de la pila «/ Js push: luce f ala pila «/ void push(double f) ( p < MAXVAL) vallep+ +1 = f; {86 FUNCIONES Y LA ESTRUCTURA DEL PROGRAMA caprtuto 4: ‘error: pile llena, no puede efectuar push %g/n", Js pop: extrae y rograsa el valor superior de la pila «/ double pop(void) { dp > 0) xetumn val[—sp]; else { printf(Yerror: pila vacia\n"); return 0.05 } ‘encuentra definida fuera de cualquier funci la y el indice de la pila que deben ser compartidos por push y por a la pila o a la pos Pasemos ahora a ‘operador u operand. guiente cardcter no es nir una cadena de digitos (que pueda inclur un punto decimal), y regré NUMBER, Ia sefal de que ha sido reunido un mimero. #include J+ gotop: obtiene cl siguiante operador u operando numérico */ int getop(char s{ |) { while ((s10] = ¢ = getch( )) == =" sll] = '\0% ai (iadlanta) Ss = return ©; 10 ¢8 un mimero */ i=; if (adigit(o)) redine la parte entera */ while (isdigit(s|+ +1] = © = gotch( )) (c= =") /+ reine la parte fraccionaria */ while (isdigit(sl+ +3] = ¢ = getch( seCCION 43, VARIABLES EXTERNAS 87 Qué son getch y ungetch? Por lo comin se da el caso de que un programa no puede determinar sia leido suficiente de la entrada hasta que ha leido de- masiado, Un ejemplo es reunir los caracteres que forman un nimero: hasta que se vea el primer no- jimero no est completo. Pero entonces el progra- mma ha leido un cardctet de mas, para el cual no esta preparado. El problema podria ser resuelto si fuera posible ‘desleer” el cardcter no desca- do, Entonces, cada vez que el programa lea un cardcter de més, podria regresar- rada, asi que el resto del eddigo se podra vomportar como si nunca se vas, getch entrega el sigui ada que va a ser considerado; ungetch el car llamadas posteriores a getch lo regresari Cémo trabajan junsas es sencillo, ungetch coloca el carict en un buffer compartido —un arreglo de caracteres. getch lee del butte algo alli y llama a gotchar si el buffer esta vacio. También debe existit una va- riable indice que registre la posicién del cardcter actual en el buffer temporal. Puesto que el buffer j el indice son compartidos por getch y ungs Fetener sus valores entre lamadas, deben ser externos a ambas ‘mos escribir getch, ungetch y sus variables compartidas com: ‘#deline BUFSIZE 100 char bul[BUSIZE}; int butp = 0; /» buffer para ungeteh +/ (+ siguiente posicion libre en buf +/ {int getch(void) /+ obtiene un (posiblemente ya regresado) cardcter +/ { } void ungetch(int c} /+ regresa cardcter a la entrada +/ ( if (bulp > = BUFSIZE) ‘ungotch: demasiados caracteres\n"); return (buip > 0) ? bufl--buip] : getchar (); else buflbufp+ +] = ¢; el capitulo 7. Se "res ala entrada, enlgar de un crdcter 88 FUNCIONES V LA ESTRUCTURA DEL PROGRALA Ejer gue el operador médulo carlo de ella, para duplica ‘Agregue una orden para limpiar la pila. C Ejercicio 4-5. Agregue acceso a funciones de biblioteca como sin, exp Y pe ite en cl apéndice B, seecidn 4. 0 variables. (Es fa 4-6, Agregue Ordenes para mai ‘Afiada una variable para elv nar veintiséis variables con nombres de un Jor impreso mas reciente. 0 que regresa a la entrada una Ejercicio 4-7, Escriba una rutina ung sul buip, o slo debe usar unge completa. :Debe ungets conocer acerca. oO Ejercicio 4-8. Suponga que nunca existicd mas de un cardcter de 1 Modifique getch y ungetch de acuerdo con eso. 0 Ejercicio 49. Nuestros geteh y ungeich no manejan correetamente un EOF. . Decida cuales deben ser sus propiedades si se regresa un EOF, y de diseno. D j Ejercicio 4-10, Una organizacion alternativa emplea gotline para leer una completa de entrada; esto hace innecesarios a getch ya ungetch. Corrija la dora para que use este planteamiento. © 4.4 Reglas de alcance 1s y variables externas que constituyen un programa en C no fexto fuente del programa se puede tener en V inas previanente compiladas dle biblioteca. Er archivos y se pueden cargat las preguntas de interés estén ‘+ .Cémo se escriben las declaraciones de modo que las variables sean declar adecuadamente durante la compilacién? * {Cémo se arreglan las declaraciones de modo que todas las piezas se cont adecuadamente cuando se carga el programa? ‘+ :Cémo se organizan las declaraciones de modo que solo haya una copia! * iCémo se inicializan las variables exteinas? Discutamos estos temas reorganizando el programa de 1a calculadora en archivos. En ia caleuledora es demasiado pequena para SECCION 44 REGLAS DE ALCANCE 89 a scpararla, pero es una excelente El alcance de un nombre es la parte del programa dentro del cual se puede usar el nombre. Para una variable automatica declarada al principio de una fun- n, el alcance es la funcidn dentro de la cual esta declarado el nombre, Las va- msmo nombre que es vido para los p ales, Elalcance de una veriable o funcién externa abarca desde el punto en que se eclara hasta el fin del erchivo que se esta compilando. Por ejemplo, si main, sp, jush, y pop estén definidas en un archivo, en el orden expuesto anteriormen- acion de los conceptos que surgen double pop(void) { ... } entonces las variables sp y val se pueden utilizar en push y pop simplemente nom- brindolas; no se necesita ninguna otra declaracién. Pero estos nombres no son visibles en main, ni push ni pop. Por otto lado, si se va a hacer referencia a una variable externa antes de su definida en un archivo fuente diferente al que se esta utilizan- oria una declaracién exter. Es importante distinguir entre la declaracién de una variable externa y su defi- nicién. Una declaracién expone las propiedades de una variable (principalmente una definicién también provoca que reserve un espacio para almacena- int sp; double val[ MAXVAI aparecen fuera de cualqui Servan un espacio para almacenamiento y tamb elresto de ese archivo fuente. Por otro lad funcién, definen las variables externas sp y val, re- sirven como declaracién para extern in sp; extern dot ‘lelaran para el resto (cuyo tamafo se determina en algun otro lug Miles reservan espacio. Sélo debe existir una definicidn de una variable externa entre todos los archi- 0: que forman un programa fuente; otros archivos pueden contener declaraciones archivo que sp es un int y que val es un arreglo double . pero no crean las variables 90 FUNCIONES ¥ LA ESTRUCTURA DEL PROGRAMA carr 4 extern para tener acceso a ellas. (También puede haber declaraciones extern en elarchivo que contiene la definici6n.) Los tamafios de los arreglos deben ser espe cificados con la definicién, pero es optativo hacerlo en una declaracién extern, La inicializacién de una variable externa 3610 va con su defis ‘Aunque no es una organizacién idénea para este programa, las funciones push y pop pueden definirse en un archivo, y las variables val y sp podrian ser definidas e inicializadas en otro. Entonces se necesitarian las siguientes definicio. nes y declaraciones para enlazarlas: void push(double f) ( .. ) double pop(void) { En el archivo 2: int sp = 0 double vallMAXVAL]; Debido a que las declaraciones extern que se encuentran en el archivo I estan tuadas antes y afuera de las definiciones de funciones, se aplican a todas las ciones; un conjunto de declaraciones basta para todo el archivo 1. Esta mis organizacién también seria nevesaria si las definiciones de sp y val se encont después de su uso en un archivo. 4.5 Archivo de encabezamiento [header 6n del programa de la calculadora en varios ‘cada uno de los componentes fuera sust iré dentro de un archivo, al que Hamare main.c; push, pop y sus variables van dentro de un segundo archivo, stack. getop va en un tercero, getop.c. Finalmente, getch y ungetch van dentro de cuarto archivo, getch.c; las separaremos de les otras debido a que podrian ve de una biblioteca compilada separadamente en un programa Hay algo ms de qué preocuparse —las defi i arlas hasta donde sea posible, de modo copia por mantener mientras se desarrolla el programa. En con tipo header, se describe en la s Consideremos ahora chivos fuente, como podria ser mente mayor. La funcién VARIARLE ESTATICAS 91 awn. slot ‘include fincnde ‘ince "cle" ‘¢doine MAKOP 100 main Las variables sp y ‘nchade Faclade SESS ‘include | fieclde Yealk” ‘dele MAXVAL 100 ‘include “eal suestit int wp = 0; osbie wall MARVAL} : vod push(double ( , “geteh.c: double pops) { ‘Paclade ‘#detne BUFSIE 100 ‘har bul(BUFSIZE| int goth(vid b 1 void wegen | te un compromiso entre el deseo de que cada archivo sélo tenga acceso a para su trabajo y la realidad préctica de que es mas ider. Hasta un tamano moderado de pro- 8 estéticas en stack.c, yballybufplen geteh.c, son para el uso Privado de las funciones que estén en sus respectivos archivos fuente, y se supone 92. FUNCIONES LA ESTRUCTURA DEL PROGRAMA carro aplicada a una variable elalcance de ese objeto al resto del archivo fuente que nombres como buf y bufpen la combinacién getch-ungetch, que deben as para que puedan ser compartidas, aunque no deben ser visibles a lo char bul[BUFSIZE]; int bufp = 0; J» butfer pare ungetch */ ‘+ eigutente posicién libre en but +/ int getoh(void) { ... } ‘entonces ninguna otra rutina sera capaz de terer acceso a buf ni a bufp, y nombres no entrardn en conflicto con los mismos nombres que estén en otros ‘os del mismo programa. De la misma manera, las variables que push y utilizan para la manipulaci6n de la pila se pueden ocultar, declarando sp yw como static. La declaracién static externa se usa con mas frecuencia en variables, es. Normaimente, los nombres de funcios parte del programa completo. Sin embargo, tuna funcién se declara como static, su nombre es invisible fuera del archivo cel que esti declarada. variables automaticas, pero a diferencia de de ir y venir cada vez que se activa la funcidn. Exo significa que las v nas static proporcionan almacenamiento privado y permanente dentro de funcion. Ejercicio 4-1 gerenci Modifique getop de modo que no n ‘emplee una variable static interna. © 4.7. Variables tipo r stro Una declaracién register indica al compilador que l empleard intensamente. Li tros de seCcION 48 ESTRUCTURA DE BLOQUE 93 etcetera. La declaraciGn [egister|sélo se puede aplicar a variables autométicas y fos pardmetros formales de una funciGn. En este tltimo caso, aparece como ‘register unsigned m, register long n) register int i gister se ignora en las dec araciones excesivas 0 no permitidas. Ademas, no es po- una variable de tipo registro (tema que se trataré en el Ia variable esta o no realmente en un registro, Las res- icas sobre el nimero y tipo de estas variables varian de una maquina a otra. 48 Estructura de bloques C_no es un lenguaje estructurado en bloques en el sentido de Pascal o lenguajes semejantes, puesto que las funciones no se pueden definir dentro de otras funcio- nes. Por otra parte, las variables se pueden definir en una modalidad de estructura de bloques dentro de una funcidn. Las declaraciones de variables (incluyendo la iniciaizacion) pueden seguir a la lave izquierda que indica cualquier proposi- cién compuesta, no s6lo la que inicia a una funcién. Las variables declaradas de ta manera ico de variables en bloques externos, derecha que corresponde con ta ini (> 0) J+ doclams una nueva i +/ for (i = 0; < mit +) } lalcance de la variable ies la rama “‘verdadera” det if; estalijno tiene nada que ‘Yer con alguna i fuera de! blogue. Una variable automatica 2ada en un bloque se iniializa cada vez que se entra al bloque. Una variable atic se in lo la primera vez que se entra al bloque. incluyendo los param: 94 FUNCIONES ¥ LA ESTRUCTURA DEL PROGRANA carrruto, int; en la fun las ocurrencias de x se refieren al pardmetro, que es un do fuera de f, se refieren al int externo. Lo mismo es valido para la variable y. Por estilo, es mejor evitar nombres de variables que coinciden con nom de un aleance exterior; la potencialidad de 2onfusién y error es muy grande, nombre con un signo de igual y una expresion: int x = 1; char apéstrofo = ‘\"; Jong dia = 1000L + 60L + 60L + 24L; + milisegundos!dia +/ le biisqueda binaria de la int binsearch(int x, int v 1 int low = 0; int mid; SECCION 4.10 RECURSIVIDAD 95 cen lugar de a igh, mid; low = 0; high = 2-1, fn efecto, las inicializaciones de variables automiticas son s6lo abreviaturas de proposiciones de asignacién. La eleccidn es en gran medida cuestiOn de gusto. Nosotros hemos empleado generalmente asignaciones explicitas, debido a que los jnicializadores en las declaraciones son dificiles de ver y lejanos del lugar de uso. Un arreglo puede ser inicializado al seguir su declaracién con una lista de ini- ddores encerrados entre llaves y separados por comas. Por ejemplo, para ar un arteglo dias con el nimero de dias de cada mes: int dias [] = { 31, 28, 31, 30, 31, 30, 31, 31, 90, 31, 30, 3 Cuando se omite el tamafto de un arreglo, el compiador calculard la longitud contando os iniializadores, los cuales son 12 en este caso. izadores para un arreglo que los del tamafio especifica- do, los otros serdn cero para variables externas oestéticas, pero basura para auto- miticas. Es un error tener demasiados inicializadores. No hay forma de indicar 1a repetici6n de un inicializador, ni de iniciaizar un elemento que esta ala mitad de un arreglo sin proporcionar también todos los valores precedentes. Los arreglos de carasteres son un caso especial de inicializacién; se puede utili- zar una cadena en lugar de la notacién de llaves y comas: char patrén{ ] = “ould”; ‘es més corto pero equivalente a char patrén{ | = { ‘0%, 'x', Y, 'd', \0"}s En este caso, el tamatio del arreglo es cinco (cuatro caracteres més el terminador non. 4.10 Recursividad Las funciones de C pueden emplearse recursivamente; esto es, una funcién Puede lamarse a si misma ya sea directa o indirectamente. Considere la impresion de un mimero como una cadena de caracteres. Como ya se mencioné anterior- Mente, los digitos se generan en orden incorrecto: los de orden inferior Stan disponibles antes de los digitos de orden superior, pero se deben imprimir rden invertido. dos soluciones a este problema. Una es almacenar los digitos en un como se generan, y después imprimirlos en orden inverso, como se hizo 96 FUNCIONES ¥ LA ESTRUCTURA DEL PROGRAMA caprtuto 4 to previo. Asi, en printd(123) el primer printd recibe el argumento n = 123. 12al segundo printd, que a su vez pasa 1 a un tercero. El printd del tercer ni imprime 1, después regresa al segundo nivel. Ese printd imprime 2, después ‘gresa al primer nivel. Ese imprime 3 y termina, se aplica después recursivamente a los dos subzonjuntos. Cuando un subcony tiene menos de dos el 5 no necesita ya ce ningiin ordenamiento; esto deti ne la recursividad. Nuestra versién de quicksort no es la més rapida posible, pero es una de ‘ms simples. Empleamos el elemento intermedio de cada subarreglo para (+ qeort: ordena vilet)..v{right] en orden ascendente +/ void qsori(int v(], int left, int right) { void swap(int v1] t i (left > = night) return; ‘J+ no hace nada si ol arreglo contiene «/ 7 menos de dos elementos */ SECCION 48 EL PREPROCESADOR DEC 97 (left + right)/2); /+ mueve ol elemento de partion */ 1 +f J» particién +/ 1 Frecuentemente existen varias lineas #iaclude al principio de un archi fuente, para incluit proposiciones #define y declaraciones extern comunes, para tener acceso a la declaracién del prototipo de una funcién de bit de headers como < stdio.h >. (Estrictamente habland acceso a los headers denenden de le la mejor manera de enlazar las declaraciones para un progré iza que todos los archivos fueate se supliran con las mismas del grande. Garai niciones y declaraciones de variables, y asi elimina un tipo de error particul ‘mente desagradable. Por supuesto, cuando s¢ cambia un archivo include, se ben recompilar todos los, que dependen de él. 4.11.2 Substitucién de macros Una definicién tiene la forma #detine nombre texto de reemplazo n de una macro del tipo mas sei tuidas por el rex de reé ‘#define tiene la misma forma que u —las sigui yeurret El nombre en BL PREPROCESADOR DEC 99 s5CCION 41 de cada linea que va a cor ya desde su punto de def Por (CAFIRMA") ni en AFIRMATIVO. CCualquier nombre puede definirse con cualquier texto de reemplazo. Por ejemplo, plazo pueda ser diferent= plo, defina una macro lamada max; ‘#detine max (A, B) (A) > (B) ? (A) : (B)) mento real correspondiente. Asi, x= max(p+q, 145); (p+) > ( En tanto que los argumentos se traten co} esta macro serviré para cualquier tipo de datos; no hay necesidad de diferentes tipos de max para diferen- tes tipos de datos, como la habria con las funciones. examina la expansion de max, notard algunos riesgos. Las expresiones se evahian dos veces; esto ¢s malo si involucra efectos colaterales como operadores incrementales © de entrada y salida. Por ejemplo, J» INCORRECTO +/ max(i+ +, j++) incrementara el valor mis grande dos veces. También debe tenerse algin cuida- 0 con los paréntesis, para asegurar que se preserva el orden de evaluacién; consi- dere qué pasa cuando lz macro #define cuadradolz) x*x — /+ INCORRECTO +/ Se invoca como cuadrado (2 + 1). Sin embargo, las macros son valiosas. Un ejemplo préctico viene de , donde getchar y putchar se definen frecuentemente como macros Para evitar el exceso de :iempo de ejecucién de una llamada a funcién por cada ‘ardcter procesado, Las funciones en también se realizan general- Mente como macros. Los nombres se pueden hacer indefinidos con #undef, para asegurar que una Tutina es realmente una funcién, no una macro: 100 FUNCIONES ¥ LA ESTRUCTURA DEL PROGRAMA, #undaf getchar int gatehar (void) { Los parametros formales no se reemplazan dentro de cadenas entre comill Sin embargo, si un nombre de pardmetro esta precedido por un # en el t de reemplazo, la combinacién se expand pardmetro reemplazado por el argumento catenacién de cadenas para hacer, por ejemplo, una macro de impresién para, puracién: #detine dprint(expr) printi(#expr" = %9\n", expr) Cuando se invoca, como en print a macro se expande en printi("ay" “= Yg\n", 2/9); y las cadenas se concatenan, asi el efecto es rinti('x/y = %q\n" Dentro del arsumento real, eada("Jse reemplaza por {"]y cada [\}por que el resultado es una constante de cader #detine paste(lront, back) front ## back asi, paste(nombre, 1) crea el token nombrel. Las reglas para cl uso anidado de ## son misteriosas; en el apéndice A den encontrar mayores detalles. Ejercicio 414, ‘una macro[awap(izi3)] que intercambie dos ar de tipo[fl (La es fa de bloques ayudara,) O 4.11.3 Inclusién condicional Es posible controlar el preprocesamiento mismo con proposiciones nales que se evaliian durante esa etapa. Exto proporciona una forma dé rmente, dependiendo del valor de condiciones evaluadas SECCION 4.11 ELPREPROCESADOR DEC 101 La linea #if evalia una expresién constante entera (que no pued la expresion es diferente de cero, se incluyen las Siguientes lineas has Helis 0 #else. (La proposicién dé (elif es como else if). Laexpresion defined(nombre) en un #if es fe ha definido, y 0 de otra manera. jemplo, para asegurarse de que el contenido de un arcl ‘una vez, el contenido del archivo se del #define HDR J+ el contenido de hdr.h va aqui */ tondit 1a primera inclusin de Rdr-h define el nombre HDR; las siguientes inclusiones encontrarin definido al nombre y pasardn hacia el #endif. Un es puede emplearse para evitar incluir archivos varias veces. Si este en forma vonsistente, entonces cada header puede en si mismo ine ‘ro del que dependa, sin que el usuario tenga que trata con La siguiente secuencia prueba el nombre SYSTEM para decidir cual versién de un header ineluir: ‘i SYSTEM == SYS ‘#dofine HDR ‘sysv.h"” ‘oli SYSTEM == BSD ‘#define HDR ‘bed h” oll SYSTEM = = MSDOS ‘#dafine HDR ‘msdos.h” #olee ‘#define HDR ‘default h” ondit ‘include HDR Laslineas#ifdete #ifadef son formas espcializadas que prucban si un nom- breestédetinido, El primer ejemplo de #if de més arriba pudo haberse escrito ‘edet HDR ‘detine HDR > contenido de hdzh va agus */ ondit caprtuco s: Apuntadores y arreglos lizan mucho en C, en parte debido a que ellos son en ocasiones, ica forma de expresar una operacion, y cn parte debido a que por lo general y los arreglos est -xplora estas rel Los apuntadores se han puesto junto a la proposicién goto como una forma maravillosa de crear programas igibles. Esto es verdadero cuando se zan en forma descuidaila, y es f inesperado. las reglas acerca de como ularse [os apuntadores, obligando a Jo que los buenos programado- an y lo que los buenos compiladores ya imponen, Ademés, el tipo void * (apuntador a void) reemplaza a char * como el tipo apropiado para un apuntador genérico. 5.1 Apuntadores y direcciones Empecemos con un dibujo simplificado de cémo se organiza la memor ‘éQuina tipica tiene un arreglo de celdas de memoria numeradas o direccionadas consecutivat que pueden manipularse individualmente o en grupos conti- {2S Una situacion comin es que cualquier byte puede ser un char, un par de cel o de un byte pueden iratarse como un entero short, y cuatro bytes adyacentes pman un long. Un apuntador es un grupo de celdas (generalmente dos cuatro) sr Pueden mantener ura direccién. Asi, sic es un char y p es un apuntador que Punta a él, podria representarse la situacion de esta manera: 103 carrru 104 APUNTADORES ¥ ARREGLOS El operador unario & da la direccién de un objeto, de modo que la proy ign, cién de c a la variable p, y sedice que p “apunta a” e. El operac @ objetos que estén en memoria: variables y elementos de arreg No puede aplicarse a expresiones, constantes o variables tipo registro, ‘operador unario * ¢s el operador de indireccién o desreferencia; cuando} un apuntador, da acceso al objeto al que seftala el apuntador. Sup que xy ¥ son enleros € ip es un apunador a int. Esta secuencia artifi muestra como declarar un apuntador y cémo emplear & y * int x = 1, y = 2, 210}; /+ ip es un apuntador a int +/ wp = 60) J+ ip ahora apunta a s{C) */ Las declaraciones de x, y y z son lo que hemos visto todo el tiempo. La di cién del apuntador ip, indica que en una expresién *dp y atof(s) tienen valores de tipo double, y 4 argumento de atof es un apuntador a char icacién que tiene el hecho de que un apunt wgido a sefilar a una clase particular de objeto: cada apuntador tipo especifico de datos. (Hay una excepcidn: un ‘‘apuntador a void” se mismo no puede au plea para mantener cualgui desrefer Sip apun donde x pueda inerementa *ip en 10, |APUNTADORES ¥ ARGUMENTOS DE FUNCIONES 108, gpc1ON 52 ios * y & se ligan mas estrechamente que los operadores, Los operadores in asi, la avitnétio yeupel toma aquello a lo que apunte ip, le agrega 1, y asigna el resultado ay, mientras que tal increm uno aguello a que ip apunta, como lo hace ip y Los mentaria ip +y ++ se asocian de derecha a izquierda. F itimo, puesto cue los apuntadores son variables, se pueden emplear sin 1 iq es otro apuntador a int, iq = ip copia el contenido de ip en iq; asi, hace que iq apunte a lo que ip esta apuntando. 5.2 Apuntadores y argumentos de funciones Puesto que C pasa los argumentos de funciones por val directa para que la funcion que se invoca altere una variable de la funcién que la llama. Por ejemplo, una rutina de ordenamiento podria intercambiar dos elemen: ‘0s desordenados con usa funcién llamada swap. No ¢s suficiente escribir swap(a, b); donde la funciém swap esté definida como void swaplint x, int y) /* INCORRECTO «/ temp = x; nay y = temp; 106 APUNTADORES ¥ ARREGLOS Debido a la llamada por valor, swap no puede afectar los argumentos ay b ‘estén en la rutina que la amd. La funcién anterior s6lo intercambia copias y de b. La forma de obtener los resultados que se desean es que el programa iny dor pase apuntadores a los valores que se cembiarén: sap \ Puesto que el operador & produce la direccién de una variable, &a es un dor a a. Dentro de la misma funcién swap, los pardmetros se declaran para apuntadores, y se tiene acceso a los operandos indirectamente a través de void swap(int *px, int “py) /+ inlercambia -px y *py */ { int temp; temp = “px: spx = spy: spy = temp: ) Graficamente: (en el invocador: *OM »OM Los argumentos tipo apuntador per biar objetos 3 cion ge lun flujo de earacteres en valores enteros, un entero por llamada. que regresar el valor encontrado y también una sefal de fin de arc no hay mas que tomar. Esos valores tienen que regresarse por ruta s. para que sin importar qué valor se emplea pera EOF, también pueda ser el de un entero de la entrada, APUNTADORES ¥ ARGUMENTOS DE FUNCIONES 107 sg0C10N 52 Una solucién es hacer que getint regrese el estado de fin de archive como su yalor de Funcion, usando un argumento apuntador para almacenar el entero con- Vertido y tenerlo en la fancida invocadora. Este esquema tambien es utilizado por scanf, como se verhen la seccion 7.4, El siguiente ciclo lena un arreglo con enteros por medio de lamadas a getint: 1, array[SIZE}, getint(int = 0; n < SUE 88 got EOF; n+ +) Cada llamada pone en array(n] el siguiente entero que se encuentra a la entrada c incrementa n. Obsérvese que es esencial pasar Ia direccion de array{n] a getint. De otra manera no hay forma de que getint|comunique el entero convertido hacia Ia funcién invocadora, Esta versin de getin rearesa EOF como fin de archivo, cero sila siguiente en- trada no es un niimero, yun valor positivo sila entrada contiene un niimero valid. #include int getch(void); void ungetch(int); int 6, sign; while (ispacele = getch))) _/+ ignora expacios en blanco */ 8c 1= FOF &&c l= ‘+! && c!= J+ no e¢ un niimero */ © = getch{ )) (e- 0); spn += sign; if(¢ t= EOF) 108 APUNTADORES ¥ ARREGLOS caprtuy A lo largo de getint, “pn se emplea como una variable int ordinaria, También utiliz6 getch y ungetch (descritas en la secci6n 4.3) para que el caracter ext que debe leerse puede regresar a la entrada. Ejercicio 5-1. Como se escribid, getin ‘0 como una representacion valida de cero, Cor acter a Ta entrada. 0 Ejercicio 5-2. Escriba gotfloat, la analogia de punto flotante de getint. Qué ti regresa geffloat como su valor de funcién? © un ~ no seguido por la para que regrese tal 5.3 Apuntadores y arreglos En C existe una fuerte relacién entre apuntadores y arreglos, tan fuerte int al10}; define un arreglo a de tamafio 10, esto es, un bloque de 10 objetos consecuti amados al i 303) La notacién ali} se refiere al i-ésimo elemento del arregio. Si pa es un apunt aun entero, declarado como entonces Ia asignacién pa = fal i hace que pa apunte al elemento cero de a; esto es, pa contiene la dreccion dea a a0) SECCION 53 APUNTADORES Y ARREGLOS 109 “Ahora Ia asignacién bie copiard el contenido de a[0) en x. ‘Si pa apunta a un elemento en particular de un arreglo, entonces por definicién pat 1 apunta al siguiente elemento, pa+i apunta i elementos después de pa, y paci apunta i elementos antes. Asi, si pa apunta a a0], sal) se refiere al contenido de afl], pati es do de ali). = “7 La jirecci6n de ali] y *(pa+i) es el conteni- ion del elemento cero del arreglo. Asi, que después de la asignacion pa = &al0) también como pa = a; as sorprendente, al menos a primera vista, es el hecho de que una referencia la convierte inme- ¥bas arts de sta equivalenca, se deriva que li] y a+ también son dénias: ia direccién del i-¢simo elemento delante de a. Por otra parte, mania las expresiones pueden usarlo con un subindice: pal (pa +3), En resumen, cualquier expresiOn de arreglo ¢ indice es equivalente a una ©xpresion escrita como un apuntador y un desplazamiento. Existe una diferencia entre un nombre de arreglo y un apuntador, que debe te- ‘terse en mente. Un apuntador es una variable, por esto pa=a y pa+ + son lega- ‘es, Pero un nombre de arreglo no es una variable; construcciones como a=pa ¥a++ son ilegales. 110 APuNrADORES ¥ ARREGLOS carrruto: ‘Cuando un nombre de arreglo se pasa a una funcidn, Io que se pasa es la | dad del elemento inicial. Dentro de la funcién que se llama, este argumento es escribir otra versién de strlen, que calcula la longitud de una cadena. reqresa la longitud de la cadena s +/ strlen(char +2) int m; for (n = 0; 5 != Oy s+ +) Puesto que s es un apuntador, es perfectamente legal incrementarlo; s+ + no ti cefecio alguno sobre la caclens de caracteres dela funcién que llamé a strlen, {que simplemente incrementa la copia privada del apuntador de strlen. Eso si fica que Hamadas como mundo"); Puesto que los parémetros formales en usa definicién de funcién, char sf]; ‘char +8; son equivatentes, preferimos el tiltimo, porque indica mas explicitamente que ppardmetro es un apuntador. Cuando un nombre de arreglo se pasa a una funci éta puede interpretar a su conveniencia que sea manejado un arreglo o un tador, y manipularlo en consecuencia. Puede incluso emplear ambas notaco ello 10 hace apropiado y claro. Es posible pasar parte de un arreglo a una funcién, pasando un apuntador inicio del subarreglo. Por ejemplo, si[ales un arreglo, ia+2) SPCCION 54 ARITMETICA DE DIRECCIONES 111 direecién del subarreglo que inicia en a[2]. Dentro retros puede ser ambas pasan a la funcién Ant are) {..) [Asi hasta donde a{ Ie sonciere, el hecho de que el parmetro se refiera a parte de un arreglo mas grande no es de consecuencia. Si se est seguro de que los elementos existen, también es posible indexar hacia airs en un arreslos Sintético, y se refieren a clementos que preceden inmediatamente a puesto, ¢silegal hacer referencia a objetos que no estén dentro de los limites del arreglo. 5.4 Aritmética de direcciones Sips un apuntadora algin elemento de un arreglo, entonces p+ + increment p para apuntar al siguierte elemento, y p+ =i la incrementa para apuntar i clemen- tos adclante de donde actualmente lo hace. Esas y otras construcciones semejantes son las formas més simples de aritmética de apuntadores o de direcciones. El lenguaje C es comsistente y regular en su enfoque a la aritmética de direccio- nes; su integracién de apuntadores, arreglos y aritmética de direcciones es uno de los aspectos que le dan fuerza. Lo ilustraremos al escribir un rudimentario asigna- dor de memoria. Hay dos rutinas: la primera, alloc(n), regresa un apuntador p a 2 posiciones consecutivss, que pueden ser empleadas por el invocador de alloc para almacenar caracter:s. La segunda, afree(p), libera el almacenamiento adqui- tido en esta forma, de modo que pueda ser reutli . Las rutinas elorden 7 se mostrara como se ces hacer que alloc maneje piezas de un gran arre- femos allocbuf. Este arreglo esta reservado para jo con apuntadores, no con in- Ho de caracteres al qu alloc y para afree. Pueso que éstas hacen su tal dices, ninguna otra rutina ne ‘eclarado como static n el archivo fuente que contiene a alloc y a afree, y ash ‘et invisible hacia afuers. En la implantacién préctica, el arreglo puede incluso no un nombre; podria obtenerse llamando a malloe o pdien "tivo un apuntador hacia algun bloque sin nombre de memoria, 112. APUNTADORES ¥ ARREGLOS caPrruL) La otra informacién necesaria es cuanto de allocbuf se ha utilizado. Empl ‘mos un apuntador, llamado allocp, que apuna hacia ite elemento li c 10 que afree(p) simpl hace allocp igual a p si p esta dentro de allocbuf. libre después de lamar a alloc: alloce: y allocbuf —— ae ie #define ALLOCSIZE 10000 /+ tamatio del espacio disponible */ static char allocbuf[ALLOCSIZE}; static char *allocp = allocbuf; [+ alnaconamiento para alloc */ J+ siguiente posiciéa libre +/ int n) + regresa un apuntador a-n caracteres J+ sh cabo +/ (allocbul + ALLOCSIZE ~ allocp > allocp +=; return allocp ~ a; /* antigua p */ }else ——_/+ no hay suficiente espacio */ ‘return 0} } ‘old afree(char +p) /+ almacenamicnto libre apuntado por p */ { if (p >= allocbuf && p < allocbuf + ALLOCSIZE) alloep = P; t En general, un apuntador puede ser inicializado tal como cualquier otra aunque normalmente los Gnicos valores significatives son cero o una presidn que involucre la direccién de un dato previamente definido y de un apropiado. La declaracién static char sallocp = allocbuf; SECCION 54 ARITMETICA DE DIRECCIONES 113 define a allocp como ua apuntador a caracteres y lo inicializa para apuntar al principio de allocbuf, que es Ia siguiente posicién libre cuando el programa co- fnienza. Esto también podria haberse escrito Gallocbufl0); char ‘allocp = puesto que el nombre del arreglo es la direccién del elemento cero-ésimo, La prueba if (allocbul + ALIOCSIZE ~ allocp >= n) { /+ si cabe +/ ‘comprucba si existe sufidente espacio para satsfacer Ia peticidn de m caracteres. Si lo hay, el nuevo valor de allocp seria, cuando mucho, uno adelante del én puede satisfacerse, alloc regresa un apuntador a ese ladeclaracion de la fu alloc debe reeresar de que no queda espacio, ‘que cero nunca ¢s una direccion valida para datos y valor de cero como retorno para sefalar un suceso anormal, en este ca- 50, falta de espacio. Los apuntadores y los enteros no son intercambiables. Cero es la tinica excep- cién; la constante cero puede ser asignada a un apuntador, y éste puede compa- ‘cuencia en lugar de cero. como un mneménico para indicar mas claramente que cs un valor especial para un apuntador. NULL esta definido en . De agui en adelante se utilizaré NULL. Pruebas como if (alocbuf + ALLOCSIZE ~ allocp >= n) { /* si cabe */ Mp >= allocbul && p < allocbuf + ALLOCSIZE) ‘muestran varias facetas importantes de la aritmética de apuntadores. Primero, los apuntadores puetien compararse bajo ciertas circunstancias. Si p y q apuntan, 8 miembros del mismo arreglo, entonces relaciones como ==, !=, <, >=, tc., funcionan correctamente. Por ejemplo, lo, ya se ha observado que un apul © Festarse, La construccén pen 114 APUNTADORES ¥ ARKEGLOS carrruto La resta de apuntadores también es vili¢a: si p y q apuntan a elementos mismo arreglo, y p-\n"}; Cuando una cadena de caracteres como ésta aparece en un progra sella cs a través de un apuntador a caracteres; printf recibe un apuntay del arreglo de caracteres Esto es, se tiene acceso a una cadena constante po apuntador a su primer elemento. [Las cadenas constantes no necesitan ser argumentos de funciones, Si pmessage se declara cono char “pmessage; ‘entonees la proposicién message = "ya or el tempo”; ‘signa a pmessage un apuntador al arreglo de caracteres. Esta no es la copia de luna cadena; s6lo concierse a apuntadores. El lenguaje C no proporciona ningin operador para procesar como unidad una cadena de caracteres Existe una importante diferencia entre estas definiciones: cchar amessage[ ] = "ya es el tiempo"; / arreglo «/ char spmessage = "ya os el tiempo"; /* apuntador +/ Amessage es un arreglo, suficientemente grande como para contener Ia secuencia feewrctiesy AAO a 2a Se pueden mouificarearactresindividua. '6 dno del aregl, peo amessage sempre se refer ala isn localad de ‘cenamiento. Por oto lado, pmessage es un apuntador, inicializado para ‘Dania a una eadena constante el apuntador puede modifcase posterionmente due apunte a alin otro lado, pero el resultado es indefinido si trata de mo- ificar et Poecsage: [—}—e[ ahora ov elena ] anessage: [ahora es 0 IG APUNTADORES ¥ ARREGLOS ann APUNTADORES A CARACTERES Y FUNCIONES 117 lustraremos més aspects de los apuntadores los ateglos, estudiando jae ciclo. El efecto rederque lon caracteresss.co siones de dos utiles funciones adaptadas de funcién es strepy(e), que copia la cadena t a simplemente 8, pero esto copia el apun:ador, no los caracteres. Para cearacteres Se requiere de un ciclo. Primero esti la version con un arreglo. versién de eubindicas +/ “Samo restimen final, observe que una comparacién contra \O' es redundante, ‘que la pregunta es simplemente si la expresién es cero. Asi, la funcién po- ia escribirse correctamente como J+ stropy: copia t hacia old ainipyfchar va, abi + stropy: copia t hacia s; versién 3 con apuntadoros */ void strepy(char +5, char “t) { = while (*s+ + = t+ +) , la conveniencia de esta Puesto que se encontrar (0 puede parecer misterioso a primers iderable, y debe dominarse el en programas de C. feca esténdar () strepy, devuelve la cadena objetivo vin, ue examinaremos es stromp(s,{), que compara las cadenas regresa un valor negativo, cero 0 positivo sis es Iexicogratfi- igual a, o mayor que t. El valor se obtiene al restar los carac- teres de la primera posicion en que s y t no coinciden, (+ stromp: regresa 0 a1 s>t +/ int tromp(char +, char +!) Puesto que los argumentos se pasan por valor, stropy puede toss y t en la forma que le parezca mejor. Aqui hay apuntadores com mente iniiaizados, que se desplazan a lo largo del areglo un carécter a la \O' con que termina t se ha copiado a s. En la préctica, strepy no se escribiria como se mostré anteriormente. Los fan return sli] — ' J+ stropy: copia t hacia s; void strcpy(char +s, char { sién 2 con apuntadores +/ a versién con apuntadores de stremp: while (est = st4 +) I= 0) } Esto traslada el incremento de s y de t hada dentro de la parte de prucba d clo. Bl valor de *t-+ + es el cardcter al que apunta t ante 118 APUNTADORES Y ARREGLOS Puesto que ++ y — son operadores prefijos o post ', Se presentan ot combinaciones de +, ++ y ——, aunque con menos frec cia. Por ejemplo, — disminuye p antes de traer el cardcter al que apunta. En efecto, la pareja de J+ meta val en la pila +/ J» saca el tope de ia pila y lo pone en val «/ son expresiones idiomiticas esténdar para meter y sacar algo de una pi Ia seccién 4.3, El header contiene declaraviones para las funciones que se en esta seccién, ademas de una variedad de otras funciones para mi de cadenas en Ia biblioteca estandar, Ejercicio 5-3. Escriba una versién con apuntadores de la funcién streat que ‘muestra en el capitulo 2: streat(s,) copia la cadena t al final de s. C rend(s,t, que regresa 1 si la cadena t se pr inal de la cadena, y cero si no « asi. © Ejercicio 5-5. Escriba versiones de las funciones de biblioteca stmomp, que operan con hasta los n primeros caracteres de sus argument ) copia hasta n caracteres de t hacia 8, Ei jones mas completas. © 56, Reescriba los pro} . empleando apuntador lo 3 se presento una funcin de ordenamiento Shell que podia’ lo de enteros, yen lo 4 se mejor6 con un quicksort 10 funcionard, excepto que akora se debe tratar con Iineas de tudes, y que, a diferencia de los enteros, no se pueden ibiar en una simple operacién. Se necesita una representacién de Aqui es donde entran los arreglos de apantadores. Si las lineas que se ¥ ordenar se almacenan juntas en un gran arreglo de earacteres, entonces s@ tener acceso a cada linea por medio de un apuntador a su primer cardcter- sBOCION 58 AKKEGLOS DE APUNTADORES; APUNTADORES A APUNTADORES 119 lado, los apuntadores se pueden almacenar en un arreglo, Dos lineas se pueden asando sus apuntadores a stremp. Cuando dos lineas desordenadas rcambian los apuntadores en el arreglo de apun- EE jina el doble problema de un manejo complicado de almacenamiento y roduciria al mover ne tFes pasos: proceso de ordenzmiento lee todas las lineas de entrada ‘ordénalas Jimprimelas en ordea apuntadores hi irada, puesto que esa informacién se req i. Debido a que la funcidn de entrada s6l cas, puede regresar alguna cuenta de ne que imprimir las lineas en el orden en que apare- arreglo de apuntadores. #include include ‘#define MAXLINES 5000 J+ max # de lineas por ordenar */ char ‘lineptr[MAXLINES]; _/* apuntadores a neas de texto */ ft ceadlines(char «linepte| old writelines(cha: “linept Void qeort(char “lineptl |, int let, int right); ‘+ ordona lineae de entrada +/ main) ( 120 APUNTADORES ¥ ARREGLOS worn ARREGLOS DE APUNTADORES; APUNTADORES A APUNTADORES 121 secc1ON 56 int lines; J+ mimero de ineas de entrada leidas «/ que indica que lineptr es un atteglo de MAXLINES elementos, cada uno de los ss un apuntador a char. Esto es, lineptr[i] es un api cs el cardcter al que apunta, el primer cardcter de readlinex(tinoptr, MAXLINES)) > = 0) { -D; wnitelines(lizeptr, tam 0; ombre de como un apuntador en la misma forma que en nuestros ejemplos anteriores (rrtelines puede escribirse en su lugar como or: entrada demasiado grande para ordenara\n") 1 waltelines: ei void wrtelinoe(c i while (alines— > 0) ‘deline MAXLEN 1000 + méx longitud de cua printl(%s\n", sineptr+ +); int geiline(char +, ini}; } Inicialmente *lineptr apunta a la primera linea; cada incremento lo avanza al si- (+ readlines: lee lineas de entrada +/ {int readlines(char slinepts{ |, int mazlines) ( ‘int Ion, lines; ‘char *p, line[MAXLEN]; jones deben modificarse, y la operacién de comparacién debe hacerse ‘a stromp. El algoritmo permanece igual, lo que nos da cierta confianza de que ain trabajard. len—l] = \0'; /+ elimina earécter nueva linea +/ strepy(, lin + no hace nada si el arreglo contiene */ ‘menos de doe elementos =/ ft + sight)/2); sswap(y, left, as) cgeor(y, left, last); gone, last +1, right) for () = 0; < mlines; i+ rinti("%e\n", linep ' La funcién getline se traté en la seccién 1.9, EI principal nuevo elemento es la declaracién para lineptr: cchar slineptr[MAXLINES] ina de i Poco sig + swap: inlercambia vii] y vil +/ void swap(char +], { 122 APUNTADORES ¥ ARREGLOS carrtut spCcION 527 AARREGLOS MULTIDIMENSIONALES 123 leap = your¥s4 == 0 Gite yoas%6100 != 0 year%6400 = = 0; for (1 = 1;4 < month; 14 +) day += daytab[leap]til; return day; ano +/ 5 y dia a partir de dia Puesto que cual void month daylint year, int yearday, int *pmonth, int «pday cardcter, temp lemento individual de v (alias lineptr) es un apuntador debe serlo, de medo que uno pueda copiarse al otro, Ejercicio §-7. Reescriba readlines para almacenar lineas en un arreglo pr cionado por main, en lugar de llamar a alloc para obtener espacio de almac leap = yoar%d == 0 &6 year%100 != 0 ! year%400 = = 0; miento. {Cudnto més rapido es el programa? 0 ee a for (1 = 1; yoarday > yearday —= dayta spmonth = 3 spday = yearday; 5.7 Arreglos multidimensionale: EI lenguaje C proporciona arreglos muitidimensionales rectangulares, cen la préctica se usan menos que los arreglos de apuntadores. En esta s ‘mostraremos algunas de sus propiedades. Considérese el problema de la conversion de fechas, de dia del mes a dia ao y viceversa. Por ejemplo, el 1 de mazo es el 60° dia de un ano que no bisiesto, y el 61° dia de uno que silo es. Definamos dos funciones para hacer month_day, para que conversion: day_of_year convierte mes y dia en el dia del ano, convierte el dia del afio en mes y dia, Puesto que esta valores, 1os argumentos de mes y dia deben ser apuntadores: month day(1988, 60, &m, &d) hhace m igual a 2 yd igual a 2 ‘Ambas funciones dias de cada mes ( por mes difiere para ¥ Bo bsiestos, es mas facil separarlos en renglones de un arreglo ional quessiga la pista de lo q durante los célculos. El arreglo y las funciones que real son como se muestra a continuacién: static char daytab(2] (13] = { (0, 31, 28, 31, 30, 31, 20, 31, 31. 30, 31, 30, (0, 31, 29, 31, 30, 31, 30, 31, 31. 30, 31, 20, 31}, daytab es el primerarreglo de caracteres de dos dimensiones con el que hemos tratado. En C, un arrezlo de dos dimensiones es en realidad un arreglo unidimen- sional, cada uno de cuyos elementos es un arreglo, Por ello, los subindices se es- ciben como daytab| ‘+ [ronglén} {columaa] +/ cn lugar de ‘+ day-ol_year: obtiene dia del ato a partir de mes y atlo «/ int day-ot_year(int year, int month, iat day) 4 pasa a una funci el numero de columnas declaracién de pa- inti, leap; imiimero de renglo- APUNTADORES VS. ARREGLOS MULTIDIMENSIONALES 125 124 APUNTADORES ¥ ARREGLOS caprTuto seCCION 58 hi totum (n <1 {n> 12)? namo(0] ; name[n]; , La declaracién de name, que es un ar rma que [a de lneptr en el ejemplo del de cadenas de caracteres; cada una Los caractetes dk nes es itrelevante, puesto que lo que se pasaes, como antes, un apuntador @ arreglo de renglones, donde cada rengln es un arreglo de 13 ints. Es este particular, es un apuntador a objetos que son arreglos de 13 ints. Entonces, el arreplo daytab se pasara a la funcién f, a declaracién de f seria Sint daytab ‘También podria ser © podria ser | niimero corr que indica que el pardmetro es un apuntador a un arreglo de 13 enteros. Los rémesis son necesarios, puesto que los corchetes [tienen mas alta preced 5.9 Apuntadores vs. arreglos multidimensionales 1s nuevos usuarios de C algunas veces se confunden con la le dos dimersiones y uno de apuntadores, como name en el ejemplo rncia entre int +daylab (13) es un arreglo de 13 apuntadores a entero. De modo mas general, dimensién (subindice) de un arreglo queda abierta; todas las ot En la seccién 5.12 se discute mas acerca de declaraciones complicadas, i "4 entonces tanto afi ] son referencias sintdcticamente legitimas a co int, Per snte un arreglo de dos dimensiones: se le han asignado 200 localidades de tamafio de un int, y se emplea el célculo convencional de subindices rectangulares 20% renglén-+columna para encontrar elemento alrengl6n,columna]. Para b, sin embargo, la definicién s6lo asigna 10 apuntado- res y no los icializacién debe realizarse en forma expli stéticamente con cédigo. Suponiendo que cada elemento de b ap arreglo de veinte elemestos, entonces existiran 200 ints reservados, mas diez cel- das para los apuntadores. La ventaja importante del arreglo de apuntadores es pueden ser de longitudes diferentes. Esto es, m0 ¢s rcicio 5-8. No © Solucione ese defect fe deteccién de errores en day-of_year ni en mont o izacién de arreglos de apuntadores rese el problema de escribir una funcién month_name(n), que lor a una cadena de caracteres que contengan el nombre del n: ‘a es una aplicacién ideal para un arreglo static interno, mont contiene un arreglo reservado de cadenas de caracteres, y regresa un apunt a la cadena apropiada cuando se llama, Este seccién muestra como se inici ese arreglo de nombres, La fo a ningun. (05, el uso mas fre- s de anuntadores es para almacenar cadenas de cari como en la funcién month_name. Compare la declaracion J+ month name: regresa el nombre dei n-ésimo met +! ‘char *month name(int 2) 126 APUNTADORES Y ARREGLOS con la de un arreglo bidimensional: b’, cchar aname| ] [15] = { "Mes ilegal", "Ene", “Feb”, “Mar” } 8 0 8 0 Ejercicio 5-9. Reescriba las rutinas day_ol_year y month_day empleando aj tadores en lugar de indices. 5.10 Argumentos en Ia linea de érdenes Dentro de un medio ambiente que maneje C hay una forma de pasar mentos en la linea de ordenes o de parimetros a un programa cuando empi ejecucion. Cuando se llama a main se le invaca con dos argumentos. El pri (llamado por conveneién arge, por argument count) sel niumero de argumei en la linea de ordenes con los que se invocé el programa; el segundo (argv, por gument vector) es un apuntador a un arreglo de cadenas de caracteres que et ne los argumentos, uno por cadena. Se acostumbra utilizar niveles mult \dores para manipular esas cadenas de caracteres. sjemplo mas set rograma echo, que desplicga sus argu inea de Ordenes en una linea, separsdos por blancos. Esto es, la of ‘echo hola, mundo imprime hola, mundo Por convencidn, argv(0] es el nombre con ef que se invocd el programa, que argc es por lo menos 1. Si argc es 1, entonce no hay argumentos en la después del nombre del programa. En el ejemplo anterior, arge es 3, y a1 “hola” y “mando”, respectivamente. El prit ‘gumento optativo es argv} y el altimo es argv[arge—1]; ademas, el quiere que argvlarge] sea un apuntador nulo. argy: -Lechon ] seC10N 5.10 ARGUMENTOS EN LA LINEA DE COMANDOS 127 La primera version de echo trata a argv como un arreglo de apuntadores a ca #include 1+ eco de los argumentos de la linea de érdenes; 1a. versién +/ ‘main(int argc, char ¢argv! J) ( } Como argv es un apunador a un arreglo de apuntadores, se pueden manipular ‘al apuntador en lugar ce indexar al arreglo. Esta siguiente variacién se basa en IF argv, que ¢s un apuntador a un apuntador a char, en tanto se dis fe los argumentos de la linea de érdenes; 2a. versién +) age, cher "ar uesto que argy es un apuntadar al inicio del arreglo de cadenas de argumentos, incrementarlo en 1 (+ +argu) lo hace apuntar hacia argv(1] en lugar de apuntar 4 argv(0]. Cada incremento sucesivo lo mueve al siguiente argumento; entonces “argv es el apuntador aese argumento. Al mismo tiempo, arge disminuye; cuan- 4o lesa a cero, no quedan argumentos por imprimir. En forma alternativa, podemos escribir la proposicién printf como print{(arge > 1)? "Ya": "9%", ++ + arev); Eso demuestra que el argumento de formato del printf también puede ser una presion Como un segundo ejemplo, hagamos algunas me; 28"4.1 que encuentra un patron. Si se recuerda, potundo del progra Farts la guia del BY €l patron que se debe encontrar se especifique por el primer areumento en la Mea de drdenes. yrograma de la sec nde busqueda en 128 APUNTADORES ¥ ARREGLOS carn #include include ‘#define MAXLINE 1000 {int gotlino(char line, int max); J+ find: smprime lineas que coinciden con el patrén del Jer. argumento main(int argc, char *arav{ }) c cchar line[MARLINE]; int found = 0; i (arge I= 2) printi("Uso: find patrén\n"); alse while (getline(line, MAXLINE) > 0) Mf (steteline, aray{l] != NULL) { printf("%s", line); found+ +; return found } La funcién strstr(s,t) de la biblioteca esténda: regresa un apuntador a la pri cocurrencia de la cadena t dentro de la cadena s, 0 NULL si no exis sda linea impresa con su nimero de linea ‘Una convencién comiin para programas en C en sistemas UNIX es que un (*nimero") para solicitar la numeracién de lineas, entonces la orden find —x -n patrén cada linea que no coincida con el patrén, precedida por su numero’ Los argumentos para opciones deben ser permitidos en cualquier orden resto del programa debe ser independiente de! nimero de argumentos que ran presentes. Ademés, es conveniente para los usuarios que los argument las opciones puedan combinarse, como en find ~ax patron Aqui esta el programa: sgcc108 5.10 ARGUMENTOS EN LA LINEA DE COMANDOS 129 #include include ‘#doline MAXLINE 1000 (char ‘line, int max); Js find: imprime lineas que coinciden con el patrén del Ler. argumento +/ ‘main(int arge, char argv! J) 4 char line[MAXLINE); Jong lineno = 0; int ©, except = 0, number = 0, found = 0; while (—arge > 0 &é (++ +argv)[0] = = 9 while (¢ = ++ +aravi0)) umber = 1; break; defeat printi(“ind: opcién ilegal %c\n", ¢); arge = 0; found = ~2; Dreak; ‘while (gelline(line, MAXLINE) > 0) { linexo+ +; if ((tratx(line, vargv) |= NULL) != oxcopt) { f (oumber) return found; ) 1a S965 di ¥y argy se incrementa antes de cada argumento opcional. inal del ci 10 hay errores, argc dice cudntos argumentos permanecen, 130 APUNTADORES ¥ ARREGLOS caPrTULo sin procesar y argy apunta al primero de éstos. Asi, arge debe ser Ly +argy det + +argv)[O} cs su primer cara En efecto, esto es lo que empleamos en el ciclo mAs interno, donde la tarea proceder a lo largo de una cadena especifice de argumentos. En el ciclo mas expresion *+ +argv{0] incrementa el apuntador argvi0] Es raro que se empleen expresiones con apuntadores més complicadas que tas; en tal caso, seré més intutivo separarlas en dos 0 tres pasos. Ejercicio 5-10. Escriba el programa expr, que evalia una expresién polaca im st a de ordenes, donde cada operador w operando es un argumento p separado. Por ejemplo, opr 234 + se evahia como 2 x G+4). 0 rcicio 5-11. Modifique el programa entab y detab (escritos como ejercicios de puntos de tabulacién como argu: los tabuladores habituales si no hay argumentos. © Ejercicio $-12, Extienda entab y detab de modo que acepten la abreviatura ‘entab-m + que indica puntos d in cada m columnas, i en la columna m. leccione el comportamiento por omisién més conveniente (para el usuario). Ejercicio 5-13. Escriba el programa tail, qu¢ imprime las ultimas n lineas entrada. Por omisién, 1s 10, digamos, pero puede madificarse con un argu {0 optativo, de modo que tal-n imprime las tiltimas 1 lineas. El programa debe comportarse en forma raci sin importar cuan poco razonable sea la entrada o el valor de n. Escriba el pr ma de manera que haga el mejor uso de la memoria disponible; las almacenarse como en el programa de ordenamiento de la seccién 5.6, no arreglo de dos dimensiones de tamaiio fijo. 5.11 Apuntadores a funciones En C, una funcién por si sola no es una variable, pero es posible definit tadores a funciones, que pueden asignarse, ser solocados en arreglos, pasados SECCION 5.11 modo que sise da el argum: APUNTADORES A FUNCIONES 131 snéricamente en lugar de lexicograti La comparacién lexicogrifica de do ‘es; también requeriremos de una ruti as y regrese la jones se declaran antes 1e los objetosesten en orden. las operaciones de comparaci ineas es realizada por stremp, como an- jumemp que compare el valor numérico .cidn que hace stremp. Bstas fun- de los errores en los argu: jos elementos principales. clude ‘#deline MAXLINES 5000 cchar +lineptr/MAXLINES); int readlines(char “linept void wrtelines(char slineptr| void qsort(void slineptr| J Js max # de liness 2 ordenar +/ 1+ apuntadores a lineas de texto */ int (comp) (void +, Int numemp(char *, char *); /» ordena linear de entrada +/ main(int argc, char ‘argv{ 1) { 1+ mximero de lineas de entrada leidas +/ J+ 1.81 08 ordenamionto numérico +/ int alines; int numeric = 0; (arge > 1 & stremplargy(l], "-n") = = 0) MAXLINES)) >= 0) { el (numeric ? numemp : stromp)); 132 APUNTADORES ¥ ARREGLOS ‘carro rada demasiado grande para ser ordenada\n"); En la llamada a qsort, stromp y numemp son direcciones de funciones. Ci se sabe que son funciones, el operador & no es necesario, en la misma forma tno es necesario antes del nombre de un arreglo. Hemos escrito qsort de modo que pueda procesar cualquier tipo de dato, slo cadenas de caracteres. Como se indice por la funcién prototipo, qsort es un arreglo de apuntadores, dos enteros y una funcién con dos argumentos de ti apuntador. Para los argumentos apuntadores se emplea el tipo de apuntador rico void +. Cualquier apuntador puede ser forzado a ser void * y resresa de nuevo sin pérdida de informacion, de modo que podemos llamar a qsort fe zando los argumentos a voids. Fl elaborado cast del argumento de la Fanci fuerza los argumentos de la funcion de comparacion. Esto generalmente tendra efecto sobre la representacion real, pero asegura al compilador que t if (left >= right) /+no hace nada si el arreglo contiene */ Las declaraciones deben estudiarse con cuidado, Eleuarto pardimetro de qsort int (comp) (void +, void +) Que indica que comp es un apuntador a una funcién qu; void * y regresa un El uso de comp SeCCION 5.1 APUNTADORES A FUNCIONES 133 te con la declaracion: comp es un apuntador a una funcién, *comp es is son necesarios para que los componentes sean correctamente asociados; sin ellos, int scomp(void +, void +) f+ INCORRECTO «/ indica que comp es una funcién que regresa un apuntador a int, lo cual es muy diferente. ‘Ya hemos mostrade stremp, que c ‘que compara dos cadenas numericamente, valor que include (> numemp: compara sl y 62 numéricamente */ int umemp(char *s1, char +52) { La funcién swap, que intercambia dos apuntadores, es idéntica a la que pre- Sentamos anteriormente en este capitulo, excepto en que las declaraciones se han cambiado a void +. ‘oid swap(void 4 Sint 5, int) } Puede agregarse una variedad dé lgunas se convierten en ejercicios opciones al programa de ordenamien- teresantes. Ejercicio 5-14. Modificue el programa de ordenamiento de modo que maneje na bandera x, que iadica ordenar en orden inverso (descendente). Asegurese que —x, trabaja con =n. 3 134 APUNTADORES ¥ ARREGLOS carrrul las letras maysculas y miniis las durante el ordenamiento; Ejercicio 5-15. Agregue la opcién ~f para ign Jas, de modo que no se haga distincién ent ejemplo, al comparar, ay A son iguales. © jercicio 5-16. Agregue la opcién ~d (“orden de directorio”), que compara s was, nuimeros y blancos. Aseguirese de que trabaja en conjuncién conf. 0 jercicio 5-17. Agregue capacidad de manejo de campos, para que el order (0 se haga sobre campos de las lineas, caca campo ordenado de acuerdo con 10 independiente de opciones. (EI indice de este libro fue ordenado, las entradas y —n para los mimeres de pagina.) O 5.12 Declaraciones complicadas Al lenguaje C se le reprucba algunas veces por la sit icularmente las que involucran apuntadores a ful de haver que coincidan las declaraciones con » pero puede ser confusa para los dificiles, del sno pueden leerse de izquierda a derecha, y debido al exceso de uso de sis. La diferencia entre ‘+ f funcién que regresa un apuntador a int «/ int (ep ilustra el problema: « es un operador prefijoy tiene menor precedencia que ( ‘modo que los paréntesis son necesarios para obligar a una asociacién apropi ‘Aunque en la préctica es extrafio que aparezcan declaraciones verdad ¢ sadas, es importante saber como entenderlas y, si es nevesario, . Una buena forma de sintetizar decaraciones es en pequefios pasos , que se discute en la seccién 6.7. Como una alten presentaremos un par de programas que cenvierten de C valido a una dé cin verbal y viceversa, La descripcién verbal se lee de izquierda a derecha. La primera, del, es la més compleja. Cenvierte una declaracién de C en descripcién hecha con palabras, como en egos ejemplos: ‘(+ pf: apuntador a una funcién que regresa un int «/ chen sarge apuntador 4 unapuntador # char fuacién que regress apurtador a void srcciON 3.12 DECLARACIONES COMPLICADAS 135 comp: apuniador a una funcién que regresa void har (x 010. x: funcién cue regresa un apuniador a un arzeglol | de ‘apuntadores auna funciéa que regresa char char (+(x) x: arreglo[ de apuntadores a wna funcion que regresa ‘up apuntador a un arregio[8] de char del esti basada en la gramatica que especi en forma precisa en el apéndice A, seccién 8.5; ésta es una forma simplificada: considere este declarador: dt \ ] et | det-ir | detdir act ca un declarador, at puede emplearse para reconocer declaraciones, Por ejemplo, define 136 APUNTADORES ¥ ARREGLOS ca El coraz6n del programa del es un par de funciones, del y dirdcl, que bben una declaracién de acuerdo con esta gramética. Debido a que la grami llaman recursivamente una a sel programa se conoce eonocen piezas de una ictico por descenso rei J+ del: reconoce una declaracién void del{veid) int ns; for (ns = 0; gettoken( ) /* euenia +6 */ dirdel( while (as strcaifout, “apuntador a); } J+ dirdel: reconoce un declarador directo */ ‘void dirdel(void) { int type; iM (lokentype == "(){ —fs (ech) +f while ((ype=gettoken( )) == PARENS | type = = BRACKETS) ‘modo que las declaraciones invalidas tambi dejan come ejetccios, ‘cupera mucho ante los lo confunden, Esas secC10N 5.12 DECLARACIONES COMPLICADAS 137 ‘Aqui estén las variables globales y la rutina principal: waclude #include #include ‘#deline MAXTOKEN 100 ‘enum { NAME, PARENS, BRACKETS }; {nt gettoken(void) int tokentypo; char token|MAXTOKEN]; cchar name[MAXTOKEN]; cchar datatype[MAXTOKEN]; nombre del identiicador + tipe de dato = char, int, while ((c = geteh()) == He == 40 DECLARACIONES COMPLICADAS 139 138 APUNTADORES v ARREGLOS caprruto gf seccion $12 zotum tokentype = PARENS; } else { ungeteh(c); return tokentype = ‘( } else (6 == ‘19 for (p++ = ci (p++ = geteh( )) “p= "0; ‘P+ + = c;lsalnum(e = getch( ));) jo 5-20. Extiende del para que maneje declaraciones con tipos de argu- de funciones, ealificadores como const, etcétera, C se discutieron en el capitulo 4. en la direccién inversa, especialmente si no nos preocu Por la generacién de paréntesis redundantes. El programa undel convierte Aescripcién verbal como "x es una funcién que regresa un apuntador a un de apuntadores a funciones que regresan char”, que se expresaré como {La sintaxis abreviada de la entrada nos permite reutilizar ala funcién gettoken. Funcién undel también emplea las mismas variables externas que dol. /* undel: convierle una descripcién vetbal a declaracién «/ main( ) { int type; char temp(MAXTOKEN}; =n) ype = = BRACKETS) carituLoé: Estructuras Una estructura es una coleccién de una 0 mas variables, de tipos posiblemente diferentes, agrupadas bajo un solo nombre para manejo convenient fructuras se conocen como “records” en algunos otros lenguajes, p Pascal.) Las estructuras ayudan a organizar datos complicados, en particular dentro de programas grandes, debido a que permiten que a un grupo de relacionadas se les trate como una unidad en lugar de como entidades separadas. ‘Unejemplo tradicional de estructura es el registro de una némina: un emplea- do estd descrito por un conjunto de atributos, como nombre, domicilio, numero del seguro social, salario, etc. Algunos de estos atributos pueden, a su vez, ser estructuras: un nombre tiene varios componentes, como los tiene un domicilio y aiin un salario. Otro ejemplo, mas tipico para C, procede de las graticas: un pun- to es un par de coo-denadas, un rectangulo es un par de puntos, y otros casos semejantes. EI principal cambio realizado por el estiindar ANSI es la definicién de la asig- nacign de estructuras —las estructuras se pueden copiar y asignar, pasar a funcio- nes y ser regresadas por funciones. Esto ha sido manejado por muchos compiladores durante varios afios, pero las propiedades estin ahora definidas en forma precisa. Las estructuras y lot arreglas automaticos ahora también se pueden inicializar. 6.1 Conceptos basicos sobre estructuras Definamos alguras estructuras propias para graficacién. El objeto basico es ‘un punto, del cual supondremos que tiene una coordenada x y una coordenada 3 ambas enteras, 142 esrRUCTURAS on Los dos componentes pueden ser colocados en una estructura declarada asi: struct point ( int x; int ¥: k La palabra reservada struct presenta la declaracion de una estructura, que ces una lista de declaraciones entre llaves. Ln nombre optativo, llamado rétulo estructura, puede seguir a la palabra struct (como aqui lo hace poi da nombre a esta clase de ble ordinaria (esto es, no miemt pueden tener el mismo nombre sin conflicto, puesto que siempre se pueden dist ir por el contexto. Ademas, en diferentesestructuras pueden encontrarse los ‘mos nombres de miembros, aunque por ciestiones de estilo se deberian de los mismos nombres s6lo para objetos esirechamente relacionados. Una declaracién struct define un tipo. La llave derecha que termina Ia ls de miembros puede ser seguida por una lista de variables, como se hace para c quier tipo basico. Esto es, struct ve es sintdcticamente andlogo a int x,y, cn el sentido de que cada props Una declaracién de serva espacio de almacenamiento sino que si ola forma de una mbargo. lo se puede emplear post ra. Por ejemplo, dada la declaracién arterior de point, struct point pt; define una variable pt que es una estructura de tipo struct point. Una estruct se puede inicializar al seguir su definicién con una tuno una expresin constante, para los miembros: struct point maxpt = { 320, 200 }; Una estructura automatica también se puede inicializar por asignacién o Ia do a una funcién que regresa una estructura del tipo adecuado. Se hace referencia a un miembro de una estructura en particular en una €¥ presién con una construccién de la forme nombre-estructura.miembro SPCCION 62 [ESTRUCTURAS Y FUNCIONES 143 El operador miembro de estructura “.” conecta al nombre de la estructura con el nombre del miembro. Por ejemplo, para imprimir las coordenadas del pun- 0 rinti('%d,%d", pt.x, pt para calcular la cistancia del origen double dist, syt(double); apt, dist = sqet((double)pt.x + ptx + (double y+ pty) Las ras pueden anidarse. Una representacion de un rectangulo es co- ‘mo un par de puntos que denotan las esquinas diagonalmente opuestas: » or Pal struct rect { struct point ptl; struct point pt2; h La estructura 1 Si declaramos screen como contiene dos estructuras poi struct rect scteen; entonces sereen.ptlx se refiere a la coordenada x del miembro prl de screen. 6.2 Estructuras y funciones ura son copiarla 0 asignarla in con &, y tener acceso a sus miembros. La copia la asignacién incltyen pasarlas como argumentos a funciones y también regre- valores de funciones. Las estructuras no se pueden comparar. Una estructura se puede ini 144 estRucTURAS carrito Investiguemos las estructuras escribiendo algunas funciones para maniput puntos y rectingulos. Hay por lo menos tres acercamientos posibles: pasar radamente los componentes, pasar una estructura completa o pasar un apuni a ella, Cada uno tiene sus puntos buenos ¥ malos. La primera funcion, makepoint, toma dos enteros y regresa una estructung point: J+ makepoint: crea un punto con las componentes x, ¥ */ struct point makepoint(int x, it y) f struct point temp; tomp.x = x tempy = yi return temp; ' Nétese que no hay conflicto entre el nombr: del argumento y el mi ‘mismo nombre; incluso la reutilizacién de los nombres refuerza el ‘makepoint ahora se puede usar para inicializar dindmicamente cualquier tructura, o para proporcionar los argumentos de la estructura a una funci struct point makepoin! screen.ptl = makepoint(0, ptl.y + soreen.p2.9)/2), EI siguiente paso es un conjunto de funciones para hacer operaciones arit ticas sobre los puntos. Por ejemplo, Js addpoint: uma dos puntos */ nto 10s argumentos como el valor de retorno son estructuras. Incr famos los componentes en pl en lugar de utilizar explicitamente una variable t poral para hacer énfasis en que los pardmetros de la estructura son pasados valor como cualesquiera otros. sECCION 62 [ESTRUCTURAS Y FUNCIONES 145 ‘Como otro ejemplo, la funcién ptinrect prueba si un punto esti dentro de un sectdngulo, donde hemos adoptado la convencién de que un rectangulo incluye fs lados igquierdo ¢ inferior pero no sus lados superior y derecho: ptinroct: regreta 1 si p esté en r, 0 sino lo ests «/ plinrect(atruct point p, struct rect 2) return px >= rptlx 6& px < rpldx 88 py >= epi y GG py < rpi2y; } Eso supone que el ecténgulo estérepresentado en una forma esténdar en donde Jas coordenadas ptl son menores que las coordenadas pi2. La siguiente Funcion regresa un recténgulo, garantizando que esté en forma candnica: / canontect: pone en Jorma candnica las coordenadas de un rectingulo */ struct rect cancareci(struct rect) { struct rect ‘emp; lemp.pi2.y = max(rptl.y, rp return temp; , Si una estructura grande va a ser pasada a una funcion, generalmente es mas a estruct son como los apuntadores a variables ordinarias. La declaracion struct point *ppi dice que pp es un apuntador a una estructura de tipo struct point. Si pp apunta 4 una estructura point, “pp es la estructura, y (+pp).x y (*pp)-y son los miem- bros, Para emplear pp, se podria escribir, por ejemplo, struct point origin, «pp: Los paréntesis son necesarios en (*pp).x debido a que la precedencia del opera- dor miembro de estructura . es mayor que la de +. La expresién «pp.x significa “(ep.x), lo cual es ilegal debido a que x no es un apuntador. 146 estRuctuRAS caprruto Los apuntadores a estructuras se usan cor frecuencia que se ha prc cionado una notacién alternativa como aoreviacidn. Si p es un apuntador a tructura, entonces p> miembro de estructura se refiere al miembro en particular. (El operador -> es un signo menos segui inmediatamente por >.) De esta manera podriamos haber escrito printi("el origon es (%éd, %d)\n", pp->x, pp->y); Tanto . como —> se asocian de izquietda a derecha, de modo que si tent struct rect 1, ¢xp = 7: centonces estas cuatro expresiones son equivalentes: Los operadores de estructuras . y ->, junto con ( ) para llamadas a funci para subindices, estan hasta arriba de la jerarquia de precedencias y se Los paréntesis se pueden emplear para alterar ‘+p)->len incrementa a p innecesario.) ier cosa a la que str apunl tecomo *8+ +); (*p- “p+ +->str incrementa a p después de hacer el acceso a lo que str apunta. 6.3. Arreglos de estructuras (Considérese escribir un programa para contar las ocurrencias de cada pal reservada de C. Se requiere de un arreglo de cadenas de caracteres para manté seCCION 63 ARREGLOS DE ESTRUCTURAS 147 tos nombres, y un arreglo de enteros para las cuentas. Una posibilidad es usar 's arreglos paralelos, keyword y keyconunt, como en ero el hecho de quelos arreglos sean paralelos sugiere una organizacién diferen- te, un arreglo de estructuras. Cada entrada de una palabra es una pareja: char *word; int count; y hay un arreglo de parejas. La declaracién de estructura struct key { cchar *word; int coun! ) keytab[NKEYS| declara un tipo de estructura key, define un arreglo keytab de estructuras de ese tipo, y reserva espacio de almacenamiento para ellas. Cada elemento del arre- alo es una estructura. Esto también se podria escribir como struct key { char *word; int count ki struct key keyiabINKEYS]; cidn es seguida por ana lista de inicializadores entre llaves: struct key { char word; int count; ) keytabl } = | 148 EsTRUCTURAS SECCION 631 ARREGLOS DE ESTRUCTURAS 149 rotor 0; bbinsearch: encuentra wna palabra Binsearch(char *word, struct ke 14s preciso encerrar los ii © estructura entre llaves, como en int cond; int low, hich, mid; pero simples 0 cadenas de caracteres, y cuando todos das en el arreglo keytab se calculard 1 se deja vacto. ‘else if (cond > 0) low = mid + 1; else setura mid ' return ~1; ) #include #inclade ‘#include Mostraremos la funcién getword en un momento; por ahora es suficiente decir que cada llamada a getword encuentra una palabra, que se copia dentro del arre- alo referido como su primer argumento, La cantidad NKEYS es el nimero de palabras en keytab, Aunque las 10 mas Facil y seguro que lo haga la ma- fa a cambios. Una pos izedores con un apuntador nulo y luego re0 de keytab hasia que se encuentra el fin. ‘#dcline MAXWORD 100 int getword(char ‘mero de entradas es size of keytab / sze of struct key C proporciona un ope! S€ puede emplear para c siaeof objeto sizeof (nombre de tipo) 150° esrRUCTURAS cart SECCION 6 APUNTADORES 0 ESTRUCTURAS 181 dan un entero igual al tamaiio en bytes de mente, sizeof produce un valor entero sit signo cuyo ne] header .) Un objeto puede ser una vari ipo basico como int 0 de © un apuntador. : aito del arreglo dividido liza en una proposicién #¢ gotword fecoleccion de un cardcter adelamt La llamada a ungetch regresa el cardcter a la entrada para la getword también usa isspace cl tamafo de un elemento. Este cal para fijar el valor de NKEYS: #deline. NKEYS (sizeof keytab / of(struct key) ersion de getword tra forma de escribir esto es dividir el tamafio del arreglo entre el tama jemtarios 0 fi un elemento especifico: ‘#deline NKEYS (cizoo! keylab / sizeof keytab[0]) ventaja de que no neces izeof no se puede utilizar en una linea #if, debido a que el prepr no analiza nombres di ‘0 la expresion del #define no es evaluada el preprocesador, y aqui el cédigo ‘Ahora la funcién getword. Hemos esc neral de obtiene ‘palabra’? de dena de letras y digitos que principia con una espacio en blanco. El valor de la funcién es el primer caracter de la palat EOF para fin de archivo, 0 el cardcter e1 si mismo cuando no es alfabét J+ gotword: obtiene la siguiente palabra o cardctor de la entrada */ int getword(char *word, { 6.4 Apuntadores a estructuras ar algunas de las consideraciones involucradas con apuntadores y le nuevo el programa de conteo de palabras Ladeclaraci 1a de keytab no requiere de cambios, pero main y binsearch si necesitan modificasiones. include #include ‘include +#dofine MAXWORD 100 int gotword(char «, int); struct key ebinsearch(char «, struct key int c, gatoh(roid); void uagetch( char w = word; J cuenta palakcas reservadas de C; versién con apuntadores «/ main( ) { char wordMAXWORD]; struct key *p; while (isspace(c = getch( ))) while (getword(word, MAXWORD) != EOF) caipha( word il (p=binsearch(word, keytab, NKEYS)) != NULL) p>eount + +; for (p = keytab; p < keytab + NKEYS; p+ +) if (p—>count > 0) 4d s/n", p->count, p~>word); j lim > 0; w+ +) isalnum(+w = goteh( )) { ‘ungetch(+w); break; return 0; ‘encuentra una palabra en tab| key *binsearch(char *word, struct key +tab, struct key shigh = étl requiere de cambios significaiivos en binsearch. izadores para low y high son ahora apuntadores al inicio y j El cdlculo del elemento itermedio ya no puede ser simplemente mid = (low+high) /2 Puesto que la suma de dos apuntadores es iegal. Sin embargo, la resta es ma, por lo que high-low es el ntimero de elementos, y asi hhace que mid apunte al elemento que est a la mitad entre low y high. El cambio mas importante es ajustar el algoritmo para estar seguros de al o intenta hacer acceso a un elemento fuera del slo. El problema es que &tab[—I] y &tab[n’ estan ambas fuera de los limites arreglo tab. La primera es estrictamente ilegal, y icin det lenguaje garantiza que la aritmética de apunt hho genera un apuntador i zal desreferenciar la seat seCCION 63 [ESTRUCTURAS AUTORREFERENCIADAS 183 es que involucra el primer elemento después del fin de un arreslo (esto es, Gtabla} trabajara correctamente, ‘En main escribimos for (p = keytab; > < keylab + NKEYS; p+ +) i p ¢5 un apuntador < una estructura, la aritmética con p roma en cuenta el para obtener el siguiente ele {1 ciclo en el momento zorrecto. embargo, no hay que suponer que el tamafo de una estructura es la suma de los tamafios de sus miembros. Debido a requisitos de alineacion pai tes objet0s, podria haber “huecos” no identificados dentro de una estructura, ‘Asi por ejemplo, si un char es de un byte y un int de cuatro bytes, la estructura struct { char ¢; int % bien podria requerir ocho bytes, no cinco. El operador sizeof regresa el valor apropiado. Finalmente, un comentario acerca del formato del programa: cuando una funcién regresa un tipo complicado como un apuntador a estructura, como en struct key *binsearch(char +word, struct key stab, int 2) i de leer y de encontrar con un editor de lexto. Por eso, algunas veces se emplea un estilo alternativo: struct key * binsearch(char *word, struct key stab, int n) Esto es algo de gusto personal; seleccione a forma que prefiera y manténgala. 6.5 Estructuras autorreferenciadas ‘Supéngase que deseamos manejar el problema mas general de contar las ocu- or anticipado, 20 podemos orden: biisqueda binaria. No pedemos hacer una busqueda lineal para cada "eee, para ver siya se ha visto, puesto que el programa tomaria demasiad bo, cjecucidn tiende a recer en pr mere de palabras de entrada.) {Como podemos organizar los ‘tos para trata eficien-emente una lista de palabras arbitrarias? Una solucidn es martener siempre ordenado el conjunto de palabras que ya ‘han visto, colocando cada una en su posicién correcta cuando Mega. Esto, 154 esrRUCTURAS de cualquier manera, no se podria realizar recorriendo las palabras en un lineal —también tomado demasiado estructura de datos llamada drbo! binario, El arbol contiene tun apuntador tena cuenta tun apuntador al nodo hijo de la izquiorda tun apuntador al nodo his Ningiin nodo puede tener més de dos hi Los nodos se mantienen de tal manera que en cualquier nodo el subérbol quierdo contiene sélo palabras que son bra que esté en el nodo, y el subarbol de la derecha slo contiene palabras son mayores, Este es el drbol para la oracién bres buenos vengan al auxi palabra tal como fue encontrada. labra nueva. Este proceso es recursiv para insercion ¢ impresion seran lo mas natural. Regresando a la des ‘mo una estructura con cuatro componentes: spCCION 6S [ESTRUCTURAS AUTORREFERENCIADAS 155 sta declaracién recursiva de un nodo podria parecer riesgosa, pero es correcta. fs ilegal que una estructura contenga una instancia de si misma, pero carrrun mpo. En lugar de ello utilizaremos ruct tnode “left {eclara a left como un apuntador @ tnode, no como un inode en si. ‘Ocasionalmente, se requiere de una variacién de estructuras autorreferenciadas: dos estructuras que hagan referencia una a la otra. La forma de manejar esto es: ‘nodo” por cada palabra distinta; cada nodo conti lo de la palabra ‘mimero de ocurrencias | struct t{ dorecha lp pubae tener: cero una struct sp; /+ p apunta a una 8 */ icograficamente menores que la iat struct {+q; + qapunta una t +/ 's tiempo de que todos los de su partido”, como se construyé al insertar es sorprendentemente pequeti yetword, que ya hemos descrit include include include ‘#define MAXWORD 100, struct tnode raddiree(struct tnode *, char *); void treeprini(strict tnode *); int gotword(char , int); J+ conteo de frecuencia de palabras +/ main( ) i a nueva palabra es menor que la palabra del arb. iequierda o, de otra manera, en el nodo hij cen la direceién requerida, la palabra nueva no struct tnode *r00t; char word[NAXWORD]; isqueda desde uno de sus hijos. Por ello, unas rutinas recursi root = NULL; jtwerd(word, MAXWORD) != EOF) (isalpha(word| ion de un nodo, se representa convenientemente rot = ad sick als lm of Ee: ete (sepa hain eto »/ Paar iat count (+ imero de ocurrencas +! : tact node iol ingulerde #1 acne sete La funcidn adatroo es recursiva, main presenta una palabra al nivel superior {el ral (a rai). En ead etapa la palabra se compara con la que ya est alma 156 ESTRUCTURAS carr cenada en el nodo, y se filtra bajando hacia el subérbol izquierdo 0 derecho una llamada recursiva a addtree. Finalmente la palabra que ya esta en se incremet apuntador nulo, indicando que se debe crear un nodo y agr ‘crea un nuevo nodo, addtree regresa un apuntacor a él, ylo struct tnode stalloo( voi char *strdup(char un modo con w, eno bajo p */ uct tnode +p, ckar *w) Js addtree: age struct inode raddi int cond i (p == NULL /+ llegé una nueva palabra +/ prSword pr>eount = 1; pr>lelt = p—> (cond = stremp(w, p~> ws J> palabra repetida +/ } ‘menor que el contenido del subsrbol izquierdo po>right = addtree(p—> ni return p: lun nodo del érbol, y la nueva palabra se copia un lugar ocul blaremos de esas rutinas en un momento.) Le cuenta se iniciali se hacen nulos. Esta parte del cédigo se ejecuta s6lo para las hojas del art bol izquierdo (todas las palabras menores que és posteriormente el subarbol derecho (todas las palabras mayores). Si se si inseguro sobre a forma en que trabaja la recursion, simule la operacion ol mostrado anteriormente. Iimpresién del arbol pen orden */ (struct tnode +p SECCION 6.5 [ESTRUCTURAS AUTORREFERENCIADAS 157 cl arbol se *'desbalancea’’ debido a que las palabras no Hlegan en orden aleato: -mpo de ejecucién puede aumentar demasiado. En el peor de los casos, si las palabras ya estén en orden, este programa realiza una ccostosa simulacién de bisqueda lineal. Existen generalizaciones del érbol binario ‘que no padecen de este comportamiento del peor caso, pero no las describiremos aqui. Antes de dejar este zjemplo, también es deseable una breve exposicién sobre un problema relacionaco con los asignadores de memoria. Es claramente desea- ble que s6lo exista un asignador de almacenamiento en un programa, aun cuando srentes clases de objetos. Pero si un asignador va a procesar peticiones igamos, apuntadores a char y apuntadores a struct tnodes, surgen dos pre- imera, gc6mo cumple los requisitos de la mayor parte de las maquinas reales, de que los objetos de ciertos tipos deben satisfacer restricciones de alinea- isn (por ejemplo, genecalmente los enteros deben ser situados en localidades pa- res)? Segunda, cuales ceclaraciones pueden tratar con el hecho de quie un asigna dor de memoria necesariamente debe regresar diferentes clases de apuntadores? Los requisitos de alixeacién por lo general se pueden satisfacer facilmente, al costo de algiin espacio desperdiciado, asegurando que el asignador siempre regre- se un apuntador que ctmpla con todas las restricciones de alineacién. El alloc el capitulo 5 no garantiza ninguna alineacién en particular, de modo que emplea- remos la funcion malloc de la biblioteca estandar, que st lo hace. En el capitulo 8 se mostrara una forma de realizar malloc. La pregunta acerca del tipo de declaracién para una funcién como malloc es para cualquier Ienguaje que tome con seriedad la revision de tipos. En , el método apropiadoes declarar que malloc regresa un apu bués forzar explicitamente con un cast al apuntador para hacerlo del seado, malloc y las rutinas relativas estan declaradas en et header . Asi, talloc se puede escribir como Una nota practi ude ces un tnode */ node “tall selumn (struct tnode +) malloc(sizeoi(struct tnode)): } Strdup simplemente copia la cadena dada por su argumento a un lugar segu- 9, obtenido por una llamada a mallee:

Das könnte Ihnen auch gefallen