¡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: