¡Te damos la bienvenida a Scribd!
Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Documentos de Pasatiempos y Manualidades
Documentos de Crecimiento personal
Lenguaje C
\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 0IG 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 ante118 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 en128 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. 3134 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, define136 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 == 40DECLARACIONES 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 ini144 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 alma156 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:158 rstRucTURAS captTuto, char *strdup(char *) _/+ crea un duplcado de s «/ { char *p: p= (char +) malloo(stelon(s) +); /* +1 para \O! +/ M(p != NULL) Py(P, 8); return p ) ‘malloc regresa NULL si manejo de jay espacio dispenible; strdup pasa ese valor, de- invocador, a malloc puede los 7 y 8. yerarse para str den alfabético cada grupo de nombres de variable que sean idénticas en sus pri ‘meros 6 caracteres, pero diferentes en el resto. No cuente palabras dentro de denas ni comentarios. Haga que 6 sea un parimetro que pueda fijarse desde tinea de ordenes. 0 6-3. Bscriba un programa de referencas eruzadas que imprima una list \s palabras de un documento, y para cada palabra, un: inea en los que aparece. Elimine palastas como “ Ejercicio 6-4, Escriba un programa que imprina las distintas palabras de su en trada, ordenadas en forma descendente de acuerdo con su frecuencia de ocur cia. Precede a cada palabra por su conteo. C 6.6 Busqueda en tablas sta seceion escribiremos los componentes de un paquete de basqueda doting IN 1 bre IN y el texto de reer cuando el nombre IN apar state = IN; se debe reemplazar por 1 sn dos rut registra el nombre s y el los nombres y textos de reempl sxto de reemplazo t en una tabla; s y t s SECCION 66 BUSQUEDA EN TABLAS 159 solo cadenas de caractetes. lookup(s) busca s en la tabla y regresa un apuntador fl lugar en donde fue encontrado, o NULL si no esti, El algoritmo es una bisqueda hash —el nombre que llega se convierte a pequeio entero no negaivo, que después se usa para indexar un arreglo de apunt ores. Un clemento delarreglo apunta al prineipio de u gue describen nombres que tienen ese valor deh j2in nombre ha obtenico ese valor. jgada de bloques es NULL si nin. 5 SL nombre Se Lam sombre 2 ES dea > entrada de la tabla: +/ J+ siguiente entrada en Ta cadena +/ J+ nombre definido */ J+ texto de reemplazo */ cchar *defn; El arreglo de apuntadores es s6lo “#doline HASHSIE 101 static struct nlis shashtab[HASHSIZE]; /+ tabla de apuntadores+/ La funcion de hash, que se utiliza tanto en lookup como en inst de cardcter de la cadena a una combinacion mezclada de los a resa el médulo de. residuo entre el tamafo del arreglo, Esta no es la mejor ion de hash posible, pero es pequeila y efectiva. ‘+ hash: forma un valor hash para la cadena s */ unsigned hash(char +=) { ‘unsigned hachval; for (hashval = 0; +5 != ‘\O'; s+ +) hhashval = ++ + 31 + hashval; rotum hachral % HASHSIZE; , La aritmetica sin signe asegura que el valor de hash no es neeativo,160 estaucruras El proceso de haslt produce un indice inicial en el areglo hasht se encontrara en algun lugar, serd en la lista de bloques que empi «queda se realiza por lookup. Si lookup encuentra que la entrada ya regresa un apuntador a ella; de otra manera, regresa NULL. J+ lookup: busca s en hashtab +/ struct alist “lookp(char +2) { struct alist emp; for (np = hashtab[hash(s)|; np != NULL; np = np->next) 3 (sremp(s, np->name) = 0) ; Ir e0 enconixs +) + no 48 encontz6 */ El ciclo for que est en lookup es la expresién idiomitica esténdar para movers sobre una lista ligada: for (ptr = head; ptr != NULL; pir = ptr~>next de ser asi, la nueva defi struct nlist lookup(char char ‘strdup(char * ‘+ install: coloca (name, dein) struct nlistcinstall(char *name, char *¢efn) { if (ap = lookup(as fue encontrado */ ap = (struct (ap = = NULL sp(name)) = = NULL) hashval = hash( id +) ap~>detn); /+ M((ap~>defn = strdup(defn) = = retura NULL; retum ap; ‘SECCION 67 TyPEDEr 161 Ejercicio 6-8. Escriba una funcién undef que borre un nombre y una defini- cidn de la tabla mantenida por lookup ¢ install. 0 ss, sin jercicio 6-6. Haga ina version simple del procesador #define ( inas de argumentos) adecuadz para usarse con programas en C, basada en la esta seecidn, También podré encontrar dtiles getch y ‘ungetch. © 6.7 Typedef proporciona una facilidad llamada typedet para crear nuevos tipos de da- typedef int Longitud; nombre Longitud un sinénimo de int. El tipo Longitud puede emplearse aciones, casts, ctc., exactamente de la misma manera en que lo podria Longitud on, mazlen; Longitud “lenzthsl De modo semejante, la declaracién typedef char *Cadena; hace a Cadena un sindnimo para char * o apuntador a cardcter, que después puc- de-usarse en declaraciones y casts alloctint); nombre de variable, 10 justo después di ‘typedef es como las clases de almacenami do nombres con mayiscula para los typé Como un ejemplo més complicado, podriamos declarar Rodos del drbol mostrados anteriormente en este capitulo: typedef struct tnede Treeptr; ic. Hemos emplea- typedef struct tnode{ /+ el nodo del Arbol: +/ char *word; J+ apunta hacia el texto */ int couat + niimero de ocurrencias +) Troo; J hijo izquierdo +/ Treeptr right; J" hijo derecho */ ) Treenode;162 ESTRUCTURAS captTuta, Esto crea dos nuevas palabras reservadas para tipos, llamados Treenode (una. ‘tructura) y Treeptr (un apuntador a la estructura). Entonces, la rutina tal podria ser Troeptr talloc(void) { retum (Treepte) malloc(sizeof(Treenode)); ‘ningtin sentido; simplemente agrega un nuevo nombre para algiin tipo ya exist ie. Tampoco es alguna nueva sem jables declaradas de esta ms jtamente, En efecto, type ser interpretado por el compilador puece realizar subst estiin mas alla de las capacidades del preprocesador. Por ejemplo, typedef int (*AAF)(char *, char *); crea el tipo AAF, de “apuntador a funciin (de dos argumentos char *) que sgresa int”, el cual se puede usar en contextos como AAF strcmp, numemp; rias cantidades enteras, y entonces hacer un conjunto apropiado le short, int y long para cada méquina. Tipos como size_t y ptrdiftt de la bibl esténdar son ejemplos. El segundo proposito de los typedef es proporcionar mejor document cién para un programa —un tipo lamadoTreeptr puede ser més facil de que uno declarado s6lo como un apuntador a una estructura complicada. 6.8 Uniones ‘Una unién es una variable que puede contener (en momentos diferentes) 105 de diferentes tipos y tamafios, y el conppilador hace el segui y requisitos de alineacion. Las uniones proporcionan una forma de manipular Ferentes clases de datos dentro de una sola area de almacenamiento, el program; informacion depenciente de la maquina. Son an: variant records de Pascal. SECCION 68 UMIONES 163 Como un ejemplo, que podria ser encontrado en el manejador de la tabla de simbolos de un compilador, supéngase que una constante podria ser un| 0 un apuntador a cardcter. El valor de una co guardado en una variable del tipo ad No ob manejador de tablas si el valor ocupa la misma ca do en el mismo lugar sin importar su tipo. Este es el propé: una sola variable que puede l La variable u sera suficientemente grande como para mantener al mayor de los plant macenado actualmente en una unidn; si algo se almacena como un tipo y se recu- pera como otfo, el resultado depende de la implantacién, Sintacticamente, se tiene acceso a los miembros de una unin con nombre-unidn. miembro ‘apuntador-uniin—> miembro precisamente como a las estructuras. Si la variable utype se emplea para llevar el registro del tipo actualmente almacenado en u, entonces se podria ver el cédigo como if (utype = = INT) prinii("dato incorrecto Sod en utype\n”, ulyp Las uniones pueden presentarse dentro de estructuras y arreglos, y viceversa. La notacién para tener acceso @ un miembro de una unién en una estructura (0 Viceversa) es idéntica a la de las estructuras anidadas. Por ejemplo, en el arreglo de estructuras defirido por tract {164 estmuctuRAS capituto int utype; union { al; float fval; char *sval; ) symtebINSYM; al miembro ival se le refiere como En efecto, una unién es una estructura en la cual todos los miembros tiei un desplazamiento de cero a partir de la bas grande para mantener al miembro ‘més anc! jas las mismas operaciones sobi luras: asignacion o copia como unidad, tom miembro. F con un valor del tipo de su pirimer miem- jormente solo se puede inicializar con un v El asignador de almacenamiento del cay tuna unién para obligar a gue una variable de limites de almacenamiento. lo 8 muestra cémo se puede alineada para una clase pari 6.9 Campos de bits Cuando el espacio de almacenamiemo es escaso, puede ser necesario Paquetar varios objetos dentro de una sola palabra de maqui 8 un conjunto de banderas de un bit er aplicaciones como tat ara compiladores. Los formaios de datos impuestos externa faces hacia dispostivos de hardware, frecuentemente requieren tomar partes de una palabra, Imaginese un fragmento de un compilador que manipula una tabla de simbé los. Cada identificador dentro de un programa tiene cierta informacién asoci ‘41, por ejemplo, si es o no una palabra reservada, si es 0 no externa y/o est ¥ otros aspectos. La forma mas compacta de codifical conjunto de banderas de un bit dentro de un char o int La forma usual en que esto se realiza es definiendo un conjunto de “mai ras!” correspondientes a las posiciones relevantes de bits, como en secCION 69 CAMPOS DE BITS 165 ‘#define KEYWORD 01 define EXTERNAL 02 #define STATIC 04 ‘enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 }; a ser cosa de complemento, 145 expresiones aparecen frecuentemente: lags |= EXTERNAL } STATIC; enciende los bits EXTERNAL y STATIC en flags, en tanto que flags 6 = ~(EXTERNAL j STATIC); Jos apaga, ¥ if (lags & (EXTERNAL | STATIC)) = = 0) .. s estén apagados. es verdadero si ambos Ait yacer eqs sed Pia Gri, Por Chip, a bolos #define podria haber sido reemplazada por la definici6n 0 en que sigue al caracter dos puntos representa : idades si ipos son dedarados unsigned int para ase Los campos individuales son referidos en la misma forma que para otros iembros de estructuras: flags.is__keyword, flags.is_extemn, etc, Los campos comportan como 2equefios enteros y pueden participar en expresiones aritmé- como lo hacenotros enteros. Asi, el ejemplo previo pudo eseribirse més na- almente como flags.is_oatera = flags.ia_static = 1;166 esrRUcTURAS cap para encender los bi flags.is_ext = flags.is_statie = 0; para apagarlos; y lag.is_extern = = 0 8 flage.ie_static = = 0) para probarlos. Casi todo acerea de los campos es dependiente de la implan o pueda traslapar al ‘campos no necesitan tener nombre; los campos sin. nombre (dos pk plitud solamente) se emplean para aderecha en algunas méquinas y de ierda en otras. Esto significa que aunque los campos son itiles el mantenimiento de estructuras de datos definidas internamente, la pregunts ‘qué punta viene primero tiene que considerarse cuidadosamente cuando se ionan datos definidos externamente; ‘os programas que dependen de tales. no son transportables. Los campos sélo se pueden declarar como enteros; u idad, se debe especificar explicitamente signed o unsigned. son arregios y no tienen direcciones, ce modo que el operador & no puede arse a ellos, con su medio amt trado antes. En ese c¢ de funciones que proporcionan jo de memoria, rat mM la bibl ©stdndar, normalmente el teclado, con getchar: int getchar(void) Las propiedadles de las funciones de docena de headers; ya hemi sjemplo, la biblioteca jeva inca en la entrada y de nuevo en la capituLo 7: Entrada y salida 167 que hasta ahora nolas hemos destacado. Sin embargo, los programas interactéan de cadenas, mane-168 ENTRADA ¥ SALIDA carro 7 getchar regresa el siguiente cardoter de la entrada cada vez que se invoca, 0 EOF cuando encuentra fin de archivo, La sonstante simbdlica EOF esta definida en . El valor es tipicamente 1, pero las pruebas se deben escribir en fun- cién de EOF, de modo que sean independientes del valor especifico. En muchos medios ambientes, un archivo puede tomar empleando la convencién < para redireccionamiento de entrada: prog prog nombrearch: si prog ida puede ser liza putchar, prog >archsal escribira la salida estndar hacia archsal. Si se per ntereonexion, prog | otroprog deja la salida estindar de prog en la entrada estandar de otropr La salida producida por printf tamtién encuentra su camino haci tandar. Las llamadas a putchar y a printf pueden estar traslapadas —la salid aparece en el orden en que se hicieron las lamadas. : Cada archivo fuente que se refiera a una funcién de biblioteca de entrada/' lida debe contener la linea #include ida con getchar, putohar y p' lad es sufiviente para comenzar. Est 8 particularmente cierto si se emplea la redireccidn para conectar la salida de seccion 7.2 SALIDA CON FORMATO-PRINTE 169 programa a la en:rada de otro. Por ejemplo, considérese el programa lower, que convierte su entrada a minisculas: #include #include convierte la entrada a mindsculas *) main( ) Int; la, y represa los otros caracteres in sane * como getchar y putchar en y tolower en ido macros, evitandose asi la sobrecarga di |. Eseriba un programa que convierta mayusculas a min sndiendo del nombre con que s¢ invoque, dado en argy 7.2. Salida con formato—printt a funcién de ida printf traduce val res. La deseripeidn de para la definicién comple- are,, are , da formato e imp! cl control de format. Regresa el La cadena de formato contiene dos tipos de obj ue son copiados al flujo de de los euales causa la conversion é sus argumentos en la salida estandar bajo \lmero de caracteres impresos. : caracteres ordinarios,70 exTRADA Y SALIDA ‘caPrTUuLo 7 ‘* Un signo menos, que especifica el ajuste s la izquierda del argumento conver- rimero que especifica el ancho mi 1» de campo. El argumento converti- cadena que ‘0 el numero minimo de digitos para un entero, © Unahsi Ie) si sera como un long. Los caracteres de conversién se muestran en ba tabla 7-1. Si el cardcter después del Ye no es una especificacién de conversién, el comportamiento no esta definido, TTARLA 7-1. CONVERSIONES BASICAS DE PRINTF CaRACTER TIPO DE ARGUMENTO: IMPRESO COMO vimero decimal. ° vimero octal sin signo (sin ero inicial. aX ; niimero hexadecimal sin signe (con un Ox 0 OX inicial, usando abcd ‘© ABCDEF para 10, ... 1S. 8 i; mimero decimal sin signo. e i; cardcter se 5 cchar *; imprime caracteres de una cadena hasta un \O" o el nimero de: racteres dado por la precsién 1 double; [—Jm.dddddd, en donde et nimero de ds esta dado por ta preci sin (predeterminado 3 6), ,£ | double; [-]m.dddddde = xx 0 [bn.ddddddE + xx, en donde el nimero d dis esta dado por la precision (predeterminado a 6) aG ‘expouente es menor que —4 0 mayor 0 i8t P ;puntador (representacion dependiente de la instalacion). % ido en ningiin argumento: imprime un Ye. far por +, en cuyo caso el valor do el siguiente argumento (que debe ser int), Por ejemplo, pat ir al menos max caracteres de una cadena s, print(%6.0", max, 8); SECCION 73 LSTAS DE ARGUMENTOS DE LONGITUD VARIABLE 171 La mayoria de lasconversiones de formato se han ilustrado en capitulos ante- siores. Una excepcién es la precisién relacionada con las cadenas dda campo para que se pueda apreciar su extensién, 6s: ‘hola, mundo: 96108: ‘hola, mundo: %,108: ‘hola, mund: 4-108: ‘hola, mundo: 96.15 ‘hola, mundo: 194715: ‘hola, mundo 418.106: hola, mand: 15.105: ‘hola, mund printf emplea su primer argumento para decidir cuéntos ceuiles son sus tipos. printf se con hay suficientes argumentos a si ia diferencia entre estas dos Js FALLA si ¢ contiene % +/ ); f+ SEGURO «/ La funcién sprintf realiza las mismas conversiones que printf, pero almacena ta salida en de una cadena: sprinti(char ‘cadena, char “format, ar ) arg, et, de acuerdo con format como antes, pero coloca el resultado en sadendjen vez de en la salida es- {eadenal debe ser suficientemente grande como pa ‘imal de acuerdo con la costumbre local, y separar lineas largas de texto. 0 7.3 Listas de argumentos de longitud variable Esta seccion contiene la realizacion de una version minima de printf, para Mostrar cémo escribir una funcién que procese una lista de argumentos de lot ‘ud variable en una forma transportable. Puesto que estamos interesados princi Palimente en el procesemiento de argumentos, minprint! procesara la cadena de ‘ormato y los argumentos, pero llamara al printf real para hacer las conversiones te formato.172 ENTRADA Y SALIDA caprtuto! La declaracién correcta para printf es Ant print(char * donde la declaracién .. significa que el mimero y tipo de esos argumentos . s6lo puede aparecer al final de la lista de argument ‘Nuestra minprintf se declara como ) ya que no regresard la cuenta de caracteres que regresa printf El truco est4 en cémo minprintf recorre Ia lista de argumentos cuando la ls ni siquiera tiene un nombre. El header estindar contiene un junto de macrodefiniciones que definen cénio avanzar sobre una mentos. La realizacion de este header variera de una maquina a interfaz que presenta es void minp! El tipo va_list se emplea para declarar una variable que se referiré a argumento en su momento; enminprintf,es'a variable se llama ap, por ‘ment pointer" (apuntador a argumento). La macro va_stazt ini apuntar al primer argumento sin nombre. Debe Hamarse una vez antes de ap. Al menos debe haber un argumento con nombre; el iltimo argumento nombre es empleado por va_start para Cada llamada de va_arg regresa un argumento y avanza ap al sigui va_arg emplea un nombre de tipo para deteminar qué tipo regresar y cudn. ge serit el avance, Finalmente, va-end realiza las labores de limpieza y arré {que sean necesarias. Debe invocarse antes que la funcin regrese. Estas propiedades forman la base de nuestro printf simplificado: #include J+ minprintf: printf minima com lista variable de argumentos +/ void minprint{char #fmt, ..) { va_list ap; /+ apunta a cada ary sin nombre en orden «/ cchar «p, ‘eval; int ival; double aval ce que ap apunte al ler. arg sin nombre «/ (+ +p) { case val = va_arg(ap, SECCION 74 ENTRADA CON FORMATO-SCANF 173, for (oval = va_arg(ap, char +); esval; sval+ +) ‘putchar(+sval); va_end(ap); /* limpia cuando todo esté hecho +/ t 7-3, Aumente minprin fa que maneje otras de las caract printf. 0 7.4 Entrada con formato—scant La funcién scanf es la entrada andloga de print, y proporciona muchas de las mismas facilidades de conversién en la direccién opuesta int scanf{char slormat, ...) scanf lee caracteres dela entrada estindar, los cificaciones que estan en format, resultados a través de los argu- ments restantes. El a:gumento de formato se describe abajo; los otros argumen- tos, cada uno de lo; cwales debe ser un apuntador, indican dénde deberd almacenarse la entrada correspondientemente conv "omo con printf, esta Seccidn es un resumen de las po: iva, scanf se detiene cuando termi entrada no coincide con la esp nnimero de ftems de ertrada que ecidir cudntos iter tese que esto es "no coincide cor llamada a seanf conti ‘er que ya fue conv Existe también una funcién sscanf que lee de una cadena y no de la entrada ssténdar: int escanf(char ‘cadena, char format, arg, ary =) preta de acuerdo con las espe ‘iden con éxito. Esto se puede emplear para ron, Al final del archivo, regresa EOF; n6- tardcter de entrada era especificacidn en la cadena de formato. La siguiente ala busqueda inmediatamente después de!174 ENTRADA Y SALIDA caprtuto 7 Rasirea la cadena de acuerdo con el formato en format, y almacena el valor resultante a través de arg,, arg,, etc. Estos argumentos deben ser apuntadot La cadena de formato generalmente contiene especificaciones de conversién, las cuales son empleadas para controlar la conversién de entrada. La cadena de formato puede contener: * Blancos o tabuladores, los cuales son ignorados. (no %), que se espera coincidan con el siguiente carée- spacio en blanco del flujo de entrada, cs de conversion, consstenes eel carcter Mo, un cardcteropkati= vo de supresién de asignacién +, un ntmero optativo que especifica el ancho maximo de campo, una b, I, 0 L optativa que indica la amplitud del objetivo, yum cardcter de conversion. La especificacion de conversion dirige la conversion del siguiente campo de en- trada, Normalmente el resultado se coloca en la variable apuntada por el argu ‘mento correspor . Si se indica la supresion de asignacién con el cardcter +, sin embargo, ida es ignorado y no se realiza asignacion alguna, Un campo de ‘4 definido como una cadena de caracteres que no son espa cio cn blanco; se extiende hasta el ite espacio en blanco o hasta que el blanco. es de espacio en blanco son tabulador, de carro, tabulador vertical y avance de El cardeter de conversién indica la interpretacién del campo de entrada. El argumento correspondiente debe ser un apuntador, como es requerido por la se- ‘mintica de las llamadas por valor de C. Los eacacteres de conversion se mu en la tabla 7-2. TTAMLA 7-2, CONVERSIONES BASICAS DE SCANE ‘CARACTER Dao DE ENTRADA: “IPO DE ARGUMENTO: 7 aaraaaas Eleni puede eure xl iad con Oo hxadeill icado, El sako normal sobre los espacios en blanco 8 suprimido; para leer el siguiente espacio no blanco, use 961s. SeCCION 74 ENTRADA CON FORMATO-SCANF 175 5 cadena de caracter taracteressuficiente que serd agregada ‘nero de punto flotante con signo, punto decimal y exponente optativos; float « % % literal; no se hace asignacién alguna, lada); char *, apunta a un arreglo de ara la cadena y una terminacion "Ot Los caracteres de conversion d, i, 0, u, x pueden ser precedidos por h para indicar que en la lista de argumentos aparece un apuntador a short en lugar de 2 int, por] (letra ele} para indicar que aparece un apuntador a long en la lista de argumentos. En forma semejante, los caracteres de conversién e, f, ¢ pueden idos por 1 para indicar que hay un apuntador a double en lugar de a float en la lista de argimentos. ‘Como un primer ejemplo, Ia rudimentaria calculadora del capitulo 4 se puede cscribir con scanf para hacer la conversién de entrada: #include ‘Suponga que desearos leer lineas de entrada que contienen fechas de la forma 25 Die 1988 La proposicién scant es int day, year; char monthname(20]; scanf("%d %s Md", Say, monthname, Seat); ‘No se emplea & con monthname, ya que un nombre de arreglo es un apuntador. Pueden aparecer caracteres literales en la cadena de formato de scant, y de- ben coincidir con los mismos caracteres de la entrada. De modo que podemos leer fechas de la forma mn/dd/yy con esta proposicién scant: int day, month, year; | Bmonth, Bday, ye176 ENTRADA ¥ SALIDA sscanf ignora los blancos y los tabuladores Ademés, salta sobre los espacios en blanco (blancos, tabuladores, nuevas lineas, tc.) mientras busca los valores de entrada. Para leer de entradas cuyo format sscanf. Por ejemplo, suponga que deseamos leer lineas que pueden contener fe chas en cualquiera de las formas anteriores. Entonces podemos escribir rma 25 Dic 1988 +/ , day, Gyear) = = 3) } Las llamadas a scanf pueden estar me ciones de entrada. La siguiente llamada a c en lugar de scant("%d", &n); Este error generalmente no se detecta en tiempo de compilacién. Ejercicio 7-4. Escriba una versién privada de scanf andloga a minprinti de seccidn anterior. Ejercicio 7-5. Reescriba la calculadora postfija del capitulo 4 usando scanf y/ seanf para hacer la entrada y la conversién. 0 7.5. Acceso a archivos Hasta ahora todos los ejemplos han la salida estandar, las cuales se definen tivo local es escribir un programa que dé acceso a un de la entrada estandar y escrito toma ACCESO A ARCHIVOS 177 SBECION 75 colector de entradas de sropésito general para prograi idad de vener acceso a los arc! eatzc yc {mprime el contenido delos archivos x.c y y.c (y nada mds) en la salida estandar. La pregunta es como hacer que los archivos nombratdos sean leidos —esto es, 10 conectar las propesiciones que leen los datos, con los nombres externos que o tiene en m {que Ser abierto por la fu no como x.¢ 0 y.c, hace ‘yo (cuyos detal cen posteriores lectut Este apuntador informacién acerca del archivo, posicién de cardcter actual en el buifer, y si han ocurrido errores o fin de archivo. Los usuarios no ne detalles, debido a que las definiciones obtenidas de incluyen una declaraci6n de estructura Hamada FILE. La tinica declaracién necesaria para un apuntador de archivo se ejemplifica por junos arreglos y negociaciones con el sistema operati- . y regresa un apuntador que sera usado o escrituras del archiv FILE «ip; FILE «fopen(char +nombre, char modo); FILE, Notese que FILE & ra; esta definido con un typedef, (Los det ma UNIX se explican en la seccion 8.5.) La llamada a fopen en un programa es fp = fopen(nomb:e, modo); El primer argumento de fopen es una cadena de caracteres que contiene el nom- yen lectura ("x"), escritara (w"), y afladido ("a entre archivos de texto ybinarios; para los ditimos, debe ese de ta cadena de modo. Si un archivo que nc existe se abre para escribir 0 afadir, se crea, si es p ble. Abrir un archivo existente para escribir provoca que los contenidos anterio- res sean desechadas, mientras que abrirlo para afladir los preserva. Es un error ar de leer un archivo que no existe, y también pueden haber otras causas de ttror, como tratar de leer un archivo cuando no se tiene permiso. Si existe cual- la seccion 1 en el apéndice B.)178 erRapa ¥ SALIDA carrots Lo siguiente que se requiere es una forma de leer o escribir el archivo una que est abierto, Existen varias posibilidades, de las cuales gete y pute son las ples. gete regresa el siguiente cardcter de un archivo; necesita el apuntadg del archivo para decirle cual es. (FILE stp) ete regresa el siguiente caracter del flujo al que se refiere fp; regresa EOF si o re algin error. pate es una funcion s int puto(int ¢, FILE fp) pute escribe el cardcter ¢ en el archivo fp y regresa el cardcter escrito, o EOI ‘ocurre un error. Tal como getchar y putchar, gote y pute pueden ser macros; lugar de funciones ‘Cuando se arranca un programa en C, a medio ambiente del sistema oper vo es responsable de abrir tres archivos y proporcionar apuntadores de ar ppara ellos. Estos archivos son la entrada estandar, la salida estandar y el estndar; los apuntadores de archivo correspondientes se llaman stdin, st stdery, y estin declarados en . Normalmente stdin se conecta a do y stdout y stderr se conectan a la pant rigidos a archivos 6 a in getchar y putchar puede! y stdout, como sigue: #deline getchar( ) _getc(sidin) +#define putchar(c) pute((c), stdout) da o salida de archivos con formato se pueden emplear las funcic nes fscanf y fprintf. Estas son idénticas a scanf y printf, excepto en que el pri argumento es un apuntador de archivo que especifica el archivo que sera la cadena de formato es el segundo argumento. int fprint(FILE “fp, char "form Habiendo hecho a un lado estos prerrequisitos, ya estamos ahora en posi jr el programa cat, que concatena archivos. El diseno se ha encontr argumentos en la linea de ord y se procesan en orden. Si no ha nes, se interpretan como nombres de argumentos, se procesa la entrada es! include 1+ cat: concatena archivos, versién 1 »/ main(int arge, char *argu{ |) { FILE “ip; void filecopy(FILE +, FILE SHCCION 76 [MANEIO DE ERRORES-STOERR Y EXIT 179 i (erge == 1) /* sin args; copia la entrada esténdar filecopy(stdin, stdout); lee while (——arge > 0) 4 (Up = fopen(++ +argy, "x")) = = NULL) { pinti("cat: no ge puede abrir %s\n", argv); rotum 1 else { lecopy dor retum 0; ) Js ilecopy: 60 void filecopy(FILE 1 inte; chive ifp al archivo ofp */ FILE +ofp) ile ((e = getcti Pute(e, ofp); != EOF) ) Los apuntadores dle archivo stdin y stdout son objetos de tipo FILE +. Sin embar- ‘20, son constantes, #0 variables, por lo que no es posible asignarles algo. La funcién 3t fcloso(FILE +ip) slo inverso de fopex; interrumpe la conexién que fue establecida por fopen en- reel apuntador de archivo y el nombre externo, liberando al apuntador de archi- vo para otro archivo, Puesto que la mayoria de los sistemas operativos tienen al- sotre el mimero de archivos que un programa pu yerar los apuntadores de archivo ‘cuando ya no son necesarios, usar felose en un archivo de sal do la salida, Cuando un programa termina normalmente, maticamente para cada archivo abierto. (Se puede cerrar son necesarios, También pueden ser reasignados por la funcién de bi reopen.) 7.6 Manejo de errores—stderr y exit E] manejo de los errores en cat no es el ideal. El pri de tener acceso a unode los archivos por alguna razén, iagndstico se imprime