27.03.2017 Views

programacion-en-c-metodologia-algoritmos-y-estructura-de-datos-editorial-mcgraw-hill

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

PROGRAMACI~N EN c<br />

Metodología, <strong>algoritmos</strong><br />

y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

as<br />

,>'<br />

. .<br />

'.<br />

L<br />

Ignacio Zahonero Martinez<br />

Departam<strong>en</strong>to <strong>de</strong> L<strong>en</strong>guajes y Sistemas Informáticos e Ing<strong>en</strong>iería <strong>de</strong>l Software<br />

Facultad <strong>de</strong> Informática/Escuela Universitaria <strong>de</strong> Informática<br />

Universidad Pontificia <strong>de</strong> Salamanca. Cumpus Madrid<br />

MADRID BUEN,OS AIRES CARACAS -,GUATEMALA. LISBOA MÉXICO<br />

NUEVA YORK PANAMA SAN JUAN SANTAFE DE BOGOTA SANTIAGO SA0 PA,ULO<br />

AUCKLAND HAMBURG0 LONDRES MILAN MONTREAL NUEVA DELHI PARIS<br />

SAN FRANCISCO SIDNEY SINGAPUR ST. LOUIS TOKIO *TORONTO


CONTENIDO<br />

Prólogo , . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

xv<br />

PARTE I. METODOLOGíA DE LA PROGRAMACIÓN<br />

Capítulo 1. Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación . .. . . ................<br />

1.1. ¿Qué es una computadora? . . . . .. . . . .. . . . . . .. . . . . . . . . .. . . . . . . . ..<br />

1.2. Organización física <strong>de</strong> una computadora (hardware) . .. . . . . . . . . . . . . .. .. .. . . ... .. .<br />

1.2.1. Dispositivos <strong>de</strong> EntradafSalida (E/S) . . . . . . .. . .. . . . . . . . . . . . . . . . . .. .. . . . .. .. ... . . ..<br />

1.2.2. La memoria c<strong>en</strong>tral (interna) . ...........................................<br />

1.2.3. La Unidad C<strong>en</strong>tral <strong>de</strong> Proceso (UCP) . . . . .<br />

1.2.4. El microprocesador . . . . . . . . . . . . . . . . . . .<br />

1.2.5. Memoria auxiliar (externa) . . . . . . . . . . . . .<br />

. . . . . . . . .<br />

1.2.6. Proceso <strong>de</strong> ejecución <strong>de</strong> un programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

1.2.7. Comunicaciones: mó<strong>de</strong>ms, re<strong>de</strong>s, telefonía RDSI y ADSL . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

1.2.8. La computadora personal multimedia i<strong>de</strong>al para 1<br />

1.3. Concepto <strong>de</strong> algoritmo . . . . . . . . . . . . . . . . . . . . . . . . .<br />

1.3.1. Características <strong>de</strong> los <strong>algoritmos</strong> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

1.4. El software (los programas) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

1.5. Los l<strong>en</strong>guajes <strong>de</strong> programación<br />

1.5.4. L<strong>en</strong>guajes <strong>de</strong> alto nivel .................................................<br />

..............................<br />

. . . . . . . . .<br />

1.6. El l<strong>en</strong>guaje C: historia y características . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

1.6.1. V<strong>en</strong>tajas <strong>de</strong> C ....................................<br />

1.6.2. Características ......................<br />

1.6.3. Versiones actu<br />

.....................................................................<br />

Capítulo 2. Fundam<strong>en</strong>tos <strong>de</strong> programación . . . . .<br />

2.1. Fases <strong>en</strong> la resolución <strong>de</strong> problemas .<br />

......................<br />

..<br />

2.1.1. Análisis <strong>de</strong>l problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

2.1.2. Diseño <strong>de</strong>l algoritmo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

2.1.3. Herrami<strong>en</strong>tas <strong>de</strong> la programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

2.1.4. Codificación <strong>de</strong> un programa . . . . . ...................................<br />

2<br />

4<br />

4<br />

5<br />

6<br />

9<br />

10<br />

10<br />

12<br />

12<br />

13<br />

15<br />

16<br />

17<br />

19<br />

20<br />

20<br />

21<br />

22<br />

22<br />

23<br />

23<br />

23<br />

25<br />

25<br />

26<br />

26<br />

27<br />

28<br />

30<br />

31<br />

32<br />

33<br />

36<br />

P<br />

V


vi<br />

Cont<strong>en</strong>ido<br />

2.1.7. Docum<strong>en</strong>tación y<br />

2.2. Programación modular . . . . .<br />

2.3. Programación <strong>estructura</strong><br />

2.3.1. Recursos abstractos . . . . . . . . . . . ...................<br />

.......... .........._...... . . . . . . . . .<br />

tructurada: estru<br />

...........<br />

..................<br />

......_..........<br />

............_... .................<br />

2.6.8. Iteración y e<br />

2.7. Métodos formales <strong>de</strong> verificación <strong>de</strong> programas ..._............. ...........<br />

2.7.1. Aserciones . . . . . . . . . ...................................<br />

2.8. Factores <strong>en</strong> la calidad <strong>de</strong>l software . . . .<br />

................<br />

......._.....<br />

.............<br />

. . . . . . . .<br />

37<br />

38<br />

38<br />

49<br />

40<br />

40<br />

40<br />

41<br />

42<br />

42<br />

43<br />

52<br />

53<br />

54<br />

55<br />

55<br />

56<br />

56<br />

57<br />

57<br />

57<br />

58<br />

58<br />

59<br />

60<br />

60<br />

62<br />

63<br />

64<br />

65<br />

65<br />

66<br />

PARTE II. FUNDAMENTOS DE PROGRAMACI~N EN c<br />

Capítulo 3. El l<strong>en</strong>guaje C: elem<strong>en</strong>tos bá<br />

........._......<br />

3.1. Estructura g<strong>en</strong>eral <strong>de</strong> un programa <strong>en</strong> .......,._...<br />

3.1.1. Directivas <strong>de</strong>l prepro<br />

3.1.2. Declaraciones global<br />

3.1.3. Función main ( ) . . .<br />

3.1.4. Funciones <strong>de</strong>finidas PO<br />

3.1.5. Com<strong>en</strong>tarios . . . . . . . . . . . . . . . ................<br />

................ ..._............... 82<br />

3.2, Creación <strong>de</strong> un programa . . . . . . . . . . . . . . . .<br />

3.3. El proceso <strong>de</strong> ejecución <strong>de</strong><br />

3.4. Depuración <strong>de</strong> un program<br />

3.4.2. Errores lógicos . . .<br />

....................................<br />

3.4.5. Errores <strong>en</strong> tiempo <strong>de</strong> .................. ..........<br />

.................. 90<br />

............. 90<br />

. . . . . . . . . . 90


?<br />

Cont<strong>en</strong>ido<br />

vi¡<br />

3.6.5. Signos <strong>de</strong> puntuación y separadores ............................................. 92<br />

3.6.6. Archivos <strong>de</strong> cabecera ................................. ....... 92<br />

3.7. Tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong> C . . ..... ............... 92<br />

3.7.1. Enteros(int) .............................................................. 93<br />

3.7.2. Tipos <strong>de</strong> coma flotante ( f 1 oat<br />

3.7.3. Caracteres (char) ................<br />

3.8. El tipo <strong>de</strong> dato LÓGICO ..........<br />

3.8.1. Escritura <strong>de</strong> valores lógicos ....... 97<br />

3.9. Constantes ............... .............<br />

es .......................................................... 98<br />

3.9.2. Constantes <strong>de</strong>finidas (simbólicas) .............................................. 101<br />

3.9.3. Constantes <strong>en</strong>umeradas . . ......... ........<br />

3.9.4. Constantes <strong>de</strong>claradas con latile.. ....<br />

3.10. Variables ...................................<br />

.,<br />

3.10.1. Declaracion ............................................................... 103<br />

3.10.2. Inicialización <strong>de</strong> variables .. .... ..... .... 105<br />

3.10.3. Declaración o <strong>de</strong>finición ..................................................... 105<br />

3.11. Duración<strong>de</strong>unavariable ........................................................... 106<br />

3.11.1. Variables locales . ...... ..... .... 106<br />

3.11.2. Variables globales ................................ 106<br />

3.11.3. Variables dinámicas ........................................................ 107<br />

3.12. Entradas y salidas ........<br />

3.12.1. Salida ... ........<br />

3.12.2. Entrada . . ........................................ 111<br />

3.12.3. Salida <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres ............................................... 112<br />

3.12.4. Entrada <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres ... .... ..... ....... 112<br />

3.13. Resum<strong>en</strong> ....................................................................... 113<br />

3.14. Ejercicios ....................................................................... 113<br />

Capítulo 4. Operadores y expresiones .......................................................... 114<br />

4.1. Operadores y expresiones .......................................................... 116<br />

4.2. Operador <strong>de</strong> asignación . . .... ....... 116<br />

4.3. Operadores aritméticos ............................ . 117<br />

. . .<br />

4.3.1. Asociatividad .............................................................. 119<br />

4.3.2. Uso <strong>de</strong> paréntesis ........................................................ 120<br />

4.4. Operadores <strong>de</strong> increm<strong>en</strong>t n y <strong>de</strong>crem<strong>en</strong>tación<br />

4.5. Operadores relacionales ....................<br />

4.6. Operadores lógicos .......................................................... 125<br />

4.6.1. Evaluación <strong>en</strong> cortocircuito ...... ..... 127<br />

4.6.2. Asignaciones booleatias (lógicas) .............................................. 128<br />

4.7. Operadores <strong>de</strong> manipulación <strong>de</strong> bits ....................... 129<br />

4.7.1. Operadores <strong>de</strong> asignación adic ... ..... 130<br />

4.7.2. Operadores <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> bits (», «) ......................................<br />

4.7.3. Operadores <strong>de</strong> direcciones ....................................................<br />

131<br />

131<br />

4.8. Operador condicional ....... 132<br />

4.9. Operador coma ..... .........<br />

4.10. Operadores especiales<br />

4.10.1. El operador ( )<br />

4.10.2. El operador [ ]<br />

4.11. El operador SIZEOF .<br />

4.12. Conversiones <strong>de</strong> tipos<br />

4.12.1. Conversión im ......... ........<br />

4.12.2. Reglas ......<br />

......... ........<br />

4.12.3. Conversión explícita ........................................................ 136<br />

4.13. Prioridad y asociatividad . ... 136<br />

4.14. Resum<strong>en</strong> ....................................................................... 137


vi¡¡<br />

Cont<strong>en</strong>ido<br />

4.15. Ejercicios ....................................................................... 137<br />

4.16. Problemas ...................................................................... 139<br />

Capítulo 5. Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias if y switch ...................................... 142<br />

5.1. Estructuras <strong>de</strong> control .................. 144<br />

5.2. Las<strong>en</strong>t<strong>en</strong>cia if ..........................................................<br />

5.3. S<strong>en</strong>t<strong>en</strong>cia i f <strong>de</strong> dos alternativas: i f - e 1 se ...................................<br />

5.4. S<strong>en</strong>t<strong>en</strong>cias i f - el se anidadas ....................................... 150<br />

5.4.1. Sangría <strong>en</strong> las s<strong>en</strong>t<strong>en</strong>cias i<br />

5.4.2. Comparación <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias<br />

5.5. S<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> control switch ....................................................... 154<br />

5.5.1. Caso particular <strong>de</strong> case .................................. 159<br />

5.5.2. Uso <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias swi t c ........................<br />

5.6. Expresiones condicionales: el operador ? : ...................................<br />

5.7. Evaluación <strong>en</strong> cortocircuito <strong>de</strong> expresiones lógicas ................................... 161<br />

5.8. Puesta a punto <strong>de</strong> programas ..................................................<br />

5.9. Errores frecu<strong>en</strong>tes <strong>de</strong> programación ............................................<br />

5.10. Resum<strong>en</strong> ...... ........ 164<br />

5.11. Ejercicios .................................................<br />

5.12. Problemas ...................................................................... 167<br />

Capítulo 6. Estructuras <strong>de</strong> control: bucles<br />

6.1. La s<strong>en</strong>t<strong>en</strong>cia whi 1 e .....................................................<br />

6.1.1. Operadores <strong>de</strong> inc<br />

6.1.2. Terminaciones anormales <strong>de</strong> un ciclo ........ 174<br />

6.1.3. Diseño efici<strong>en</strong>te d<br />

6.1.4. Bucles while con cero iteraciones ............................................. 174<br />

6.1.5. Bucles controlados por c<strong>en</strong>tinelas ....... 175<br />

6.1.6. Bucles controlados por indicadores (ban<strong>de</strong>ras) .................................<br />

6.1.7. La s<strong>en</strong>t<strong>en</strong>cia break <strong>en</strong><br />

6.1.8. Bucles while (true) ................................................. 178<br />

6.2. Repetición: el bucle €or .....<br />

6.2.1. Difer<strong>en</strong>tes usos <strong>de</strong> bucles for ................................................. 184<br />

6.3. Precauciones <strong>en</strong> el uso <strong>de</strong> for ...................................................... 185<br />

6.3.1. Bucles infinitos .............<br />

6.3.2. Los bucles for vacíos .......<br />

6.3.3. S<strong>en</strong>t<strong>en</strong>cias nulas <strong>en</strong> bucles for ................................................ 188<br />

6.3.4. S<strong>en</strong>t<strong>en</strong>cias break y continue ...........<br />

188<br />

6.4. Repetición: el bucle do ... whi le .............. ............................. 190<br />

6.4.1. Difer<strong>en</strong>cias <strong>en</strong>tre while y do-while .......................................... 191<br />

6.5. Comparación <strong>de</strong> bucles while, for y do-whi le<br />

6.6. Diseño <strong>de</strong> bucles ............................<br />

6.6.1. Bucles para diseño <strong>de</strong> sumas y productos ...<br />

6.6.2. Fin <strong>de</strong> un bucle ............................................. 194<br />

6.6.3. Otras técnicas d ..... .... 196<br />

6.6.4. Bucles for vacíos .......................................................... 196<br />

6.7. Bucles anidados ...................................... 197<br />

6.8. Resum<strong>en</strong> ...........................................................<br />

. . .<br />

6.9. Ejercicios ...........................................................<br />

6.10. Problemas ............................................... 203<br />

6.11. Proyectos d 206<br />

Capítulo7. Funciones ...................................................................... 208<br />

7.1. Concepto<strong>de</strong>función .............................................................. 210<br />

7.2. Estructura<strong>de</strong>unafunción .......................................................... 211<br />

7.2.1. Nombre <strong>de</strong> una función ...................................................... 213


.<br />

Cont<strong>en</strong>ido<br />

7.2.2. Tipo <strong>de</strong> dato <strong>de</strong> retorno . . . . . . . . . . . . . . . . . . . . ..........................<br />

7.2.3. Resultados <strong>de</strong> una función . . . .. . . . .. . . . ...............................<br />

7.2.4. Llamada a una función . . . .<br />

. . .. . .. .<br />

7.3. Prototipos <strong>de</strong> las funciones . . . . . . . I . . . . . . . . . . . . . .. . . . ....................<br />

7.3.1. Prototipos con un número no<br />

.~.............~....<br />

7.4. Parámetros <strong>de</strong> una función . .. . . . ..........................<br />

7.4.3. Difer<strong>en</strong>cias <strong>en</strong>tre paso <strong>de</strong> variables por valor y por refer<strong>en</strong>cia . . . . . . . . . .<br />

7.4.4. Parámetros cons t <strong>de</strong> una función . . . . . . . . . . . . . . .<br />

i<br />

E<br />

..........................<br />

7.6. Ámbito (alcance) . . . . .. . . ................................ . . . . . . . .<br />

7.6.1. Ambito <strong>de</strong>l programa . . . . . . . . . . . . . . . . . . . . . . . . ....................<br />

7.6.2. Ambito <strong>de</strong>l archivo fu<strong>en</strong>te . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . .. . . . .<br />

7.6.3. Ambito <strong>de</strong> una función . . . . . . . .......................... . . . . . . . .<br />

7.6.4. Ambito <strong>de</strong> bloque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ....................<br />

7.6.5. Variables locales . . . . . . . . . . . . . . . . . . . .. . . . . . . . . . . . . .. . . . . . . . .. . . . . . . . . . ..<br />

7.7. Clases <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to . . . . . . . ........................<br />

7.7.1. Variables automáticas . .. . . . .... . . . . . . . . . . . . . . . . . . . ......................<br />

7.7.2. Variables externas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .. . .. .. . . .. .<br />

7.7.3. Variables registro . . . . .. . . . . . . . . . . . . . . ...........................<br />

7.7.4. Variables estáticas . . ........................... _...............<br />

7.8. Concepto y uso <strong>de</strong> funcione a ............... ......................<br />

7.9. Funciones <strong>de</strong> carácter . . . . . . . . . . . . . . . . . . . . . . . . . . . . ......................<br />

7.9.1. Comprobación alfabética y <strong>de</strong> dígitos ........................ . . . . . . .<br />

7.9.2. Funciones <strong>de</strong> prueba <strong>de</strong> caracteres espe<br />

......................<br />

7.9.3. Funciones <strong>de</strong> conversión <strong>de</strong> caracteres . . . . . . . . . . ......................<br />

7.10. Funciones numéricas . . .. . . ........................<br />

. . . . . . .<br />

7.10.1. Funciones matemáticas . . . . . . . . . . . . . . . . . . . . . . . . . . . ......................<br />

7.10.2. Funciones trigonométricas .. .. . . . . . . . . . . . . . . .................<br />

7.10.3. Funciones logm’tmicas y expon<strong>en</strong>ciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . .<br />

7.10.4. Funciones aleatorias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ................<br />

7.13. Visibilidad <strong>de</strong> una función . .<br />

7.13.1. Variables locales fr<strong>en</strong><br />

7.13.2. Variables estáticas y automáticas . . . . . . . . ............................<br />

7.14. Compilación separada . . . . . ............................. ..........<br />

7.17. Resum<strong>en</strong> . . . . . . . . . . . . . . .<br />

7.19. Problemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ........................<br />

Capítulo 8. Arrays (listas y tablas) . . . . ............................. ............<br />

....................................... ....................<br />

8.1.2. Subíndices <strong>de</strong> un array<br />

8.1.3. Almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> me<br />

..........................<br />

...............<br />

s arrays . . . .. . . . .. . ....................<br />

8.1.4. El tamaño <strong>de</strong> los arrays . . . . . . . . . . . . .......................<br />

8.1.5. Verificación <strong>de</strong>l rango<br />

8.2. Iniciaiización <strong>de</strong> un array<br />

8.3. Arrays <strong>de</strong> caracteres y cad<strong>en</strong>as <strong>de</strong><br />

8.4. Arrays multidim<strong>en</strong>sionales . . .. .<br />

8.4.1. Inicialización <strong>de</strong> arrays mu<br />

. .. . . . . . . . . . . . . . . . . . . . . . . . . .. . . . . . .................<br />

. .. . . . . . .......................<br />

229<br />

230<br />

230<br />

230<br />

23 1<br />

231<br />

231<br />

231<br />

232<br />

232<br />

234<br />

234<br />

235<br />

236<br />

236<br />

237<br />

237<br />

238<br />

238<br />

239<br />

240<br />

243<br />

244<br />

245<br />

247<br />

249<br />

250<br />

25 I<br />

254<br />

258<br />

260<br />

260<br />

26 1<br />

262<br />

263<br />

264<br />

264<br />

266<br />

269<br />

270


X<br />

Cont<strong>en</strong>ido<br />

8.4.2. Acceso a los elem<strong>en</strong>tos <strong>de</strong> los arrays bidim<strong>en</strong>sionales ........................ 271<br />

8.4.3. Lectura y escritura <strong>de</strong> arrays bidim<strong>en</strong>sionales ..................................... 272<br />

8.4.4. Acceso a elem<strong>en</strong>tos mediante bucles ..................................<br />

8.4.5. Arrays <strong>de</strong> más <strong>de</strong> dos dim<strong>en</strong>siones ............................. .......... 274<br />

8.4.6. Una aplicación práctica ............................ ............... 274<br />

8.5. Utilización <strong>de</strong> arrays como parámetros ........... .............................. 276<br />

8.5.1. Precauciones .......<br />

..................................<br />

8.5.2. Paso <strong>de</strong> cad<strong>en</strong>as como parámetros ............................<br />

8.6. Ord<strong>en</strong>ación <strong>de</strong> listas .....................................<br />

............. 282<br />

8.6.1. Algoritmo <strong>de</strong> la burbuja ........................... ................... 282<br />

8.7. Búsqueda <strong>en</strong> listas .................................. ........................ 284<br />

8.7.1. Búsqueda secu<strong>en</strong>cia1 ................<br />

............................. 28.5<br />

8.8. Resum<strong>en</strong> .........................<br />

.............................<br />

8.9. Ejercicios ......................... ........................<br />

8.10. Problemas .............. ............................. ............. 291<br />

Capítulo 9. Estructuras y uniones ......................... ........................<br />

9.1. Estructuras ............................. .......................<br />

<strong>de</strong> una <strong>estructura</strong> ........ ............................<br />

9.1.2. Definición <strong>de</strong> variables <strong>de</strong> <strong>estructura</strong>s ...........................................<br />

9.1.3. Uso <strong>de</strong> <strong>estructura</strong>s <strong>en</strong> asignaciones ........................ ..............<br />

9.1.4. Inicialización <strong>de</strong> una <strong>de</strong>claración <strong>de</strong> <strong>estructura</strong>s ......... .......................<br />

9.1.5. El tamaño <strong>de</strong> una <strong>estructura</strong> ............<br />

9.2. Acceso a <strong>estructura</strong>s .......................<br />

............................<br />

........<br />

..............<br />

....................<br />

9.3.1. Ejemplo <strong>de</strong> <strong>estructura</strong>s anidadas ......<br />

9.4. Arrays <strong>de</strong> <strong>estructura</strong>s ............ ........................... .........<br />

.............<br />

...................<br />

9.6. Uniones ....... .............................<br />

9.7. Enumeraciones ...........................<br />

........<br />

..............<br />

9.8. Campos <strong>de</strong> bit ....... ..............<br />

9.9. Resum<strong>en</strong> . ............................<br />

......................... ............<br />

294<br />

296<br />

297<br />

297<br />

298<br />

299<br />

300<br />

300<br />

300<br />

302<br />

302<br />

303<br />

304<br />

307<br />

308<br />

309<br />

3 10<br />

31 1<br />

314<br />

314<br />

315<br />

319<br />

320<br />

32 1<br />

Capítulo 10. Punteros (apuntadores) ............................ ...................... 322<br />

10.1. Direcciones <strong>en</strong> memoria ........................ ........................... 324<br />

10.2. Concepto <strong>de</strong> puntero (apuntador) ................ ...................<br />

10.2.1. Declaración <strong>de</strong> punteros ............ ........................<br />

10.2.2. Inicialización (iniciación<br />

10.2.3. Indirección <strong>de</strong> punteros<br />

............. 327<br />

10.2.4. Punteros y verificación d<br />

10.3. Punteros null y void ......<br />

10.4. Punteros a punteros ..... ........ 331<br />

10.5. Punteros y arrays ........ ........ 332<br />

10.5.1. Nombres <strong>de</strong> arrays nteros ............... ....................... 332<br />

10.5.2. V<strong>en</strong>tajas <strong>de</strong> los punteros ..................<br />

10.6. Arrays <strong>de</strong> punteros ........................<br />

10.6.1. Inicialización <strong>de</strong> u<br />

10.7. Punteros <strong>de</strong> cad<strong>en</strong>as ............. .......................<br />

........ 33.5<br />

............ 33.5<br />

10.7.1. Punteros versus arrays .......................


Cont<strong>en</strong>ido<br />

10.8. Aritmética <strong>de</strong> punteros .. .......................................... 336<br />

10.8.1. Una aplicación <strong>de</strong> ón <strong>de</strong> caracteres ............................. 338<br />

10.9. Punteros constantes fr<strong>en</strong>te a punteros a constantes .......... ........ 339<br />

10.9.1. Punteros constantes .............................................. 339<br />

10.9.2. Punteros a constantes ..... ................................. 339<br />

.......... 340<br />

Xi<br />

I<br />

10.11.1. Inicialización <strong>de</strong> u ....... 343<br />

................................... 348<br />

................. 349<br />

10.13. Resum<strong>en</strong> ........<br />

10.14. Ejercicios ...................... ............................... 352<br />

......... 353<br />

Capítulo 11. Asignación dinámica <strong>de</strong> memoria . . ................................... 354<br />

11.1. Gestión dinámica <strong>de</strong> la memoria .......................... ....... 356<br />

11.1.1. Almacén libre (free store) ................................ 357<br />

11.2. Función malloc ( ) ................. ................................ 357<br />

11.2.1. Asignación <strong>de</strong> memoria <strong>de</strong> un tamaño <strong>de</strong>sconocido ................ 361<br />

11.2.2. Uso <strong>de</strong> mal loc ( ) para arrays multidim<strong>en</strong>sionales ................<br />

11.3. Liberación <strong>de</strong> memoria, función free ( ) ...........................<br />

11.4. Funciones <strong>de</strong> asignación <strong>de</strong> memoria call í )y realloc í) ....................... 364<br />

11.4.1. Función calloc () ............................... ...... 364<br />

11.4.2. Función realloc () ............................................ 365<br />

11.5. Asignación <strong>de</strong> memoria para array .......................... 368<br />

11.5.1. Asignación <strong>de</strong> memoria interactivam<strong>en</strong>te .................... . 369<br />

11.5.2. Asignación <strong>de</strong> memoria para un array <strong>de</strong> <strong>estructura</strong>s .............................. 371<br />

11.6. Arrays dinámicos ........................<br />

11.7. Reglas <strong>de</strong> funcionami<strong>en</strong>to <strong>de</strong> la asignaci<br />

11.8. Resum<strong>en</strong> .......... ................................................ 376<br />

11.9. Ejercicios ........................ ................................. 376<br />

11.10. Problemas ..................................... ................ 377<br />

Capítulo 12. Cad<strong>en</strong>as .................. ....................................... 378<br />

12.1. Concepto<strong>de</strong>cad<strong>en</strong>a ............................. ................. 380<br />

12.1.1. Declaración <strong>de</strong> variables <strong>de</strong> cad<strong>en</strong>a ..............................<br />

12.1.2. Inicialización <strong>de</strong> variables <strong>de</strong> cad<strong>en</strong>a<br />

12.2.2. Función putchar ( ) .................................... 385<br />

12.2.3. Función puts ( ) .....................<br />

............................... 389<br />

......... 389<br />

12.5. Asignación <strong>de</strong> cad<strong>en</strong>as ............................................... 391<br />

12.5.1. La función s t .......... .............................. 391<br />

ad<strong>en</strong>as .......................... ....... 392<br />

12.6.2. Las funciones strcat ( )y strncat ( ) ........................... 393<br />

12.7. Comparación <strong>de</strong> cad<strong>en</strong>as ................................


c<br />

xi¡<br />

Cont<strong>en</strong>ido<br />

12.7.3. La función strncmp ( ) ............<br />

.................... 396<br />

12.7.4. La función strnicmp ( ) ........... .........................<br />

12.8. Inversión <strong>de</strong> cad<strong>en</strong>as ...........<br />

........................<br />

12.9.2. Función strlwr ( ) . .<br />

12.10. Conversión <strong>de</strong> cad<strong>en</strong>as a números ...................... ................. 399<br />

12.10.1. Función atoi () ................ ............................ 399<br />

12.10.2. Función atof ( ) . . ............................<br />

12.10.3. Función ato1 (1 .................................. .............. 400<br />

12.10.4. Entrada <strong>de</strong> números y cad<strong>en</strong>as ................<br />

12.11. Búsqueda <strong>de</strong> caracteres y cad<strong>en</strong>as ......<br />

...........................<br />

12.11.2. Función strrchr () .................... .................... 402<br />

12.11.3. Función strspn () ......<br />

12.11.4. Función strcspn () .................................... ....... 403<br />

12.11.5. Función strpbrk () ................... .................... 403<br />

12.11.6. Función strstr (1 ........<br />

12.11.7. Función strtok () ........................................<br />

12.12. Resum<strong>en</strong> ................................................ ............... 405<br />

12.13. Ejercicios .................................<br />

12.14. Problemas ...................... ...................................<br />

PARTE Ill.<br />

ESTRUCTURA DE DATOS<br />

Capítulo 13. Entradas y salidas por archivos .................... ...................... 410<br />

13.1. Flujos .......................... ................................. 412<br />

13.2. Puntero FILE ................................... ................. 412<br />

13.3. Apertura <strong>de</strong> un .................................. ................. 413<br />

13.3.1. Modos <strong>de</strong> apertura <strong>de</strong> un archivo ... .............................. 414<br />

13.3.2. NULL y EOF ............ ................................... . . 415<br />

13.3.3. Cierre <strong>de</strong> archivos .................................... .............. 415<br />

13.4. Creación <strong>de</strong> un archivo secu<strong>en</strong>cia1 ............. ........................ 416<br />

............. 417<br />

....................... 421<br />

......... 423<br />

13.5.2. Función <strong>de</strong> lectura f read ( ) ...................... ................... 424<br />

13.6. Funciones para acceso aleatorio ........... .......................... 426<br />

13.6.1. Función f seek ( ) . .<br />

13.6.2. Función ftell () ................................ .................. 431<br />

13.7. Datos externos al programa co<br />

13.8. Resum<strong>en</strong> ................ ................................... ........ 434<br />

................... 435<br />

13.10. Problemas ........................... ............................. 436<br />

Capítulo 14. Listas <strong>en</strong>lazadas ............................ ........... 438<br />

14.1. Fundam<strong>en</strong>tos teóricos ............................ ..................... 440<br />

14.2. Clasificación <strong>de</strong> las listas <strong>en</strong>lazadas ...... ............................... 441<br />

14.3. Operaciones <strong>en</strong> listas <strong>en</strong>lazadas ... ........... 442<br />

14.3.1. Declaración <strong>de</strong> un nodo ............................ .................... 442


Cont<strong>en</strong>ido<br />

xiii<br />

14.3.2. Puntero <strong>de</strong> cabecera y cola .............................. .......... 443<br />

14.3.3. El puntero nulo ......... ......................... 444<br />

14.3.4. El operador - > <strong>de</strong> selecció ......................... 445<br />

14.3.5. Construcción <strong>de</strong> una lista ................... ........................... 445<br />

14.3.6. Insertar un elem<strong>en</strong>to <strong>en</strong> una lista ..... ........................<br />

447<br />

14.3.7. Búsqueda <strong>de</strong> un elem<strong>en</strong>to . ............................ ........ 453<br />

14.3.8. Supresión <strong>de</strong> un nodo <strong>en</strong> una lista ............................................. 454<br />

14.4. Lista doblem<strong>en</strong>te <strong>en</strong>lazada ......................................................... 456<br />

14.4.1. Declaración <strong>de</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada ..............................<br />

14.4.2. Insertar un elem<strong>en</strong>to <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada .......................<br />

14.4.3. Supresión <strong>de</strong> un elem<strong>en</strong>to <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada ............ ......... 459<br />

14.5. Listas circulares ...... .................................. .................. 462<br />

14.5.1. Insertar un elem <strong>en</strong> una lista circular ......... ....................... 462<br />

14.5.2. Supresión <strong>de</strong> un elem<strong>en</strong>to <strong>en</strong> una lista circular . . ........................... 463<br />

14.6. Resum<strong>en</strong> ............................... .......................<br />

467<br />

14.7. Ejercicios ..................... ............................... ....... 468<br />

14.8. Problemas ............. .................................................. 468<br />

Capítulo 15. Pilas y colas ................. ................................. .......<br />

15.1. Concepto <strong>de</strong> pila ........... ......................... ...............<br />

15.1.1. Especificaciones <strong>de</strong> una ........................<br />

15.2. El tipo pila implem<strong>en</strong>tado con arrays .....<br />

........................<br />

15.2.1. Especificación <strong>de</strong>l tipo pi 1 a .....<br />

........................<br />

15.2.2. Implem<strong>en</strong>tación <strong>de</strong> las operaciones sobre pilas ..........................<br />

15.2.3. Operaciones <strong>de</strong> verificación <strong>de</strong>l estado <strong>de</strong> la pila .....................<br />

15.3. Colas ..........................................................................<br />

15.4. El tipo cola implem<strong>en</strong>tada con arrays .................. .........................<br />

15.4.1. Definición <strong>de</strong> la especificación <strong>de</strong> una cola .......................<br />

15.4.2. Especificación <strong>de</strong>l tipo cola .... ................................<br />

15.4.3. Implem<strong>en</strong>tación <strong>de</strong>l tipo cola ................. .... .................<br />

15.4.4. Operaciones <strong>de</strong> la cola ........................ .........................<br />

15.5. Realización <strong>de</strong> una cola con una lista <strong>en</strong>lazada .......... .........................<br />

15.5.1. Declaración <strong>de</strong>l tipo cola con listas .... ....................................<br />

15.5.2. Codificación <strong>de</strong> 1 eraciones <strong>de</strong>l tipo c o 1 a con listas ..............<br />

15.6. Resum<strong>en</strong> ............ .......................................... ........<br />

15.7. Ejercicios ............ .......................................................<br />

15.8. Problemas ........................................ ...........................<br />

470<br />

472<br />

473<br />

473 I<br />

475 II<br />

477 'I<br />

478<br />

48 1<br />

483<br />

483<br />

483<br />

1 I<br />

484 I<br />

486<br />

487<br />

488<br />

489<br />

492<br />

493<br />

494 I<br />

Capítulo 16. Árboles .....................<br />

~<br />

16.1. Árboles g<strong>en</strong>erales ........ ....................... I<br />

.............................. 504<br />

16.3.1. Equilibrio ............. ......................<br />

16.3.2. Árboles binarios completos<br />

16.4. Estructura <strong>de</strong> un árbol binario ................................ .................. 511<br />

16.4.1. Difer<strong>en</strong>tes ti [I<br />

...........................<br />

e expresión ........<br />

I<br />

I<br />

.....................<br />

I<br />

16.7.2. Recomdo <strong>en</strong>ord<strong>en</strong> ...... ......................<br />

...... 521<br />

16.7.3. Recomdo postord<strong>en</strong> ..... .......................... ........ 522<br />

................... 525


xiv<br />

Cont<strong>en</strong>ido<br />

...................... 528<br />

16.9.1. Búsqueda .... .............................. ............ 528<br />

16.9.2. Insertar un nodo ....<br />

............................ 531<br />

16.9.5. Recorridos <strong>de</strong> un árbol .................. ............................ 535<br />

.................. 535<br />

................ 536<br />

..................................<br />

16.13. Problemas ..... ................................ ................. 540<br />

....... 542<br />

............................ 545<br />

....................... 575<br />

Apéndice C. Palabras reservadas <strong>de</strong> C++ .................................<br />

Apéndice D. Guía <strong>de</strong> sintaxis ANSIASO estándar C++ ..............................<br />

Apéndice E. Biblioteca <strong>de</strong> funciones ANSI C ....... ..................................<br />

Apéndice F. Recursos (Libros/Revistas/URL <strong>de</strong> Interne ............................ 713<br />

ÍNDICE ......................... .............................. ........ 727


PRÓLOGO<br />

INTRODUCCIÓN<br />

i Por qué un libro <strong>de</strong> C al principio <strong>de</strong>l siglo XXI? A pesar <strong>de</strong> haber cumplido ya sus bodas <strong>de</strong> plata<br />

(25 años <strong>de</strong> vida), C viaja con toda salud hacia los 30 años <strong>de</strong> edad que cumplirá el próximo año. Sigue<br />

si<strong>en</strong>do una <strong>de</strong> las mejores opciones para la programación <strong>de</strong> los sistemas actuales y el medio más efici<strong>en</strong>te<br />

<strong>de</strong> apr<strong>en</strong>dizaje para emigrar a los l<strong>en</strong>guajes reina, por excel<strong>en</strong>cia, <strong>en</strong> el mundo ori<strong>en</strong>tado a objetos<br />

y compon<strong>en</strong>tes y el mundo Web (C++, Java,. . .) que dominan el campo informático y <strong>de</strong> la computación.<br />

i Cuáles son las características que hac<strong>en</strong> tan popular a este l<strong>en</strong>guaje <strong>de</strong> programación e idóneo<br />

como primer l<strong>en</strong>guaje <strong>de</strong> programación <strong>en</strong> las carreras profesionales <strong>de</strong> programador (<strong>de</strong> aplicaciones<br />

y <strong>de</strong> sistemas) y <strong>de</strong>l ing<strong>en</strong>iero <strong>de</strong> software? Po<strong>de</strong>mos citar algunas muy sobresali<strong>en</strong>tes:<br />

Es muy portable (transportable <strong>en</strong>tre un gran número <strong>de</strong> plataformas hardware y plataformas sofware,<br />

sistemas operativos). Exist<strong>en</strong> numerosos compiladores para todo tipo <strong>de</strong> plataformas sobre<br />

los que corrr<strong>en</strong> los mismos programas fu<strong>en</strong>tes o con ligeras modificaciones.<br />

Es versátil y <strong>de</strong> bajo nivel, por lo que es idóneo para tareas relativas a la programación <strong>de</strong>l sistema.<br />

A pesar <strong>de</strong> ser un excel<strong>en</strong>te l<strong>en</strong>guaje para programación <strong>de</strong> sistemas, es también un efici<strong>en</strong>te y<br />

pot<strong>en</strong>te l<strong>en</strong>guaje para aplicaciones <strong>de</strong> propósito g<strong>en</strong>eral.<br />

Es un l<strong>en</strong>guaje pequeño, por lo que es relativam<strong>en</strong>te fácil construir compiladores <strong>de</strong> C y a<strong>de</strong>más<br />

es también fácil <strong>de</strong> apr<strong>en</strong><strong>de</strong>r.<br />

Todos los compiladores suel<strong>en</strong> incluir pot<strong>en</strong>tes y excel<strong>en</strong>tes bibliotecas <strong>de</strong> funciones compatibles<br />

con el estándar ANSI. Los difer<strong>en</strong>tes fabricantes suel<strong>en</strong> añadir a sus compiladores funcionalida<strong>de</strong>s<br />

diversas que aum<strong>en</strong>tan la efici<strong>en</strong>cia y pot<strong>en</strong>cia <strong>de</strong> los mismos y constituye una notable v<strong>en</strong>taja<br />

respecto a otros l<strong>en</strong>guajes.<br />

El l<strong>en</strong>guaje pres<strong>en</strong>ta una interjGaz excel<strong>en</strong>te para los sistemas operativos Unix y Windows, junto<br />

con el ya acreditado Linux.<br />

Es un l<strong>en</strong>guaje muy utilizado para la construcción <strong>de</strong>: sistemas operativos, <strong>en</strong>sambladores, programas<br />

<strong>de</strong> comunicaciones, intérpretes <strong>de</strong> l<strong>en</strong>guajes, compiladores <strong>de</strong> l<strong>en</strong>guajes, editores <strong>de</strong> textos,<br />

bases <strong>de</strong> <strong>datos</strong>, utilida<strong>de</strong>s, controladores <strong>de</strong> red, etc.<br />

Por todas estas razones y nuestra experi<strong>en</strong>cia doc<strong>en</strong>te, <strong>de</strong>cidimos escribir esta obra que, por otra parte,<br />

pudiera completar nuestras otras obras <strong>de</strong> programación escritas para C++, Java, Turbo Pascal y<br />

Visual Basic. Basados <strong>en</strong> estas premisas este libro se ha escrito p<strong>en</strong>sando <strong>en</strong> que pudiera servir <strong>de</strong><br />

xv


xvi<br />

prólogo<br />

refer<strong>en</strong>cia y guía <strong>de</strong> estudio para un primer curso <strong>de</strong> introducción a la programación, con una segunda<br />

parte que, a su vez, sirviera como continuación, y <strong>de</strong> introducción a las <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> todo ello<br />

utilizando C, y <strong>en</strong> particular la versión estándar ANSI C, como l<strong>en</strong>guaje <strong>de</strong> programación. El objetivo<br />

final que busca es, no sólo <strong>de</strong>scribir la sintaxis <strong>de</strong> C, sino y, sobre todo, mostrar las características más<br />

sobresali<strong>en</strong>tes <strong>de</strong>l l<strong>en</strong>guaje, a la vez que se <strong>en</strong>señan técnicas <strong>de</strong> programación <strong>estructura</strong>da. Así pues, los<br />

objetivos fundam<strong>en</strong>tales <strong>de</strong>l libro son:<br />

Énfasis fuerte <strong>en</strong> el análisis, construcción y diseño <strong>de</strong> programas.<br />

Un medio <strong>de</strong> resolución <strong>de</strong> problemas mediante técnicas <strong>de</strong> programación.<br />

Una introducción a la informática y a las ci<strong>en</strong>cias <strong>de</strong> la computación usando una herrami<strong>en</strong>ta <strong>de</strong><br />

programación d<strong>en</strong>ominada C (ANSI C).<br />

Enseñanza <strong>de</strong> las reglas <strong>de</strong> sintaxis más frecu<strong>en</strong>tes y efici<strong>en</strong>tes <strong>de</strong>l l<strong>en</strong>guaje C.<br />

En resum<strong>en</strong>, éste es un libro diseñado para <strong>en</strong>señar a programar utilizando C, no un libro diseñado<br />

para <strong>en</strong>señar C, aunque también pret<strong>en</strong><strong>de</strong> conseguirlo. No obstante, confiamos que los estudiantes que<br />

utilic<strong>en</strong> este libro se conviertan <strong>de</strong> un modo razonable <strong>en</strong> acérrimos seguidores y a<strong>de</strong>ptos <strong>de</strong> C, al igual<br />

que nos ocurre a casi todos los programadores que com<strong>en</strong>zamos a trabajar con este l<strong>en</strong>guaje. Así se tratará<br />

<strong>de</strong> <strong>en</strong>señar las técnicas clásicas y avanzadas <strong>de</strong> programación <strong>estructura</strong>da.<br />

LA EVOLUCI~N DE c: c++<br />

C es un l<strong>en</strong>guaje <strong>de</strong> programación <strong>de</strong> propósito g<strong>en</strong>eral que ha estado y sigue estando asociado con el<br />

sistema operativo UNIX. El adv<strong>en</strong>imi<strong>en</strong>to <strong>de</strong> nuevos sistemas operativos como Windows (95,98, NT,<br />

2000 o el reci<strong>en</strong>tem<strong>en</strong>te anunciado XP sobre la plataforma. NET) o el ya muy popular Linux, la versión<br />

abierta, gratuita <strong>de</strong> Unix que junto con el <strong>en</strong>torno Gnome está com<strong>en</strong>zando a revolucionar el mundo <strong>de</strong><br />

la programación. Esta revolución, paradójicam<strong>en</strong>te, proporciona fuerza al l<strong>en</strong>guaje <strong>de</strong> programación <strong>de</strong><br />

sistemas C. Todavía y durante muchos años C seguirá si<strong>en</strong>do uno <strong>de</strong> los l<strong>en</strong>guajes li<strong>de</strong>res <strong>en</strong> la <strong>en</strong>señanza<br />

<strong>de</strong> la programación tanto a nivel profesional como universitario. Como reconoc<strong>en</strong> sus autores<br />

Kernighan y Ritchie, <strong>en</strong> El L<strong>en</strong>guaje <strong>de</strong> Programación C, 2.” edición, C, aunque es un l<strong>en</strong>guaje idóneo<br />

para escribir compiladores y sistemas operativos, sigue si<strong>en</strong>do, sobre todo, un l<strong>en</strong>guaje para escribir<br />

aplicaciones <strong>en</strong> numerosas disciplinas. Ésta es la razón por la que a algo más <strong>de</strong> un año para cumplir los<br />

30 años <strong>de</strong> vida, C sigue si<strong>en</strong>do el l<strong>en</strong>guaje más empleado <strong>en</strong> Faculta<strong>de</strong>s y Escuelas <strong>de</strong> Ci<strong>en</strong>cias e Ing<strong>en</strong>iería,<br />

y <strong>en</strong> los c<strong>en</strong>tros <strong>de</strong> <strong>en</strong>señanza <strong>de</strong> formación profesional, y <strong>en</strong> particular los innovadores ciclos<br />

<strong>de</strong> grado superior, así como <strong>en</strong> c<strong>en</strong>tros <strong>de</strong> <strong>en</strong>señanza media y secundaria, para el apr<strong>en</strong>dizaje <strong>de</strong> legiones<br />

<strong>de</strong> promociones (g<strong>en</strong>eraciones) <strong>de</strong> estudiantes y profesionales.<br />

Las i<strong>de</strong>as fundam<strong>en</strong>tales <strong>de</strong> C provi<strong>en</strong><strong>en</strong> <strong>de</strong>l l<strong>en</strong>guaje BCPL, <strong>de</strong>sarrollado por Martin Richards. La<br />

influ<strong>en</strong>cia <strong>de</strong> BCPL sobre C continuó, indirectam<strong>en</strong>te, a través <strong>de</strong>l l<strong>en</strong>guaje B, escrito por K<strong>en</strong> Thompson<br />

<strong>en</strong> 1979 para escribir el primer sistema UNIX <strong>de</strong> la computadora DEC <strong>de</strong> Digital PDP-7. BCPL y<br />

B son l<strong>en</strong>guajes «sin tipos» <strong>en</strong> contraste con C que posee una variedad <strong>de</strong> tipos <strong>de</strong> <strong>datos</strong>.<br />

En 1975 se publica Pascal User Manual and Report la especificación <strong>de</strong>l jov<strong>en</strong> l<strong>en</strong>guaje Pascal<br />

(Wirth, J<strong>en</strong>s<strong>en</strong> 75) cuya suerte corre <strong>en</strong> paralelo con C, aunque al contrario que el compilador <strong>de</strong> Pascal<br />

construido por la casa Borland, que prácticam<strong>en</strong>te no se comercializa, C sigue si<strong>en</strong>do uno <strong>de</strong> los<br />

reyes <strong>de</strong> la iniciación a la programación. En I978 se publicó la primera edición <strong>de</strong> la obra The C Programming<br />

Language <strong>de</strong> Kernighan y Ritchie, conocido por K&R.<br />

En 1983 el American National Standards Institute (ANSI) nombró un comité para conseguir una <strong>de</strong>finición<br />

estándar <strong>de</strong> C. La <strong>de</strong>finición resultante se llamó ANSI C, que se pres<strong>en</strong>tó a finales <strong>de</strong> 1988 y se<br />

aprobó <strong>de</strong>finitivam<strong>en</strong>te por ANSI <strong>en</strong> 1989 y <strong>en</strong> 1990 se aprobó por ISO. La segunda edición The C<br />

Programming Language se consi<strong>de</strong>ra también el manual <strong>de</strong>l estándar ANSI C. Por esta razón la especificación<br />

estándar se suele conocer como ANSVISO C. Los compiladores mo<strong>de</strong>rnos soportan todas<br />

las características <strong>de</strong>finidas <strong>en</strong> ese estándar.


prólogo<br />

xvii<br />

Convivi<strong>en</strong>do con C se <strong>en</strong>cu<strong>en</strong>tra el l<strong>en</strong>guaje C++, una evolución lógica suya, y que es tal el estado<br />

<strong>de</strong> simbiosis y sinergia exist<strong>en</strong>te <strong>en</strong>tre ambos l<strong>en</strong>guajes que <strong>en</strong> muchas ocasiones se habla <strong>de</strong> C/C++<br />

para <strong>de</strong>finir a los compiladores que sigu<strong>en</strong> estas normas, dado que C++ se consi<strong>de</strong>ra un superconjunto<br />

<strong>de</strong> C.<br />

C++ ti<strong>en</strong>e sus oríg<strong>en</strong>es <strong>en</strong> C, y, sin lugar a dudas, Kemighan y Ritchie -inv<strong>en</strong>tores <strong>de</strong> C,- son<br />

«padres espirituales» <strong>de</strong> C++. Así lo manifiesta Bjarne Stroustrup -inv<strong>en</strong>tor <strong>de</strong> C++- <strong>en</strong> el prólogo<br />

<strong>de</strong> su afamada obra The C++ Programming Lunguage. C se ha conservado así como un subconjunto <strong>de</strong><br />

C++ y es, a su vez, ext<strong>en</strong>sión directa <strong>de</strong> su pre<strong>de</strong>cesor BCPL <strong>de</strong> Richards. Pero C++, tuvo muchas más<br />

fu<strong>en</strong>tes <strong>de</strong> inspiración; a<strong>de</strong>más <strong>de</strong> los autores antes citados, cabe <strong>de</strong>stacar <strong>de</strong> modo especial, Simula 67<br />

<strong>de</strong> Dah1 que fue su principal inspirador; el concepto <strong>de</strong> clase, clase <strong>de</strong>rivada yfunciones virtuales se<br />

tomaron <strong>de</strong> Simula; otra fu<strong>en</strong>te importante <strong>de</strong> refer<strong>en</strong>cia fue Algol 68 <strong>de</strong>l que se adoptó el concepto <strong>de</strong><br />

sobrecarga <strong>de</strong> operadores y la libertad <strong>de</strong> situar una <strong>de</strong>claración <strong>en</strong> cualquier lugar <strong>en</strong> el que pueda<br />

aparecer una s<strong>en</strong>t<strong>en</strong>cia. Otras aportaciones importantes <strong>de</strong> C++ como son las plantillas (templates) y la<br />

g<strong>en</strong>ericidad (tipos g<strong>en</strong>éricos) se tomaron <strong>de</strong> Ada, Clu y ML.<br />

C++ se com<strong>en</strong>zó a utilizar como un «C con clases» y fue a principios <strong>de</strong> los och<strong>en</strong>ta cuando com<strong>en</strong>zó<br />

la revolución C++, aunque su primer uso comercial, fuera <strong>de</strong> una organización <strong>de</strong> investigación,<br />

com<strong>en</strong>zó <strong>en</strong> julio <strong>de</strong> 1983. Como Stroustrup cu<strong>en</strong>ta <strong>en</strong> el prólogo <strong>de</strong> la 3." edición <strong>de</strong> su citada obra, C++<br />

nació con la i<strong>de</strong>a <strong>de</strong> que el autor y sus colegas no tuvieran que programar <strong>en</strong> <strong>en</strong>samblador ni <strong>en</strong> otros<br />

l<strong>en</strong>guajes al uso (léase Pascal, BASIC, FORTRAN,...). La explosión <strong>de</strong>l l<strong>en</strong>guaje <strong>en</strong> la comunidad informática<br />

hizo inevitable la estandarización. proceso que com<strong>en</strong>zó <strong>en</strong> 1987 [Stroustrup 941. Así nació una<br />

primera fu<strong>en</strong>te <strong>de</strong> estandarización The Annotated C++ Refer<strong>en</strong>ce Manual [Ellis 891'. En diciembre <strong>de</strong><br />

1989 se reunió el comité X3J16 <strong>de</strong> ANSI, bajo el auspicio <strong>de</strong> Hewlett-Packard y <strong>en</strong> junio <strong>de</strong> 1991 pasó<br />

el primer esfuerzo <strong>de</strong> estandarización internacional <strong>de</strong> la mano <strong>de</strong> ISO, y así com<strong>en</strong>zó a nacer el estándar<br />

ANSVISO C++. En 1995 se publicó un borrador estándar para su exam<strong>en</strong> público y <strong>en</strong> noviembre<br />

<strong>de</strong> 1997 fue finalm<strong>en</strong>te aprobado el estandar C++ internacional, aunque ha sido <strong>en</strong> 1998 cuando el proceso<br />

se ha podido dar por terminado (ANSIASO C++ Draft Standard).<br />

El libro <strong>de</strong>finitivo y refer<strong>en</strong>cia obligada para conocer y dominar C++ es la 3.a edición <strong>de</strong> la obra <strong>de</strong><br />

Stroustrup [Stroustrup 971 y actualizada <strong>en</strong> la Special Edition [Stroustrup 2000]*.<br />

i<br />

r<br />

OBJETIVOS DEL LIBRO<br />

C++ es un superconjunto <strong>de</strong> C y su mejor ext<strong>en</strong>sión. Éste es un tópico conocido por toda la comunidad<br />

<strong>de</strong> programadores <strong>de</strong>l mundo. Cabe preguntarse como hac<strong>en</strong> muchos autores, profesores, alumnos y<br />

profesionales ¿se <strong>de</strong>be apr<strong>en</strong><strong>de</strong>r primero C y luego C++? Stroustrup y una gran mayoría <strong>de</strong> programadores,<br />

contestan así: «No sólo es innecesario apr<strong>en</strong><strong>de</strong>rprimero C, sino que a<strong>de</strong>más es una mala i<strong>de</strong>a».<br />

Nosotros no somos tan radicales y p<strong>en</strong>samos que se pue<strong>de</strong> llegar a C++ procedi<strong>en</strong>do <strong>de</strong> ambos caminos,<br />

aunque es lógico la consi<strong>de</strong>ración citada anteriorm<strong>en</strong>te, ya que efectivam<strong>en</strong>te los hábitos <strong>de</strong> programación<br />

<strong>estructura</strong>da <strong>de</strong> C pued<strong>en</strong> retrasar la adquisición <strong>de</strong> los conceptos clave <strong>de</strong> C++, pero también es<br />

cierto que <strong>en</strong> muchos casos ayuda consi<strong>de</strong>rablem<strong>en</strong>te <strong>en</strong> el apr<strong>en</strong>dizaje.<br />

Este libro supone que el lector no es programador <strong>de</strong> C, ni <strong>de</strong> ningún otro l<strong>en</strong>guaje, aunque también<br />

somos consci<strong>en</strong>tes que el lector que haya seguido un primer curso <strong>de</strong> programación <strong>en</strong> <strong>algoritmos</strong> o <strong>en</strong><br />

algún l<strong>en</strong>guaje <strong>estructura</strong>do, llámese Pascal o cualquier otro, éste le ayudará favorablem<strong>en</strong>te al correcto<br />

y rápido apr<strong>en</strong>dizaje <strong>de</strong> la programación <strong>en</strong> C y obt<strong>en</strong>drá el máximo r<strong>en</strong>dimi<strong>en</strong>to <strong>de</strong> esta obra. Sin<br />

embargo, si ya conoce C++, naturalm<strong>en</strong>te no t<strong>en</strong>drá ningún problema, <strong>en</strong> su apr<strong>en</strong>dizaje, muy al contrario,<br />

bastará que lea con <strong>de</strong>talle las difer<strong>en</strong>cias es<strong>en</strong>ciales <strong>de</strong> los apéndices C y D <strong>de</strong> modo que irá<br />

' Traducida al español por el autor <strong>de</strong> este libro junto con el profesor Miguel Katnb, <strong>de</strong> la Universidad <strong>de</strong> la Habana [Ellis 941<br />

Esta obra qe <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> proceso <strong>de</strong> traducción al español por un equipo <strong>de</strong> profesores <strong>de</strong> vanas universida<strong>de</strong>s españolas coordinadas<br />

por el autor <strong>de</strong> esta obra


xviii<br />

Prólogo<br />

integrando gradualm<strong>en</strong>te los nuevos conceptos que irá <strong>en</strong>contrando a medida que avance <strong>en</strong> la obra con<br />

los conceptos clásicos <strong>de</strong> C++. El libro pret<strong>en</strong><strong>de</strong> <strong>en</strong>señar a programar utilizando dos conceptos fundam<strong>en</strong>tale<br />

s :<br />

1. Algoritmos (conjunto <strong>de</strong> instrucciones programadas para resolver una tarea específica).<br />

2. Datos (una colección <strong>de</strong> <strong>datos</strong> que se proporcionan a los <strong>algoritmos</strong> que se han <strong>de</strong> ejecutar para<br />

<strong>en</strong>contrar una solución: los <strong>datos</strong> se organizarán <strong>en</strong> <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong>).<br />

Los dos primeros aspectos, <strong>algoritmos</strong> y <strong>datos</strong>, han permanecido invariables a lo largo <strong>de</strong> la corta historia<br />

<strong>de</strong> la informáticdcomputación, pero la interrelación <strong>en</strong>tre ellos sí que ha variado y continuará<br />

haciéndolo. Esta interrelación se conoce como paradigma <strong>de</strong> programación.<br />

En el paradigma <strong>de</strong> programación procedim<strong>en</strong>tal @rocedural o por procedimi<strong>en</strong>tos) un problema se<br />

mo<strong>de</strong>la directam<strong>en</strong>te mediante un conjunto <strong>de</strong> <strong>algoritmos</strong>. Un problema cualquiera, la nómina <strong>de</strong> una<br />

empresa o la gestión <strong>de</strong> v<strong>en</strong>tas <strong>de</strong> un almacén, se repres<strong>en</strong>tan como una serie <strong>de</strong> procedimi<strong>en</strong>tos que<br />

manipulan <strong>datos</strong>. Los <strong>datos</strong> se almac<strong>en</strong>an separadam<strong>en</strong>te y se acce<strong>de</strong> a ellos o bi<strong>en</strong> mediante una posición<br />

global o mediante parámetros <strong>en</strong> los procedimi<strong>en</strong>tos. Tres l<strong>en</strong>guajes <strong>de</strong> programación clásicos,<br />

FORTRAN, Pascal y C, han repres<strong>en</strong>tado el arquetipo <strong>de</strong> la programación procedim<strong>en</strong>tal, también relacionada<br />

estrecham<strong>en</strong>te y -a veces- conocida como programación <strong>estructura</strong>da. La programación<br />

con soporte <strong>en</strong> C++, proporciona el paradigma procedim<strong>en</strong>tal con un énfasis <strong>en</strong> funciones, plantillas <strong>de</strong><br />

funciones y <strong>algoritmos</strong> g<strong>en</strong>éricos.<br />

En la década <strong>de</strong> los set<strong>en</strong>ta, el <strong>en</strong>foque <strong>de</strong>l diseño <strong>de</strong> programas se <strong>de</strong>splazó <strong>de</strong>s<strong>de</strong> el paradigma procedim<strong>en</strong>tal<br />

al ori<strong>en</strong>tado a objetos apoyado <strong>en</strong> los tipos abstractos <strong>de</strong> <strong>datos</strong> (TAD). En este paradigma un<br />

problema mo<strong>de</strong>la un conjunto <strong>de</strong> abstracciones <strong>de</strong> <strong>datos</strong>. En C++ estas abstracciones se conoc<strong>en</strong> como<br />

clases. Las clases conti<strong>en</strong><strong>en</strong> un conjunto <strong>de</strong> instancias o ejemplares <strong>de</strong> la misma que se d<strong>en</strong>ominan objetos,<br />

<strong>de</strong> modo que un programa actúa como un conjunto <strong>de</strong> objetos que se relacionan <strong>en</strong>tre sí. La gran<br />

difer<strong>en</strong>cia <strong>en</strong>tre ambos paradigmas resi<strong>de</strong> <strong>en</strong> el hecho <strong>de</strong> que los <strong>algoritmos</strong> asociados con cada clase se<br />

conoc<strong>en</strong> como interfaz pública <strong>de</strong> la clase y los <strong>datos</strong> se almac<strong>en</strong>an privadam<strong>en</strong>te d<strong>en</strong>tro <strong>de</strong> cada objeto<br />

<strong>de</strong> modo que el acceso a los <strong>datos</strong> está oculto al programa g<strong>en</strong>eral y se gestionan a través <strong>de</strong> la interfaz.<br />

Así pues, <strong>en</strong> resum<strong>en</strong>, los objetivos fundam<strong>en</strong>tales <strong>de</strong> esta obra son: introducción a la programación<br />

<strong>estructura</strong>da y <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> con el l<strong>en</strong>guaje estándar C <strong>de</strong> ANSVISO; otros objetivo complem<strong>en</strong>tario<br />

es preparar al lector para su emigración a C++, para lo cual se han escrito dos apéndices completos<br />

C y D que pres<strong>en</strong>tan una amplia refer<strong>en</strong>cia <strong>de</strong> palabras reservadas y una guía <strong>de</strong> sintaxis <strong>de</strong> C++<br />

con el objeto <strong>de</strong> que el lector pueda convertir programas escritos <strong>en</strong> C a C++ (con la excepción <strong>de</strong> las<br />

propieda<strong>de</strong>s <strong>de</strong> ori<strong>en</strong>tación a objetos que se sal<strong>en</strong> fuera <strong>de</strong>l ámbito <strong>de</strong> esta obra).<br />

EL LIBRO COMO HERRAMIENTA DOCENTE<br />

La experi<strong>en</strong>cia <strong>de</strong> los autores <strong>de</strong>s<strong>de</strong> hace muchos años con obras muy implantadas <strong>en</strong> el mundo universitario<br />

como Programación <strong>en</strong> C++, Programación <strong>en</strong> Turbo Pascal (<strong>en</strong> su 3." edición), <strong>estructura</strong><br />

<strong>de</strong> <strong>datos</strong>, Fundam<strong>en</strong>tos <strong>de</strong> programación (<strong>en</strong> su 2." edición y <strong>en</strong> preparación la 3." edición) y Programación<br />

<strong>en</strong> BASIC (que alcanzó tres ediciones y numerosísimas reimpresiones <strong>en</strong> la década <strong>de</strong> los och<strong>en</strong>ta),<br />

nos ha llevado a mant<strong>en</strong>er la <strong>estructura</strong> <strong>de</strong> estas obras, actualizándola a los cont<strong>en</strong>idos que se prevén<br />

para los estudiantes <strong>de</strong>l futuro siglo XXI. Por ello <strong>en</strong> el cont<strong>en</strong>ido <strong>de</strong> la obra hemos t<strong>en</strong>ido <strong>en</strong> cu<strong>en</strong>ta<br />

no sólo las directrices <strong>de</strong> los planes <strong>de</strong> estudio españoles <strong>de</strong> ing<strong>en</strong>iería informática e ing<strong>en</strong>iería técnica<br />

informática (antiguas lic<strong>en</strong>ciaturas y diplomaturas <strong>en</strong> informática) y lic<strong>en</strong>ciaturas <strong>en</strong> ci<strong>en</strong>cias <strong>de</strong> la computación,<br />

sino también <strong>de</strong> ing<strong>en</strong>ierías tales como industriales, telecomunicaciones, agrónomos o minas,<br />

o las más reci<strong>en</strong>tes incorporadas, <strong>en</strong> España, como ing<strong>en</strong>iería <strong>en</strong> geo<strong>de</strong>sia. Asímismo, <strong>en</strong> el diseño <strong>de</strong><br />

la obra se han t<strong>en</strong>ido <strong>en</strong> cu<strong>en</strong>ta las directrices oficiales vig<strong>en</strong>tes <strong>en</strong> España para la Formación Profesional<br />

<strong>de</strong> Grado Superior; por ello se ha tratado <strong>de</strong> que el cont<strong>en</strong>ido <strong>de</strong> la obra contemple los programas<br />

propuestos para el ciclo <strong>de</strong> <strong>de</strong>sarrollo <strong>de</strong> Aplicaciones Informáticas <strong>en</strong> el módulo <strong>de</strong> Programación<br />

<strong>en</strong> L<strong>en</strong>guaje Estructurado; también se ha tratado <strong>en</strong> la medida <strong>de</strong> lo posible <strong>de</strong> que pueda servir <strong>de</strong>


Prólogo<br />

xix<br />

refer<strong>en</strong>cia al ciclo <strong>de</strong> Administración <strong>de</strong> Sistemas Informúticos <strong>en</strong> el módulo <strong>de</strong> Fundam<strong>en</strong>tos <strong>de</strong> Programación.<br />

Nuestro conocimi<strong>en</strong>to <strong>de</strong>l mundo educativo latinoamericano nos ha llevado a p<strong>en</strong>sar también <strong>en</strong> las<br />

carreras <strong>de</strong> ing<strong>en</strong>iería <strong>de</strong> sistemas computacionales y las lic<strong>en</strong>ciaturas <strong>en</strong> informática y <strong>en</strong> sistemas <strong>de</strong><br />

información, carreras hermanas <strong>de</strong> las citadas anteriorm<strong>en</strong>te.<br />

Por todo lo anterior, el cont<strong>en</strong>ido <strong>de</strong>l libro int<strong>en</strong>ta seguir un programa estándar <strong>de</strong> un primer curso<br />

<strong>de</strong> introducción a la programación y, según situaciones, un segundo curso <strong>de</strong> programación <strong>de</strong> nivel<br />

medio <strong>en</strong> asignaturas tales como Metodología <strong>de</strong> la Programación, Fundam<strong>en</strong>tos <strong>de</strong> Programación,<br />

Introducción a la Programación, ... Asimismo, se ha buscado seguir las directrices emanadas <strong>de</strong> la<br />

ACM-IEEE para los cursos CS 1 y CS8 <strong>en</strong> los planes recom<strong>en</strong>dados <strong>en</strong> los Computing Curricula <strong>de</strong><br />

1991 y las recom<strong>en</strong>daciones <strong>de</strong> los actuales Computing Curricula 2001 <strong>en</strong> las áreas <strong>de</strong> conocimi<strong>en</strong>to<br />

Programming Fundam<strong>en</strong>tals [PF,10] y Programming Languages [PL, 1 11, así como las vig<strong>en</strong>tes <strong>en</strong> universida<strong>de</strong>s<br />

latinoamericanas que conocemos, y con las que t<strong>en</strong>emos relaciones profesionales.<br />

El cont<strong>en</strong>ido <strong>de</strong>l libro abarca los citados programas y comi<strong>en</strong>za con la introducción a los <strong>algoritmos</strong><br />

y a laprogramación, para llegar a <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong>. Por esta circunstancia la <strong>estructura</strong> <strong>de</strong>l curso no<br />

ha <strong>de</strong> ser secu<strong>en</strong>cia1 <strong>en</strong> su totalidad sino que el profesor/maestro y el alumno/lector podrán estudiar sus<br />

materias <strong>en</strong> el ord<strong>en</strong> que consi<strong>de</strong>r<strong>en</strong> más oportuno. Ésta es la razón principal por la cual el libro se ha<br />

organizado <strong>en</strong> tres partes y <strong>en</strong> seis apéndices.<br />

Se trata <strong>de</strong> <strong>de</strong>scribir el paradigma más popular <strong>en</strong> el mundo <strong>de</strong> la programación: el procedim<strong>en</strong>tal y preparar<br />

al lector para su inmersión <strong>en</strong> el ya implantado paradigma ori<strong>en</strong>tado a objetos. Los cursos <strong>de</strong> programación<br />

<strong>en</strong> sus niveles inicial y medio están evolucionando para aprovechar las v<strong>en</strong>tajas <strong>de</strong> nuevas y<br />

futuras t<strong>en</strong>d<strong>en</strong>cias <strong>en</strong> ing<strong>en</strong>iería <strong>de</strong> software y <strong>en</strong> diseño <strong>de</strong> l<strong>en</strong>guajes <strong>de</strong> programación, específicam<strong>en</strong>te<br />

diseño y programación ori<strong>en</strong>tada a objetos. Algunas faculta<strong>de</strong>s y escuelas <strong>de</strong> ing<strong>en</strong>ieros, junto con la nueva<br />

formación profesional (ciclos formativos <strong>de</strong> nivel superior) <strong>en</strong> España y <strong>en</strong> Latinoamérica, están introduci<strong>en</strong>do<br />

a sus alumnos <strong>en</strong> la programación ori<strong>en</strong>tada a objetos, inmediatam<strong>en</strong>te <strong>de</strong>spués <strong>de</strong>l conocimi<strong>en</strong>to<br />

<strong>de</strong> la programación <strong>estructura</strong>da, e incluso +n ocasiones antes-. Por esta razón, una metodología que<br />

se podría seguir sería impartir un curso <strong>de</strong>findam<strong>en</strong>tos <strong>de</strong> programación seguido <strong>de</strong> <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong><br />

y luego seguir con un segundo nivel <strong>de</strong> programación avanzada que constituy<strong>en</strong> las tres partes <strong>de</strong>l libro.<br />

P<strong>en</strong>sando <strong>en</strong> aquellos alumnos que <strong>de</strong>se<strong>en</strong> continuar su formación estudiando C++ se han escrito los apéndices<br />

C y D, que les permita adaptarse fácilm<strong>en</strong>te a las particularida<strong>de</strong>s básicas <strong>de</strong> C++ y po<strong>de</strong>r continuar<br />

sin esfuerzo la parte primera y avanzar con mayor rapi<strong>de</strong>z a las sigui<strong>en</strong>tes partes <strong>de</strong>l libro.<br />

CARACTER~STICAS IMPORTANTES DEL LIBRO<br />

Programación <strong>en</strong> C, utiliza los sigui<strong>en</strong>tes elem<strong>en</strong>tos clave para conseguir obt<strong>en</strong>er el mayor r<strong>en</strong>dimi<strong>en</strong>to<br />

<strong>de</strong>l material incluido <strong>en</strong> sus difer<strong>en</strong>tes capítulos:<br />

Cont<strong>en</strong>ido. Enumera los apartados <strong>de</strong>scritos <strong>en</strong> el capítulo.<br />

Introducción. Abre el capítulo con una breve revisión <strong>de</strong> los puntos y objetivos más importantes que<br />

se tratarán y todo aquello que se pue<strong>de</strong> esperar <strong>de</strong>l mismo.<br />

Conceptos clave. Enumera los términos informáticos y <strong>de</strong> programación más notables que se tratarán<br />

<strong>en</strong> el capítulo.<br />

Descripción <strong>de</strong>l capítulo. Explicación usual <strong>de</strong> los apartados correspondi<strong>en</strong>tes <strong>de</strong>l capítulo. En<br />

cada capítulo se incluy<strong>en</strong> ejemplos y ejercicios resueltos. Los listados <strong>de</strong> los programas completos<br />

o parciales se escrib<strong>en</strong> <strong>en</strong> letra courier con la finalidad principal <strong>de</strong> que puedan ser id<strong>en</strong>tificados<br />

fácilm<strong>en</strong>te por el lector.<br />

Resum<strong>en</strong> <strong>de</strong>l capítulo. Revisa los temas importantes que los estudiantes y lectores <strong>de</strong>b<strong>en</strong> compr<strong>en</strong><strong>de</strong>r<br />

y recordar. Busca también ayudar a reforzar los conceptos clave que se han apr<strong>en</strong>dido <strong>en</strong><br />

el capítulo.


XX<br />

Prólogo<br />

Ejercicios. Al final <strong>de</strong> cada capítulo se proporciona a los lectores una lista <strong>de</strong> ejercicios s<strong>en</strong>cillos<br />

<strong>de</strong> modo que le sirvan <strong>de</strong> oportunidad para que puedan medir el avance experim<strong>en</strong>tado mi<strong>en</strong>tras<br />

le<strong>en</strong> y sigu<strong>en</strong> -<strong>en</strong> su cas- las explicaciones <strong>de</strong>l profesor relativas al capítulo.<br />

Problemas. Después <strong>de</strong>l apartado Ejercicios, se añad<strong>en</strong> una serie <strong>de</strong> activida<strong>de</strong>s y proyectos <strong>de</strong><br />

programación que se le propon<strong>en</strong> al lector como tarea complem<strong>en</strong>taria <strong>de</strong> los ejercicios y <strong>de</strong> un<br />

nivel <strong>de</strong> dificultad algo mayor.<br />

A lo largo <strong>de</strong> todo el libro se incluy<strong>en</strong> una serie <strong>de</strong> recuadros -sombreados o no- que ofrec<strong>en</strong> al<br />

lector consejos, advert<strong>en</strong>cias y reglas <strong>de</strong> uso <strong>de</strong>l l<strong>en</strong>guaje y <strong>de</strong> técnicas <strong>de</strong> programación, con la finalidad<br />

<strong>de</strong> que puedan ir asimilando conceptos prácticos <strong>de</strong> interés que les ayud<strong>en</strong> <strong>en</strong> el apr<strong>en</strong>dizaje y construcción<br />

<strong>de</strong> programas efici<strong>en</strong>tes y <strong>de</strong> fácil lectura.<br />

0 Recuadro. Conceptos importantes que el lector <strong>de</strong>be consi<strong>de</strong>rar durante el <strong>de</strong>sarrollo <strong>de</strong>l capítulo.<br />

0 Consejo. I<strong>de</strong>as, suger<strong>en</strong>cias, recom<strong>en</strong>daciones, ... al lector, con el objetivo <strong>de</strong> obt<strong>en</strong>er el mayor r<strong>en</strong>dimi<strong>en</strong>to<br />

posible <strong>de</strong>l l<strong>en</strong>guaje y <strong>de</strong> la programación.<br />

Precaución. Advert<strong>en</strong>cia al lector para que t<strong>en</strong>ga cuidado al hacer uso <strong>de</strong> los conceptos incluidos<br />

<strong>en</strong> el recuadro adjunto.<br />

Reglas. Normas o i<strong>de</strong>as que el lector <strong>de</strong>be seguir prefer<strong>en</strong>tem<strong>en</strong>te <strong>en</strong> el diseño y construcción <strong>de</strong><br />

sus programas.<br />

ORGANIZACI~N DEL LIBRO<br />

El libro se divi<strong>de</strong> <strong>en</strong> tres partes que unidas constituy<strong>en</strong> un curso completo <strong>de</strong> programación <strong>en</strong> C. Dado<br />

que el conocimi<strong>en</strong>to es acumulativo, los primeros capítulos proporcionan el fundam<strong>en</strong>to conceptual<br />

para la compr<strong>en</strong>sión y apr<strong>en</strong>dizaje <strong>de</strong> C y una guía a los estudiantes a través <strong>de</strong> ejemplos y ejercicios<br />

s<strong>en</strong>cillos y los capítulos posteriores pres<strong>en</strong>tan <strong>de</strong> modo progresivo la programación <strong>en</strong> C <strong>en</strong> <strong>de</strong>talle, <strong>en</strong><br />

el paradigma procedim<strong>en</strong>tal. Los apéndices conti<strong>en</strong><strong>en</strong> un conjunto <strong>de</strong> temas importantes que incluy<strong>en</strong><br />

<strong>de</strong>s<strong>de</strong> guías <strong>de</strong> sintaxis <strong>de</strong> ANSYISO C, hasta o una biblioteca <strong>de</strong> funciones y clases, junto con una<br />

ext<strong>en</strong>sa bibliografía <strong>de</strong> <strong>algoritmos</strong>, <strong>estructura</strong> <strong>de</strong> <strong>datos</strong>, programación ori<strong>en</strong>tada a objetos y una amplia<br />

lista <strong>de</strong> sitios <strong>de</strong> Internet (URLs) don<strong>de</strong> el lector podrá complem<strong>en</strong>tar, ampliar y profundizar <strong>en</strong> el mundo<br />

<strong>de</strong> la programación y <strong>en</strong> la introducción a la ing<strong>en</strong>iería <strong>de</strong> software.<br />

PARTE I. METODOLOGÍA DE LA PROGRAMACI~N<br />

Esta parte es un primer curso <strong>de</strong> programación para alumnos principiantes <strong>en</strong> asignaturas <strong>de</strong> introducción<br />

a la programación <strong>en</strong> l<strong>en</strong>guajes <strong>estructura</strong>dos. Esta parte sirve tanto para cursos <strong>de</strong> C como <strong>de</strong><br />

C++ (<strong>en</strong> este caso con la ayuda <strong>de</strong> los apéndices C y D). Esta parte comi<strong>en</strong>za con una introducción a<br />

la informática y a las ci<strong>en</strong>cias <strong>de</strong> la computación como a la programación. Describe los elem<strong>en</strong>tos<br />

básicos constitutivos <strong>de</strong> un programa y las herrami<strong>en</strong>tas <strong>de</strong> programación utilizadas tales como <strong>algoritmos</strong>,<br />

diagramas <strong>de</strong> flujo, etc. Asimismo se incluye un curso <strong>de</strong>l l<strong>en</strong>guaje C y técnicas <strong>de</strong> programación<br />

que <strong>de</strong>berá emplear el lector <strong>en</strong> su apr<strong>en</strong>dizaje <strong>de</strong> programación. La obra se <strong>estructura</strong> <strong>en</strong> tres<br />

partes: Metodologia <strong>de</strong> programación (conceptos básicos para el análisis, diseño y construcción <strong>de</strong><br />

programas), Fundam<strong>en</strong>tos <strong>de</strong> programación <strong>en</strong> C (sintaxis, reglas y criterios <strong>de</strong> construcción <strong>de</strong>l l<strong>en</strong>guaje<br />

<strong>de</strong> programación C junto con temas específicos <strong>de</strong> C como punteros, arrays, cad<strong>en</strong>as,...), Estructura<br />

<strong>de</strong> <strong>datos</strong> (<strong>en</strong> esta parte se analizan los archivos y las <strong>estructura</strong>s dinámicas <strong>de</strong> <strong>datos</strong> tales como listas<br />

<strong>en</strong>lazadas, pilas, colas y árboles ). Completa la obra una serie <strong>de</strong> apéndices que buscan<br />

es<strong>en</strong>cialm<strong>en</strong>te proporcionar información complem<strong>en</strong>taria <strong>de</strong> utilidad para el lector <strong>en</strong> su período <strong>de</strong><br />

apr<strong>en</strong>dizaje <strong>en</strong> programación <strong>en</strong> C, así como un pequeño curso <strong>de</strong> C++ <strong>en</strong> forma <strong>de</strong> palabras reservadas<br />

y guía <strong>de</strong> refer<strong>en</strong>cia <strong>de</strong> sintaxis que permita al lector emigrar al l<strong>en</strong>guaje C++ facilitándole para<br />

ello las reglas y normas necesarias para convertir programas escritos <strong>en</strong> C a programas escritos<br />

<strong>en</strong> C++.


1<br />

Prólogo<br />

xxi<br />

Capítulo 1. Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación. Proporciona una revisión<br />

<strong>de</strong> las características más importantes necesarias para seguir bi<strong>en</strong> un curso <strong>de</strong> programación básico<br />

y avanzado <strong>en</strong> C. Para ello se <strong>de</strong>scribe la organización física <strong>de</strong> una computadora junto con los conceptos<br />

<strong>de</strong> algoritmo y <strong>de</strong> programa. Asimismo se explican los difer<strong>en</strong>tes tipos <strong>de</strong> l<strong>en</strong>guajes <strong>de</strong><br />

programación y una breve historia <strong>de</strong>l l<strong>en</strong>guaje C.<br />

Capítulo 2. Fundam<strong>en</strong>tos <strong>de</strong> programación. En este capítulo se <strong>de</strong>scrib<strong>en</strong> las fases <strong>de</strong> resolución <strong>de</strong><br />

un problema y los difer<strong>en</strong>tes tipos <strong>de</strong> programación (modular y <strong>estructura</strong>da). Se explican también las<br />

herrami<strong>en</strong>tas <strong>de</strong> programación y repres<strong>en</strong>taciones gráficas utilizadas más frecu<strong>en</strong>tem<strong>en</strong>te <strong>en</strong> el mundo<br />

<strong>de</strong> la programación.<br />

PARTE 11. FUNDAMENTOS DE PROGRAMACI~N EN c<br />

Capítulo 3. El l<strong>en</strong>guaje C: Elem<strong>en</strong>tos básicos. Enseña la <strong>estructura</strong> g<strong>en</strong>eral <strong>de</strong> un programa <strong>en</strong> C junto<br />

con las operaciones básicas <strong>de</strong> creación, ejecución y <strong>de</strong>puración <strong>de</strong> un programa. Se <strong>de</strong>scrib<strong>en</strong> también<br />

los elem<strong>en</strong>tos clave <strong>de</strong> un programa (palabras reservadas, com<strong>en</strong>tarios, tipos <strong>de</strong> <strong>datos</strong>, constantes<br />

y variables, ...) junto con los métodos para efectuar <strong>en</strong>trada y salida <strong>de</strong> <strong>datos</strong> a la computadora.<br />

Capítulo 4. Operadores y expresiones. Se <strong>de</strong>scrib<strong>en</strong> los conceptos y tipos <strong>de</strong> operadores y expresiones,<br />

conversiones y preced<strong>en</strong>cias. Se <strong>de</strong>stacan operadores especiales tales como manipulación <strong>de</strong> bits, condicional,<br />

sizeof, () , [ I , : : , coma, etc.<br />

Capítulo 5. Estmcturas <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias if y swí tch Introduce al concepto <strong>de</strong> <strong>estructura</strong><br />

<strong>de</strong> control y, <strong>en</strong> particular, <strong>estructura</strong>s <strong>de</strong> selección, tales como if , if -else, case y switch.<br />

Expresiones condicionales con el operador ? : , evaluación <strong>en</strong> cortocircuito <strong>de</strong> expresiones lógicas,<br />

errores frecu<strong>en</strong>tes <strong>de</strong> programación y puesta a punto <strong>de</strong> programas.<br />

Capítulo 6. Estructuras repetitivas: bucles (for, while y do-while). El capítulo introduce las<br />

<strong>estructura</strong>s repetitivas (for, while y do-whi le). Examina la repetición (iteración) <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias<br />

<strong>en</strong> <strong>de</strong>talle y compara los bucles controlados por c<strong>en</strong>tinela, ban<strong>de</strong>ra, etc. Explica precauciones y reglas<br />

<strong>de</strong> uso <strong>de</strong> diseño <strong>de</strong> bucles. Compara los tres difer<strong>en</strong>tes tipos <strong>de</strong> bucles, así como el concepto <strong>de</strong> bucles<br />

anidados.<br />

Capítulo 7. Funciones. Examina el diseño y construcción <strong>de</strong> módulos <strong>de</strong> programas mediante funciones.<br />

Se <strong>de</strong>fine la <strong>estructura</strong> <strong>de</strong> una función, prototipos y parámetros. El concepto <strong>de</strong> funciones <strong>en</strong> línea<br />

(inline). Uso <strong>de</strong> bibliotecas <strong>de</strong> funciones, clases <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to, ámbitos, visibilidad <strong>de</strong> una<br />

función. Asimismo se introduce el concepto <strong>de</strong> recursividad y plantillas <strong>de</strong> funciones.<br />

Capítulo 8. Arrays (listas y tablas). Examina la <strong>estructura</strong>ción <strong>de</strong> los <strong>datos</strong> <strong>en</strong> arrays o grupos <strong>de</strong> elem<strong>en</strong>tos<br />

dato <strong>de</strong>l mismo tipo. El capítulo pres<strong>en</strong>ta numerosos ejemplos <strong>de</strong> arays <strong>de</strong> uno, dos o múltiples<br />

índices. Se realiza una introducción a los <strong>algoritmos</strong> <strong>de</strong> ord<strong>en</strong>ación y búsqueda <strong>de</strong> elem<strong>en</strong>tos <strong>en</strong> una<br />

lista. 1<br />

Capítulo 9. Estructuras y uniones. Conceptos <strong>de</strong> <strong>estructura</strong>s, <strong>de</strong>claración, <strong>de</strong>finición, iniciación, uso<br />

y tamaño. Acceso a <strong>estructura</strong>s, arrays <strong>de</strong> <strong>estructura</strong>s y <strong>estructura</strong>s anidadas. Uniones y <strong>en</strong>umeraciones.<br />

Capítulo 10. Punteros (apuntadores). Pres<strong>en</strong>ta una <strong>de</strong> las características más pot<strong>en</strong>tes y efici<strong>en</strong>tes <strong>de</strong>l<br />

l<strong>en</strong>guaje C, los punteros. Este capítulo proporciona explicación <strong>de</strong>tallada <strong>de</strong> los punteros, arrays <strong>de</strong><br />

punteros, punteros <strong>de</strong> cad<strong>en</strong>a, aritmética <strong>de</strong> punteros, punteros constantes, punteros como argum<strong>en</strong>tos<br />

<strong>de</strong> funciones, punteros a funciones y a <strong>estructura</strong>s.<br />

I<br />

I<br />

I


xxii<br />

Prólogo<br />

Capítulo 11. Asignación dinámica <strong>de</strong> memoria. En este capítulo se <strong>de</strong>scribe la gestión dinámica <strong>de</strong> la<br />

memoria y las funciones asociadas para esas tareas : mal loc ( ) , free ( ) , cal loc ( ) ,<br />

realloc ( ) . Se dan reglas <strong>de</strong> funcionami<strong>en</strong>to <strong>de</strong> esas funciones y para asignación y liberación <strong>de</strong><br />

memoria. También se <strong>de</strong>scribe el concepto <strong>de</strong> arrays dinámicos y asignación <strong>de</strong> memoria para arrays.<br />

Capítulo 12. Cad<strong>en</strong>as. Se examina el concepto <strong>de</strong> cad<strong>en</strong>a (string) así como las relaciones <strong>en</strong>tre punteros,<br />

arrays y cad<strong>en</strong>as <strong>en</strong> C. Se introduc<strong>en</strong> conceptos básicos <strong>de</strong> manipulación <strong>de</strong> cad<strong>en</strong>as junto con operaciones<br />

básicas tales como longitud, concat<strong>en</strong>ación, comparación, conversión y búsqueda <strong>de</strong> caracteres<br />

y cad<strong>en</strong>as. Se <strong>de</strong>scrib<strong>en</strong> las funciones más notables <strong>de</strong> la biblioteca string. h.<br />

PARTE 111. ESTRUCTURA DE DATOS<br />

Esta parte es clave <strong>en</strong> el apr<strong>en</strong>dizaje <strong>de</strong> técnicas <strong>de</strong> programación. Tal es su importancia que los planes<br />

<strong>de</strong> estudio <strong>de</strong> cualquier carrera <strong>de</strong> ing<strong>en</strong>iería informática o <strong>de</strong> ci<strong>en</strong>cias <strong>de</strong> la computación incluy<strong>en</strong> una<br />

asignatura troncal d<strong>en</strong>ominada Estructura <strong>de</strong> <strong>datos</strong>.<br />

Capítulo 13. Archivos. El concepto <strong>de</strong> archivo junto con su <strong>de</strong>finición e implem<strong>en</strong>tación es motivo <strong>de</strong><br />

estudio <strong>en</strong> este capítulo. Las operaciones usuales se estudian con <strong>de</strong>t<strong>en</strong>imi<strong>en</strong>to.<br />

Capítulo 14. Listas <strong>en</strong>lazadas. Una lista <strong>en</strong>lazada es una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> que manti<strong>en</strong>e una colección<br />

<strong>de</strong> elem<strong>en</strong>tos, pero el número <strong>de</strong> ellos no se conoce por anticipado o varía <strong>en</strong> un amplio rango. La<br />

lista <strong>en</strong>lazada se compone <strong>de</strong> elem<strong>en</strong>tos que conti<strong>en</strong><strong>en</strong> un valor y un puntero. El capítulo <strong>de</strong>scribe los<br />

fundam<strong>en</strong>tos teóricos y las operaciones que se pued<strong>en</strong> realizar <strong>en</strong> la lista <strong>en</strong>lazada. También se <strong>de</strong>scrib<strong>en</strong><br />

los distintos tipos <strong>de</strong> listas <strong>en</strong>lazadas.<br />

Capítulo 15. Pilas y colas. Colas <strong>de</strong> priorida<strong>de</strong>s. Las i<strong>de</strong>as abstractas <strong>de</strong> pila y cola se <strong>de</strong>scrib<strong>en</strong> <strong>en</strong> el<br />

capítulo. Pilas y colas se pued<strong>en</strong> implem<strong>en</strong>tar <strong>de</strong> difer<strong>en</strong>tes maneras, bi<strong>en</strong> con vectores (arrays) o con<br />

listas <strong>en</strong>lazadas.<br />

Capítulo 16. Árboles. Los árboles son otro tipo <strong>de</strong> <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> dinámica y no lineal. Las operaciones<br />

básicas <strong>en</strong> los árboles junto con sus operaciones fundam<strong>en</strong>tales se estudian <strong>en</strong> el Capítulo 2 1.<br />

APÉNDICES<br />

En todos los libros <strong>de</strong>dicados a la <strong>en</strong>señanza y apr<strong>en</strong>dizaje <strong>de</strong> técnicas <strong>de</strong> programación es frecu<strong>en</strong>te<br />

incluir apéndices <strong>de</strong> temas complem<strong>en</strong>tarios a los explicados <strong>en</strong> los capítulos anteriores. Estos apéndices<br />

sirv<strong>en</strong> <strong>de</strong> guía y refer<strong>en</strong>cia <strong>de</strong> elem<strong>en</strong>tos importantes <strong>de</strong>l l<strong>en</strong>guaje y <strong>de</strong> la programación <strong>de</strong> computadoras.<br />

Apéndice A. L<strong>en</strong>guaje ANSI C. Guh <strong>de</strong> refer<strong>en</strong>cia. Descripción <strong>de</strong>tallada <strong>de</strong> los elem<strong>en</strong>tos fundam<strong>en</strong>tales<br />

<strong>de</strong>l estándar C.<br />

Apéndice B. Códigos <strong>de</strong> caracteres ASCZZ. Listado <strong>de</strong>l juego <strong>de</strong> caracteres <strong>de</strong>l código ASCII utilizado<br />

<strong>en</strong> la actualidad <strong>en</strong> la mayoría <strong>de</strong> las computadoras.<br />

Apéndice C. Palabras reservadas <strong>de</strong> C++. Listado por ord<strong>en</strong> alfabético <strong>de</strong> las palabras reservadas <strong>en</strong><br />

ANSIíiSO C++, al estilo <strong>de</strong> diccionario. Definición y uso <strong>de</strong> cada palabra reservada, con ejemplos s<strong>en</strong>cillos<br />

<strong>de</strong> aplicación.<br />

Apéndice D. Guh <strong>de</strong> sintuxis ANSUISO estándar C++. Refer<strong>en</strong>cia completa <strong>de</strong> sintaxis <strong>de</strong> C++ para<br />

que junto con las palabras reservadas facilite la migración <strong>de</strong> programas C a C++ y permita al lector convertir<br />

programas <strong>estructura</strong>dos escritos <strong>en</strong> C a C++.


prólogo<br />

xxi i i<br />

Apéndice E. Biblioteca <strong>de</strong> funciones estándar ANSI C. Diccionario <strong>en</strong> ord<strong>en</strong> alfabético <strong>de</strong> las funciones<br />

estándar <strong>de</strong> la biblioteca estándar <strong>de</strong> ANSIASO C++, con indicación <strong>de</strong> la sintaxis <strong>de</strong>l prototipo<br />

<strong>de</strong> cada función, una <strong>de</strong>scripción <strong>de</strong> su misión junto con algunos ejemplos s<strong>en</strong>cillos <strong>de</strong> la misma.<br />

Apéndice E Recursos <strong>de</strong> C (Libros, Revistas, URLS <strong>de</strong> Internet). Enumeración <strong>de</strong> los libros más sobresali<strong>en</strong>tes<br />

empleados por los autores <strong>en</strong> la escritura <strong>de</strong> esta obra, así como otras obras importantes complem<strong>en</strong>tarias<br />

que ayud<strong>en</strong> al lector que <strong>de</strong>see profundizar o ampliar aquellos conceptos que consi<strong>de</strong>re<br />

necesario conocer con más <strong>de</strong>t<strong>en</strong>imi<strong>en</strong>to. Asimismo se adjuntan direcciones <strong>de</strong> Internet importantes<br />

para el programador <strong>de</strong> C junto con las revistas más prestigiosas <strong>de</strong>l sector informático y <strong>de</strong> computación<br />

<strong>en</strong> el campo <strong>de</strong> programación.<br />

AGRADECIMIENTOS<br />

Un libro nunca es fruto único <strong>de</strong>l autor, sobre todo si el libro está concebido como libro <strong>de</strong> texto y autoapr<strong>en</strong>dizaje,<br />

y pret<strong>en</strong><strong>de</strong> llegar a lectores y estudiantes <strong>de</strong> informática y <strong>de</strong> computación, y, <strong>en</strong> g<strong>en</strong>eral,<br />

<strong>de</strong> ci<strong>en</strong>cias e ing<strong>en</strong>iería, formación profesional <strong>de</strong> grado superior,. . . , así como autodidactas <strong>en</strong> asignaturas<br />

relacionadas con la programación (introducción, fundam<strong>en</strong>tos, avanzada, etc.). Esta obra no es<br />

una excepción a la regla y son muchas las personas que nos han ayudado a terminarla. En primer lugar<br />

nuestros colegas <strong>de</strong> la Universidad Pontijicia <strong>de</strong> Salamanca <strong>en</strong> el campus <strong>de</strong> Madrid, y <strong>en</strong> particular <strong>de</strong>l<br />

Departam<strong>en</strong>to <strong>de</strong> L<strong>en</strong>guajes y Sistemas Informáticos e Ing<strong>en</strong>iería <strong>de</strong> Software <strong>de</strong> la misma que <strong>de</strong>s<strong>de</strong><br />

hace muchos años nos ayudan y colaboran <strong>en</strong> la impartición <strong>de</strong> las difer<strong>en</strong>tes asignaturas <strong>de</strong>l <strong>de</strong>partam<strong>en</strong>to<br />

y sobre todo <strong>en</strong> la elaboración <strong>de</strong> los programas y planes <strong>de</strong> estudio <strong>de</strong> las mismas. A todos ellos<br />

les agra<strong>de</strong>cemos públicam<strong>en</strong>te su apoyo y ayuda.<br />

Asimismo <strong>de</strong>seamos expresar nuestro agra<strong>de</strong>cimi<strong>en</strong>to a la innumerable cantidad <strong>de</strong> colegas (profesores<br />

y maestros) <strong>de</strong> universida<strong>de</strong>s españolas y latinoamericanas que utilizan nuestros libros para su<br />

clases y laboratorios <strong>de</strong> prácticas. Estos colegas no sólo usan nuestros textos sino que nos hac<strong>en</strong> suger<strong>en</strong>cias<br />

y nos dan consejos <strong>de</strong> cómo mejorarlos. Nos sería imposible citarlos a todos por lo que sólo<br />

po<strong>de</strong>mos mostrar nuestro agra<strong>de</strong>cimi<strong>en</strong>to eterno por su apoyo continuo.<br />

De igual modo no po<strong>de</strong>mos olvidarnos <strong>de</strong> la razón fundam<strong>en</strong>tul <strong>de</strong> ser <strong>de</strong> este libro: los lectores. A<br />

ellos también mi agra<strong>de</strong>cimi<strong>en</strong>to eterno. A nuestros alumnos <strong>de</strong> España y Latinoamérica; a los que no<br />

si<strong>en</strong>do alumnos personales, lo son «virtuales» al saber que exist<strong>en</strong> y que con sus lecturas, sus críticas,<br />

sus com<strong>en</strong>tarios, hac<strong>en</strong> que sigamos trabajando p<strong>en</strong>sando <strong>en</strong> ellos; y a los numerosos lectores profesionales<br />

o autodidactas que confian <strong>en</strong> nuestras obras y <strong>en</strong> particular <strong>en</strong> ésta. A todos ellos nuestro reconocimi<strong>en</strong>to<br />

más sincero <strong>de</strong> gratitud.<br />

A<strong>de</strong>más <strong>de</strong> estos compañeros <strong>en</strong> doc<strong>en</strong>cia, no puedo <strong>de</strong>jar <strong>de</strong> agra<strong>de</strong>cer, una vez más, a nuestra editora<br />

-y, sin embargo, amiga- Concha Fernan<strong>de</strong>z, las constantes muestras <strong>de</strong> afecto y compr<strong>en</strong>sión<br />

que siempre ti<strong>en</strong>e, y ésta no ha sido una excepción, hacia nuestras personas y nuestra obra. Sus continuos<br />

consejos, suger<strong>en</strong>cias y recom<strong>en</strong>daciones, siempre son acertadas y, a<strong>de</strong>más, fáciles <strong>de</strong> seguir; por<br />

si eso no fuera sufici<strong>en</strong>te, siempre b<strong>en</strong>efician a la obra.<br />

A riesgo <strong>de</strong> ser reiterativos, nuestro reconocimi<strong>en</strong>to y agra<strong>de</strong>cimi<strong>en</strong>to eterno a todos: alumnos, lectores,<br />

colegas, profesores, maestros, monitores y editores. Gracias por vuestra inestimable e impagable<br />

ayuda.<br />

En Carchelejo, Jaén (Andalucía) y <strong>en</strong> Madrid, Febrero <strong>de</strong> 2001.<br />

Los autores


PARTE I<br />

METODOLOGÍA<br />

DE LA PROGRAMACI~N


CAPíTULO 1<br />

INTRODUCCIÓN A LA CIENCIA<br />

DE LA COMPUTACIÓN<br />

Y A LA PROGRAMACIÓN<br />

CONTENIDO<br />

1.1. ¿Qué es una computadora?<br />

1.2. ¿Qué es programación?<br />

1.3. Organización física <strong>de</strong> una<br />

computadora.<br />

1.4. Algoritmos y programas.<br />

1.6. Los l<strong>en</strong>guajes <strong>de</strong> programación.<br />

1.6. El l<strong>en</strong>guaje C: historia y características.<br />

1.7. Resum<strong>en</strong>.<br />

L 2


doras.<br />

En esta obra, usted com<strong>en</strong>zará a estudiar la ci<strong>en</strong>cia <strong>de</strong> computadoras o<br />

informática a travks <strong>de</strong> uno <strong>de</strong> los l<strong>en</strong>guajes <strong>de</strong> programación más versatiles<br />

disponibles hoy día: el l<strong>en</strong>guaje C. Este capítulo le introduce a la computadora<br />

y sus compon<strong>en</strong>tes, así como a los l<strong>en</strong>guajes <strong>de</strong> programación, y a la metodo<br />

logía a seguir para la resolución <strong>de</strong> problemas con computadoras y con una<br />

herrami<strong>en</strong>ta d<strong>en</strong>ominada C.<br />

La principal razón para que las personas apr<strong>en</strong>dan l<strong>en</strong>guajes y teCnicas <strong>de</strong><br />

programación es utilizar la computadora como ma herrami<strong>en</strong>ta para resolver<br />

problemas.<br />

1 CONCEPTOS CLAVE<br />

i<br />

E<br />

Algoritmo.<br />

* Athlon.<br />

Byte.<br />

CD-ROM.<br />

* Compilax=idn<br />

* Compilador.<br />

Computadora.<br />

Disquete.<br />

DWD.<br />

Editor.<br />

GB.<br />

Inbl.<br />

* Intérprete.<br />

fus.<br />

* L<strong>en</strong>guaje <strong>de</strong> programación.<br />

L<strong>en</strong>guaje <strong>en</strong>samblador.<br />

L<strong>en</strong>guajemáquina.<br />

m.<br />

Memoria.<br />

Memoriaauxiliar.<br />

Memoria c<strong>en</strong>tral.<br />

* Mó<strong>de</strong>m,<br />

MHZ.<br />

Mcroprocesador.<br />

Ord<strong>en</strong>ador.<br />

P<strong>en</strong>tium.<br />

Portabiiidad.<br />

software.<br />

Unidad C<strong>en</strong>tral <strong>de</strong> Proceso.<br />

3


4 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1 .l. LQUÉ ES UNA COMPUTADORA?<br />

Una computadora' es un dispositivo electrónico utilizado para procesar información y obt<strong>en</strong>er resultados.<br />

Los <strong>datos</strong> y la información se pued<strong>en</strong> introducir <strong>en</strong> la computadora por la <strong>en</strong>trada (input) y a<br />

continuación se procesan para producir una salida (output, resultados), como se observa <strong>en</strong> la Figura 1.1.<br />

La computadora se pue<strong>de</strong> consi<strong>de</strong>rar como una unidad <strong>en</strong> la que se pon<strong>en</strong> ciertos <strong>datos</strong>, o <strong>en</strong>trada <strong>de</strong><br />

<strong>datos</strong>. La computadora procesa estos <strong>datos</strong> y produce unos <strong>datos</strong> <strong>de</strong> salida. Los <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada y los<br />

<strong>datos</strong> <strong>de</strong> salida pued<strong>en</strong> ser, realm<strong>en</strong>te, cualquier cosa, texto, dibujos o sonido. El sistema más s<strong>en</strong>cillo<br />

<strong>de</strong> comunicarse con la computadora una persona es mediante un teclado, una pantalla (monitor) y un<br />

ratón (mouse). Hoy día exist<strong>en</strong> otros dispositivos muy populares tales como escáneres, micrófonos, altavoces,<br />

cámaras <strong>de</strong> ví<strong>de</strong>o, etc.; <strong>de</strong> igual manera, a través <strong>de</strong> mó<strong>de</strong>ms, es posible conectar su computadora<br />

con otras computadoras a través <strong>de</strong> la red Internet.<br />

COMPUTADORA<br />

Programa<br />

u<br />

Datos <strong>de</strong><br />

<strong>en</strong>trada<br />

Datos <strong>de</strong><br />

salida<br />

Figura 1.1. Proceso <strong>de</strong> información <strong>en</strong> una computadora.<br />

Los compon<strong>en</strong>tes físicos que constituy<strong>en</strong> la computadora, junto con los dispositivos que realizan<br />

las tareas <strong>de</strong> <strong>en</strong>trada y salida, se conoc<strong>en</strong> con el término hardware (traducido <strong>en</strong> ocasiones por material).<br />

El conjunto <strong>de</strong> instrucciones que hac<strong>en</strong> funcionar a la computadora se d<strong>en</strong>omina programa que<br />

se <strong>en</strong>cu<strong>en</strong>tra almac<strong>en</strong>ado <strong>en</strong> su memoria; a la persona que escribe programas se llama programador y<br />

al conjunto <strong>de</strong> programas escritos para una computadora se llama software (traducido <strong>en</strong> ocasiones por<br />

logical). Este libro se <strong>de</strong>dicará casi exclusivam<strong>en</strong>te al software, pero se hará una breve revisión <strong>de</strong>l<br />

hardware como recordatorio o introducción según sean los conocimi<strong>en</strong>tos <strong>de</strong>l lector <strong>en</strong> esta materia,<br />

1.2. ORGANIZACIÓN FíSlCA DE UNA COMPUTADORA (HARDWARE)<br />

La mayoría <strong>de</strong> las computadoras, gran<strong>de</strong>s o pequeñas, están organizadas como se muestra <strong>en</strong> la Figura<br />

1.2. Ellas constan fundam<strong>en</strong>talm<strong>en</strong>te <strong>de</strong> tres compon<strong>en</strong>tes principales: unidad c<strong>en</strong>tral <strong>de</strong> proceso<br />

(UCP) o procesador (compuesta <strong>de</strong> la UAL, Unidad aritmético-lógica y la UOC, Unidad <strong>de</strong> Control),<br />

la memoria principal o c<strong>en</strong>tral y el programa.<br />

' En España está muy ext<strong>en</strong>dido el término ord<strong>en</strong>ador para referirse a la traducción <strong>de</strong> la palabra inglesa computer.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 5<br />

Entrada <strong>de</strong> <strong>datos</strong><br />

t<br />

t<br />

Figura 1.2. Organización física <strong>de</strong> una computadora.<br />

Salida <strong>de</strong> <strong>datos</strong><br />

Si a la organización física <strong>de</strong> la Figura 1.2 se le añad<strong>en</strong> los dispositivos para comunicación con la<br />

computadora, aparece la <strong>estructura</strong> típica <strong>de</strong> un sistema <strong>de</strong> computadora: dispositivos <strong>de</strong> <strong>en</strong>trada, dispositivo<br />

<strong>de</strong> salida, memoria externa y el procesador/memoria c<strong>en</strong>tral con su programa (Fig. 1.3).<br />

1.2.1. Dispositivos <strong>de</strong> Entrada/Salida (E/S)<br />

Los dispositivos <strong>de</strong> EntraddSalida (E/S) [InputlOutput (UO, <strong>en</strong> inglés)] permit<strong>en</strong> la comunicación <strong>en</strong>tre<br />

la computadora y el usuario. Los dispositivos <strong>de</strong> <strong>en</strong>trada, como su nombre indica, sirv<strong>en</strong> para introducir<br />

<strong>datos</strong> (información) <strong>en</strong> la computadora para su proceso. Los <strong>datos</strong> se le<strong>en</strong> <strong>de</strong> los dispositivos <strong>de</strong><br />

<strong>en</strong>trada y se almac<strong>en</strong>an <strong>en</strong> la memoria c<strong>en</strong>tral o interna. Los dispositivos <strong>de</strong> <strong>en</strong>trada conviert<strong>en</strong> la información<br />

<strong>de</strong> <strong>en</strong>trada <strong>en</strong> señales eléctricas que se almac<strong>en</strong>an <strong>en</strong> la memoria c<strong>en</strong>tral. Dispositivos <strong>de</strong> <strong>en</strong>trada<br />

típicos son los teclados; otros son: lectores <strong>de</strong> tarjetas -ya <strong>en</strong> <strong>de</strong>suso-, lápices Ópticos, palancas<br />

<strong>de</strong> mando (joystick), lectores <strong>de</strong> códigos <strong>de</strong> barras, escáneres, micrófonos, etc. Hoy día tal vez<br />

el dispositivo <strong>de</strong> <strong>en</strong>trada más popular es el ratón (mouse) que mueve un puntero electrónico sobre la<br />

pantalla que facilita la interacción usuario-máquina 2 .<br />

- I<br />

Dispositivos<br />

<strong>de</strong> <strong>en</strong>trada<br />

1 I<br />

UCP (Procesador)<br />

1 I<br />

Unidad <strong>de</strong><br />

control<br />

Memoria c<strong>en</strong>tral<br />

Dispositivos<br />

<strong>de</strong> salida<br />

I<br />

I<br />

Unidad<br />

aritmética y<br />

lógica<br />

Memoria externa<br />

almac<strong>en</strong>ami<strong>en</strong>to<br />

perman<strong>en</strong>te<br />

Figura 1.3. Organización física <strong>de</strong> una computadora.<br />

’ Todas las acciones a realizar por el usuario se realizarán con el ratón con la excepción <strong>de</strong> las que se requier<strong>en</strong> <strong>de</strong> la escritura<br />

<strong>de</strong> <strong>datos</strong> por teclado.


6 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Los dispositivos <strong>de</strong> salida permit<strong>en</strong> repres<strong>en</strong>tar los resultados (salida) <strong>de</strong>l proceso <strong>de</strong> los <strong>datos</strong>. El di+<br />

positivo <strong>de</strong> salida típico es la pantalla (CRT)' o monitor. Otros dispositivos <strong>de</strong> salida son: impresoras<br />

(imprim<strong>en</strong> resultados <strong>en</strong> papel), trazadores gráficos (plotters), reconocedores <strong>de</strong> voz, altavoces, etc.<br />

El teclado y la pantalla constituy<strong>en</strong> -<strong>en</strong> muchas ocasiones- un Único dispositivo, d<strong>en</strong>ominado<br />

terminal. Un teclado <strong>de</strong> terminal es similar al teclado <strong>de</strong> una máquina <strong>de</strong> escribir mo<strong>de</strong>rna con la difer<strong>en</strong>cia<br />

<strong>de</strong> algunas teclas extras que ti<strong>en</strong>e el terminal para funciones especiales. Si está utilizando una<br />

computadora personal, el teclado y el monitor son dispositivos in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes conectados a la computadora<br />

por cables. En ocasiones a la impresora se la conoce como dispositivo <strong>de</strong> copia dura («hard<br />

copy»), <strong>de</strong>bido a que la escritura <strong>en</strong> la impresora es una copia perman<strong>en</strong>te (dura) <strong>de</strong> la salida, y a la<br />

pantalla se le d<strong>en</strong>omina <strong>en</strong> contraste: dispositivo <strong>de</strong> copia blanda (eso$ copy»), ya que se pier<strong>de</strong> la<br />

pantalla actual cuando se visualiLa la sigui<strong>en</strong>te.<br />

Los dispositivos <strong>de</strong> <strong>en</strong>traddsalida y los dispositivos <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to secundario o auxiliar<br />

(memoria externa) se conoc<strong>en</strong> también con el nombre <strong>de</strong> di.sposirivci.\ perlféricos o simplem<strong>en</strong>te periféricos<br />

ya que, normalm<strong>en</strong>te, son externos a la computadora. Estos dispositivos son unidad <strong>de</strong> discos<br />

(disquetes, CD-ROM, DVDs, cintas, vi<strong>de</strong>ocámaras,etc.).<br />

Figura 1.4. Dispositivo <strong>de</strong> salida (impresora)<br />

1.2.2. La memoria c<strong>en</strong>tral (interna)<br />

La memoria c<strong>en</strong>tral o simplem<strong>en</strong>te memoria (interna o principal) se utiliza para almac<strong>en</strong>ar información<br />

(RAM, Random Access Memory). En g<strong>en</strong>eral, la información almac<strong>en</strong>ada <strong>en</strong> memoria pue<strong>de</strong> ser<br />

<strong>de</strong> dos tipos: las instrucciones <strong>de</strong> un programa y los duros con los que operan las instrucciones. Por<br />

ejemplo, para que un programa se pueda ejecutar (correr, rodar, funcionar.. ., <strong>en</strong> inglés run), <strong>de</strong>be ser<br />

situado <strong>en</strong> la memoria c<strong>en</strong>tral, <strong>en</strong> una operación d<strong>en</strong>ominada carga (load) <strong>de</strong>l programa. Después, cuando<br />

se ejecuta (se realiza, funciona) el programa, cualquier dato u procesur por el programa se <strong>de</strong>be llevar<br />

a la memoria mediante las instrucciones <strong>de</strong>l programa. En la memoria c<strong>en</strong>tral, hay también <strong>datos</strong><br />

diversos y espacio <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to temporal que necesita el programa cuando se ejecuta con él a fin<br />

<strong>de</strong> po<strong>de</strong>r funcionar.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 7<br />

Cuando un programa se ejecuta (realiza, funciona) <strong>en</strong> una computadora, se dice que se ejecuta.<br />

Con el objetivo <strong>de</strong> que el procesador pueda obt<strong>en</strong>er los <strong>datos</strong> <strong>de</strong> la memoria c<strong>en</strong>tral más rápidam<strong>en</strong>te,<br />

la mayoría <strong>de</strong> los procesadores actuales (muy rápido\) utilitan con frecu<strong>en</strong>cia una memoriu<br />

d<strong>en</strong>ominada cuche‘que sirva para almac<strong>en</strong>ami<strong>en</strong>to intermedio <strong>de</strong> <strong>datos</strong> <strong>en</strong>tre el procesador y la memoria<br />

principal La memoria caché -<strong>en</strong> la actualidad- \e incorpora casi siempre al procesador.<br />

La memoria c<strong>en</strong>tral <strong>de</strong> una computadora es una zona <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to organizada <strong>en</strong> c<strong>en</strong>t<strong>en</strong>ares<br />

o millares <strong>de</strong> unida<strong>de</strong>s <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to individual o celdas. La memoria c<strong>en</strong>tral consta <strong>de</strong> un conjunto<br />

<strong>de</strong> (*eldar úe memoria (estas celdas o posiciones <strong>de</strong> memoria se d<strong>en</strong>ominan también palahms,<br />

aunque no «guardan» analogía con las palabras <strong>de</strong>l l<strong>en</strong>guaje). El número <strong>de</strong> celdas <strong>de</strong> memoria <strong>de</strong> la<br />

memoria c<strong>en</strong>tral, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l tipo y ino<strong>de</strong>lo <strong>de</strong> computadora; hoy día el número suele ser millones<br />

(32.64, 128, etc.) Cada celda <strong>de</strong> ineinoria consta <strong>de</strong> un cierto número <strong>de</strong> bits (normalm<strong>en</strong>te 8, un hite).<br />

La unidad elem<strong>en</strong>tal <strong>de</strong> memoria se llama byte (octeto). Un h\te ti<strong>en</strong>e la capacidad <strong>de</strong> almac<strong>en</strong>ar un<br />

carácter <strong>de</strong> información, y está formado por un conjunto <strong>de</strong> unida<strong>de</strong>s más pequeñas <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to<br />

d<strong>en</strong>ominadas hifv, que son dígitos binarim (O o 1 ).<br />

Figura 1.5. Computadora portátil digital.<br />

G<strong>en</strong>eralin<strong>en</strong>te. se acepta que un byte conti<strong>en</strong>e ocho bits. Por umigiit<strong>en</strong>te, si \e <strong>de</strong>sea almac<strong>en</strong>ar la<br />

frase<br />

la computadora utili/ará exactam<strong>en</strong>te 27 byte\ conscculivos <strong>de</strong> iTictnoria. Obsérvese que, a<strong>de</strong>más <strong>de</strong> las<br />

letras, exist<strong>en</strong> cuatro espacios <strong>en</strong> blanco y un punto (un espacio es un carácter que emplea también un<br />

byte). De modo similar, el número <strong>de</strong>l pasaporte<br />

1’5 4d/t!iI<br />

ocupará 9 bytes. pero si se almac<strong>en</strong>a como<br />

15 148 /891


1<br />

8 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

ocupará 11. Estos <strong>datos</strong> se llaman alfanuméricos, y pued<strong>en</strong> constar <strong>de</strong>l alfabeto, dígitos o incluso caracteres<br />

especiales (símbolos: $, #, *, etc.).<br />

Mi<strong>en</strong>tras que cada carácter <strong>de</strong> un dato alfanumérico se almac<strong>en</strong>a <strong>en</strong> un byte, la información numérica<br />

se almac<strong>en</strong>a <strong>de</strong> un modo difer<strong>en</strong>te. Los <strong>datos</strong> numéricos ocupan 2,4 e incluso 8 bytes consecutivos,<br />

<strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l tipo <strong>de</strong> dato numérico (se verá <strong>en</strong> el Capítulo 3).<br />

Exist<strong>en</strong> dos conceptos importantes asociados a cada celda o posición <strong>de</strong> memoria: su dirección y su<br />

cont<strong>en</strong>ido. Cada celda o byte ti<strong>en</strong>e asociada una única dirección que indica su posición relativa <strong>en</strong><br />

memoria y mediante la cual se pue<strong>de</strong> acce<strong>de</strong>r a la posición para almac<strong>en</strong>ar o recuperar información. La<br />

información almac<strong>en</strong>ada <strong>en</strong> una posición <strong>de</strong> memoria es su cont<strong>en</strong>ido. La Figura 1.6 muestra una memoria<br />

<strong>de</strong> computadora que consta <strong>de</strong> 1 .O00 posiciones <strong>en</strong> memoria con direcciones <strong>de</strong> O a 999. El cont<strong>en</strong>ido<br />

<strong>de</strong> estas direcciones o posiciones <strong>de</strong> memoria se llaman palabras, <strong>de</strong> modo que exist<strong>en</strong> palabras <strong>de</strong> 8,<br />

16,32 y 64 bits. Por consigui<strong>en</strong>te, si trabaja con una máquina <strong>de</strong> 32 bits, significa que <strong>en</strong> cada posición<br />

<strong>de</strong> memoria <strong>de</strong> su computadora pue<strong>de</strong> alojar 32 bits, es <strong>de</strong>cir 32 dígitos, bi<strong>en</strong> ceros o unos.<br />

Siempre que una nueva información se almac<strong>en</strong>a <strong>en</strong> una posición, se <strong>de</strong>struye (<strong>de</strong>saparece) cualquier<br />

información que <strong>en</strong> ella hubiera y no se pue<strong>de</strong> recuperar. La dirección es perman<strong>en</strong>te y única, el<br />

cont<strong>en</strong>ido pue<strong>de</strong> cambiar mi<strong>en</strong>tras se ejecuta un programa.<br />

La memoria c<strong>en</strong>tral <strong>de</strong> una computadora pue<strong>de</strong> t<strong>en</strong>er <strong>de</strong>s<strong>de</strong> unos c<strong>en</strong>t<strong>en</strong>ares <strong>de</strong> millares <strong>de</strong> bytes<br />

hasta millones <strong>de</strong> bytes. Como el byte es una unidad elem<strong>en</strong>tal <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to, se utilizan múltiplos<br />

para <strong>de</strong>finir el tamaño <strong>de</strong> la memoria c<strong>en</strong>tral: Kilo-byte (KB o Kb) igual a 1.024 bytes (2“’)-prácti-<br />

cam<strong>en</strong>te se toman 1 .O00- y Megabyte (MB o Mb) igual a 1 .O24 x 1 .O24 bytes (2”’)-prácticam<strong>en</strong>te<br />

se consi<strong>de</strong>ra un 1 .OO0.000-.<br />

Tabla 1.1. Unida<strong>de</strong>s <strong>de</strong> medida <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to.<br />

Byte<br />

Kilobyte<br />

Megabyte<br />

Gigabyte<br />

Terabyte<br />

Byte (b)<br />

Kbyte (Kb)<br />

Mbyte (Mb)<br />

Gbyte (Gb)<br />

Tbyte (Tb)<br />

equivale a<br />

equivale a<br />

equivale a<br />

equivale a<br />

equivale a<br />

8 bits<br />

I .24 bytes<br />

1 .O24 Kbytes<br />

1 .O24 Mbytes<br />

1 .O24 Gbytes<br />

1 Tb = 1.024 Gb = 1.024 Mb = 1.048.576 Kb = 1.073.741.824 b<br />

En la actualidad, las computadoras personales tipo PC suel<strong>en</strong> t<strong>en</strong>er memorias c<strong>en</strong>trales <strong>de</strong> 32 a 64<br />

Mb, aunque ya es muy frecu<strong>en</strong>te ver PC con memorias <strong>de</strong> 128 Mb y 192 Mb.<br />

direcciones<br />

999 -1<br />

Figura 1.6. Memoria c<strong>en</strong>tral <strong>de</strong> una computadora.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 9<br />

La memoria principal es la <strong>en</strong>cargada <strong>de</strong> almac<strong>en</strong>ar los programas y <strong>datos</strong> que se están ejecutando<br />

y su principal característica es que el acceso a los <strong>datos</strong> o instrucciones <strong>de</strong>s<strong>de</strong> esta memoria es muy<br />

rápido.<br />

En la memoria principal se almac<strong>en</strong>an:<br />

0 Los <strong>datos</strong> <strong>en</strong>viados para procesarse <strong>de</strong>s<strong>de</strong> los dispositivos <strong>de</strong> <strong>en</strong>trada.<br />

Los programas que realizarán los procesos.<br />

0 Los resultados obt<strong>en</strong>idos preparados para <strong>en</strong>viarse a un dispositivo <strong>de</strong> salida.<br />

En la memoria principal se pued<strong>en</strong> distinguir dos tipos <strong>de</strong> memoria: RAM y ROM. La memoria<br />

RAM (Random Access Memory, Memoria <strong>de</strong> acceso aleatorio) almac<strong>en</strong>a los <strong>datos</strong> e instrucciones a<br />

procesar. Es un tipo <strong>de</strong> memoria volátil (su cont<strong>en</strong>ido se pier<strong>de</strong> cuando se apaga la computadora); esta<br />

memoria es, <strong>en</strong> realidad, la que se suele conocer como memoria principal o <strong>de</strong> trabajo; <strong>en</strong> esta memoria<br />

se pued<strong>en</strong> escribir <strong>datos</strong> y leer <strong>de</strong> ella. La memoria ROM (Read Only Memory) es una memoria<br />

perman<strong>en</strong>te <strong>en</strong> la que no se pue<strong>de</strong> escribir (vi<strong>en</strong>e pregrabada «grabada» por el fabricante; es una memoria<br />

<strong>de</strong> sólo lectura. Los programas almac<strong>en</strong>ados <strong>en</strong> ROM no se pierd<strong>en</strong> al apagar la computadora y<br />

cuando se <strong>en</strong>ci<strong>en</strong><strong>de</strong>, se lee la información almac<strong>en</strong>ada <strong>en</strong> esta memoria. Al ser esta memoria <strong>de</strong> sólo<br />

lectura, los programas almac<strong>en</strong>ados <strong>en</strong> los chips ROM no se pued<strong>en</strong> modificar y suel<strong>en</strong> utilizarse para<br />

almac<strong>en</strong>ar los programas básicos que sirv<strong>en</strong> para arrancar la computadora.<br />

1.2.3. La Unidad C<strong>en</strong>tral <strong>de</strong> Proceso (UCP)<br />

La Unidad C<strong>en</strong>tral <strong>de</strong> Proceso, UCP (C<strong>en</strong>tral Processing ünit, CPU, <strong>en</strong> inglés), dirige y controla el<br />

proceso <strong>de</strong> información realizado por la computadora. La UCP procesa o manipula la información almac<strong>en</strong>ada<br />

<strong>en</strong> memoria; pue<strong>de</strong> recuperar información <strong>de</strong>s<strong>de</strong> memoria (esta información son <strong>datos</strong> o instrucciones:<br />

programas). También pue<strong>de</strong> almac<strong>en</strong>ar los resultados <strong>de</strong> estos procesos <strong>en</strong> memoria para su<br />

uso posterior.<br />

Unidad c<strong>en</strong>tral <strong>de</strong> proceso<br />

Unidad lógica y aritmética<br />

Memoria c<strong>en</strong>tral<br />

Unidad <strong>de</strong> control<br />

1 Datos <strong>de</strong><br />

<strong>en</strong>trada<br />

1 Datos<br />

<strong>de</strong><br />

salida<br />

Figura 1.7. Unidad C<strong>en</strong>tral <strong>de</strong> Proceso.


10 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

La UCP consta <strong>de</strong> dos compon<strong>en</strong>tes: unidad <strong>de</strong> control (UC) y unidad aritmético-16gicu (UAL)<br />

(Fig. I .7). La unidad <strong>de</strong> control (Control Unit, CU) coordina las activida<strong>de</strong>s <strong>de</strong> la computadora y <strong>de</strong>termina<br />

qué operaciones se <strong>de</strong>b<strong>en</strong> realizar y <strong>en</strong> qué ord<strong>en</strong>; asimismo controla y sincroniza todo el proceso<br />

<strong>de</strong> la computadora.<br />

La unidad aritmético-lógica (Aritmethic-Logic Unit, ALU) realiza operaciones aritméticas y Iógicas,<br />

tales como suma, resta, multiplicación, división y comparaciones. Los <strong>datos</strong> <strong>en</strong> la memoria c<strong>en</strong>tral<br />

se pued<strong>en</strong> leer (recuperar) o escribir (cambiar) por la UCP.<br />

1.2.4. El microprocesador<br />

El microprocesador es un chip (un circuito integrado) que controla y realiza las funciones y operaciones<br />

con los <strong>datos</strong>. Se suele conocer como procesador y es el cerebro y corazón <strong>de</strong> la computadora.<br />

En realidad el microprocesador repres<strong>en</strong>ta a la Unidad C<strong>en</strong>tral <strong>de</strong> Proceso.<br />

La velocidad <strong>de</strong> un microprocesador se mi<strong>de</strong> <strong>en</strong> megahercios (MHz) y manipulan palabras <strong>de</strong> 4 a<br />

64 bits. Los microprocesadores históricos van <strong>de</strong>s<strong>de</strong> el 8080 hasta el 80486/80586 pasando por el 8086,<br />

8088,80286 y 80386, todos ellos <strong>de</strong>l fabricante Intel. Exist<strong>en</strong> otras empresas como AMD y Cyrix, con<br />

mo<strong>de</strong>los similares. Los microprocesadores <strong>de</strong> segunda g<strong>en</strong>eración <strong>de</strong> Intel son los P<strong>en</strong>tium, P<strong>en</strong>tium<br />

MMX, P<strong>en</strong>tium I1 con velocida<strong>de</strong>s <strong>de</strong> 233,266,300 y 450 MHz. Los microprocesadores más mo<strong>de</strong>rnos<br />

(<strong>de</strong> 3.” g<strong>en</strong>eración) son los P<strong>en</strong>tium 111 con frecu<strong>en</strong>cias <strong>de</strong> 450 hasta 1 GHz.<br />

La guerra <strong>de</strong> los microprocesadores se c<strong>en</strong>tró <strong>en</strong> el año 2000 <strong>en</strong> torno a AMD, que ofrec<strong>en</strong> ya procesadores<br />

Athlon <strong>de</strong> 1 GHz y <strong>de</strong> I .2 GHz. Intel pres<strong>en</strong>tó a finales <strong>de</strong> noviembre <strong>de</strong> 2000 su nueva arquitectura<br />

P<strong>en</strong>tium TV-la g<strong>en</strong>eración sigui<strong>en</strong>te a la familia x86-, que ofrec<strong>en</strong> chips <strong>de</strong> velocida<strong>de</strong>s <strong>de</strong> 1.3.<br />

1.4 y 1.5 GHz y anuncian velocida<strong>de</strong>s <strong>de</strong> hasta 2 GHz.<br />

Dispositivos<br />

<strong>de</strong> <strong>en</strong>trada<br />

principal<br />

Unidad <strong>de</strong> control<br />

Dispositivos<br />

<strong>de</strong> salida<br />

v<br />

Dispositivos<br />

<strong>de</strong> €IS<br />

Unidad aritmético<br />

y lógica<br />

I<br />

Microprocesador<br />

Figura 1.8. Organización física <strong>de</strong> una computadora con un microprocesador.<br />

1.2.5. Memoria auxiliar (externa)<br />

Cuando un programa se ejecuta, se <strong>de</strong>be situar primero <strong>en</strong> memoria c<strong>en</strong>tral <strong>de</strong> igual modo que los <strong>datos</strong>.<br />

Sin embargo, la información almac<strong>en</strong>ada <strong>en</strong> la memoria se pier<strong>de</strong> (borra) cuando se apaga (<strong>de</strong>sconecta<br />

<strong>de</strong> la red eléctrica) la computadora y, por otra parte, la memoria c<strong>en</strong>tral es limitada <strong>en</strong> capacidad. Por<br />

-


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 11<br />

esta razón, para po<strong>de</strong>r disponer <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to perman<strong>en</strong>te, tanto para programas como para <strong>datos</strong>,<br />

se necesitan dispositivos <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to secundario, auxiliar o masivo («mass storage», o «secondary<br />

storage»).<br />

Los dispositivos <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to o memorias auxiliares (externas o secundarias) más comúnm<strong>en</strong>te<br />

utilizados son: cintas magnéticas, discos magnéticos, discos compactos (CD-ROM Compact<br />

Disk Read Only Memory), y vi<strong>de</strong>odiscos digitales (DVD). Las cintas son utilizadas principalm<strong>en</strong>te por<br />

sistemas <strong>de</strong> computadoras gran<strong>de</strong>s similares a las utilizadas <strong>en</strong> los equipos <strong>de</strong> audio. Los discos y disquetes<br />

magnéticos se utilizan por todas las computadoras, especialm<strong>en</strong>te las medias y pequeñas -las<br />

computadoras personales-. Los discos pued<strong>en</strong> ser duros, <strong>de</strong> gran capacidad <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to (su<br />

capacidad mínima es <strong>de</strong> 10 Mb), disquetes o discosflexibles (


12 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Comparación <strong>de</strong> la memoria c<strong>en</strong>tral y la memoria auxiliar<br />

La memoria c<strong>en</strong>tral o principal es mucho más rápida y cara que la memoria auxiliar. Se <strong>de</strong>b<strong>en</strong><br />

transferir los <strong>datos</strong> <strong>de</strong>s<strong>de</strong> la memoria auxiliar hasta la memoria c<strong>en</strong>tral, antes <strong>de</strong> que puedan ser<br />

procesados. Los <strong>datos</strong> <strong>en</strong> memoria c<strong>en</strong>tral son: volátiles y <strong>de</strong>saparec<strong>en</strong> cuando se apaga la computadora.<br />

Los <strong>datos</strong> <strong>en</strong> memoria auxiliar son perman<strong>en</strong>tes y no <strong>de</strong>saparec<strong>en</strong> cuando se apaga la<br />

computadora.<br />

Las computadoras mo<strong>de</strong>rnas necesitan comunicarse con otras computadoras. Si la computadora se<br />

conecta con una tarjeta <strong>de</strong> red se pue<strong>de</strong> conectar a una red <strong>de</strong> <strong>datos</strong> locales (red <strong>de</strong> área local). De este<br />

modo se pue<strong>de</strong> acce<strong>de</strong>r y compartir a cada una <strong>de</strong> las memorias <strong>de</strong> disco y otros dispositivos <strong>de</strong> <strong>en</strong>trada<br />

y salida. Si la computadora ti<strong>en</strong>e un rncí<strong>de</strong>m, se pue<strong>de</strong> comunicar con computadoras distantes. Se<br />

pued<strong>en</strong> conectar a una red <strong>de</strong> <strong>datos</strong> o <strong>en</strong>viar correa electrhnica a través <strong>de</strong> las re<strong>de</strong>s corporativas Intranemxtranet<br />

o la propia red Internet. También es posible <strong>en</strong>viar y recibir m<strong>en</strong>sajes <strong>de</strong> fax.<br />

1.2.6. Proceso <strong>de</strong> ejecución <strong>de</strong> un programa<br />

La Figura 1.1 O muestra la comunicación <strong>en</strong> una computadora cuando se ejecuta un programa, a través<br />

<strong>de</strong> los dispositivos <strong>de</strong> <strong>en</strong>trada y salida. El ratón y el teclado introduc<strong>en</strong> <strong>datos</strong> <strong>en</strong> la memoria c<strong>en</strong>tral<br />

cuando se ejecuta el programa. Los <strong>datos</strong> intermedios o auxiliares se transfier<strong>en</strong> <strong>de</strong>s<strong>de</strong> la unidad <strong>de</strong> disco<br />

(archivo) a la pantalla y a la unidad <strong>de</strong> disco, a medida que se ejecuta el programa.<br />

Monitor<br />

Impresora lbser<br />

Ratbn<br />

& Memoria<br />

c<strong>en</strong>tral<br />

i=-=I Unidad <strong>de</strong><br />

Teclado<br />

Figura 1.10. Proceso <strong>de</strong> ejecución <strong>de</strong> un programa.<br />

1.2.7. Comunicaciones: mó<strong>de</strong>ms, re<strong>de</strong>s, telefonía RDSl y ADSL<br />

Una <strong>de</strong> las posibilida<strong>de</strong>s más interesantes <strong>de</strong> las computadoras es la comunicación <strong>en</strong>tre ellas, cuando<br />

se <strong>en</strong>cu<strong>en</strong>tran <strong>en</strong> sitios separados físicam<strong>en</strong>te,y se <strong>en</strong>cu<strong>en</strong>tran <strong>en</strong>lazadas por vía telefónica. Estas computadoras<br />

se conectan <strong>en</strong> re<strong>de</strong>s LAN (Red <strong>de</strong> Area Local) y WAN (Red <strong>de</strong> Area Ancha), aunque hoy día,<br />

las re<strong>de</strong>s más implantadas son las re<strong>de</strong>s que se conectan con tecnología Internet, y, por tanto, conexión<br />

a la red Internet. Estas re<strong>de</strong>s son Zntranef y Extranet, y se conoc<strong>en</strong> como re<strong>de</strong>s corporativas, ya que<br />

<strong>en</strong>lazan computadoras <strong>de</strong> los empleados con las empresas. Las instalaciones <strong>de</strong> las comunicaciones<br />

requier<strong>en</strong> <strong>de</strong> líneas telefónicas analógicas o digitales y <strong>de</strong> mó<strong>de</strong>ms.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 13<br />

El mó<strong>de</strong>m es un dispositivo periférico que permite intercambiar información <strong>en</strong>tre computadoras a<br />

través <strong>de</strong> una línea telefónica. El mó<strong>de</strong>m es un acrónimo <strong>de</strong> Modulador-Demodulador, y es un dispositivo<br />

que transforma las señales digitales <strong>de</strong> la computadora <strong>en</strong> señales eléctricas analógicas telefónicas<br />

y viceversa, con lo que es posible transmitir y recibir información a través <strong>de</strong> la línea telefónica.<br />

Estas operaciones se conoc<strong>en</strong> como modulación (se transforman los <strong>datos</strong> digitales <strong>de</strong> la computadora<br />

para que puedan ser <strong>en</strong>viados por la línea telefónica como analógicos) y <strong>de</strong>modulación (transforman<br />

los <strong>datos</strong> analógicos recibidos mediante la línea telefónica <strong>en</strong> <strong>datos</strong> digitales para que puedan ser leídos<br />

por la computadora).<br />

Un mó<strong>de</strong>m convierte señal analógica <strong>en</strong> digital y viceversa.<br />

Los mó<strong>de</strong>ms permit<strong>en</strong>, a<strong>de</strong>más <strong>de</strong> las conexiones <strong>en</strong>tre computadoras, <strong>en</strong>vío y recepción <strong>de</strong> fax,<br />

acceso a Internet, etc. Una <strong>de</strong> las características más importantes <strong>de</strong> un mó<strong>de</strong>m es la velocidad. Cifras<br />

usuales son 33,600 (33 K) baudios (1 baudio es 1 bit por segundo, bps) y 56,000 baudios (56 K).<br />

Los mó<strong>de</strong>ms pued<strong>en</strong> ser <strong>de</strong> tres tipos: interno (es una tarjeta que se conecta a la placa base internam<strong>en</strong>te);<br />

externo (es un dispositivo que se conecta externam<strong>en</strong>te a la computadora a través <strong>de</strong> puertos<br />

COM, USB, etc.); PC-Card, son mó<strong>de</strong>ms <strong>de</strong>l tipo tarjeta <strong>de</strong> crédito, que sirve para conexión a las computadoras<br />

portátiles.<br />

A<strong>de</strong>más <strong>de</strong> los mó<strong>de</strong>ms analógicos, es posible la conexión a Internet y a las re<strong>de</strong>s corporativas <strong>de</strong><br />

las compañías mediante la Red Digital <strong>de</strong> Sistemas Integrados (RDSI, <strong>en</strong> inglés, IDSN), que permite la<br />

conexión a 128 Kbps, disponi<strong>en</strong>do <strong>de</strong> dos líneas telefónicas, cada una <strong>de</strong> ellas a 64 Kbps.<br />

También, se está com<strong>en</strong>zando a implantar la tecnología digital ADSL, que permite la conexión a<br />

Internet a velocidad similar a la red RDSI, 128 Kbps y a 256 Kbps, según sea para «subir» o «bajar»<br />

<strong>datos</strong> a la red, respectivam<strong>en</strong>te, pudi<strong>en</strong>do llegar a 2M bps.<br />

Figura 1.11. Mó<strong>de</strong>m comercial.<br />

1.2.8. La computadora personal multimedia i<strong>de</strong>al para la programación<br />

Hoy día, las computadoras personales profesionales y domésticas que se comercializan son prácticam<strong>en</strong>te<br />

todas ellas multimedia, es <strong>de</strong>cir, incorporan características multimedia (CD-ROM, DVD, tarjeta<br />

<strong>de</strong> sonido, altavoces y micrófono) que permit<strong>en</strong> integrar texto, sonido, gráficos e imág<strong>en</strong>es <strong>en</strong> movimi<strong>en</strong>to.<br />

Las computadoras multimedia pued<strong>en</strong> leer discos CD-ROM y DVD <strong>de</strong> gran capacidad <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to.<br />

Esta característica ha hecho que la mayoría <strong>de</strong> los fabricantes <strong>de</strong> software comercialic<strong>en</strong> sus<br />

compiladores (programas <strong>de</strong> traducción <strong>de</strong> l<strong>en</strong>guajes <strong>de</strong> programación) <strong>en</strong> CD-ROM, almac<strong>en</strong>ando <strong>en</strong><br />

un solo disco, lo que antes necesitaba seis, ocho o doce disquetes, y cada vez será más frecu<strong>en</strong>te el uso<br />

<strong>de</strong>l DVD.


14 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Figura 1.12. Computadora multimedia.<br />

El estudiante <strong>de</strong> informática o <strong>de</strong> computación actual, y mucho más el profesional, dispone <strong>de</strong> un<br />

amplio abanico <strong>de</strong> computadoras a precios asequibles y con prestaciones altas. En el cuarto trimestre <strong>de</strong>l<br />

año 2000, un PC <strong>de</strong> escritorio típico para apr<strong>en</strong><strong>de</strong>r a programar, y posteriorm<strong>en</strong>te utilizar <strong>de</strong> modo profesional,<br />

es posible <strong>en</strong>contrarlo a precios <strong>en</strong> el rango <strong>en</strong>tre 100.000 pesetas y 200.000/300.000 pesetas<br />

(US$500 a US$ 1.000/1 .500), <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> prestaciones y fabricante (según sean d6nico.w ofahricudos<br />

por nzarcus acreditadas como HI;: IBM, Compaq), aunque la mayoría <strong>de</strong> las ofertas suel<strong>en</strong> incluir,<br />

como mínimo, 64 MB <strong>de</strong> RAM, CD-ROM, monitores <strong>de</strong> 15”, tarjetas <strong>de</strong> sonido, etc. La Tabla 1.2 resume<br />

nuestra propuesta y recom<strong>en</strong>dación <strong>de</strong> características medias <strong>de</strong> un/a computador/a PC.<br />

Tabla 1.2. Características <strong>de</strong> un PC i<strong>de</strong>al.<br />

Procesador<br />

Memoria<br />

Microprocesador <strong>de</strong> las marcas Intel o AMD, <strong>de</strong> 800 Mz o superior.<br />

128 Mb y recom<strong>en</strong>dable para aplicaciones profesionales 256 o 5 12 Mb.<br />

Caché Memoria especial que usa el procesador para acelerar sus operaciones. 5 12 Kb o 128<br />

Kb.<br />

Disco duro<br />

Internet<br />

Vi<strong>de</strong>o<br />

Monitor<br />

Almac<strong>en</strong>ami<strong>en</strong>to<br />

20 Gigabytes (mínimo).<br />

Preparado para Internet (incluso con mó<strong>de</strong>m instalado <strong>de</strong> 56 Kb).<br />

Memoria <strong>de</strong> ví<strong>de</strong>o, con un mínimo <strong>de</strong> 4 Mb.<br />

17” o 19” (pantalla tradicional o plana “TFT”).<br />

CD-RW, DVD.<br />

Puertos<br />

Serie, paralelo y USB.<br />

Marcas HP, Compaq, Dell, IBM, El System, Futjisu, Inves, . .


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 15<br />

1.3. CONCEPTO DE ALGORITMO<br />

El objetivo fundam<strong>en</strong>tal <strong>de</strong> este texto es <strong>en</strong>señar a resolver problemas mediante una computadora. El<br />

programador <strong>de</strong> computadora es antes que nada una persona que resuelve problemas, por lo que para Ilegar<br />

a ser un programador eficaz se necesita apr<strong>en</strong><strong>de</strong>r a resolver problemas <strong>de</strong> un modo riguroso y sistemático.<br />

A lo largo <strong>de</strong> todo este libro nos referiremos a la metodología necesaria para resolver problemas<br />

mediante programas, concepto que se d<strong>en</strong>omina metodología <strong>de</strong> la programación. El eje<br />

c<strong>en</strong>tral <strong>de</strong> esta metodología es el concepto, ya tratado, <strong>de</strong> algoritmo.<br />

Un algoritmo es un método para resolver un problema. Aunque la popularización <strong>de</strong>l término ha Ilegado<br />

con el adv<strong>en</strong>imi<strong>en</strong>to <strong>de</strong> la era informhtica, algoritmo provi<strong>en</strong>e <strong>de</strong> Mohammed al-KhoW¿irizmi,<br />

matemático persa que vivió durante el siglo IX y alcanzó gran reputación por el <strong>en</strong>unciado <strong>de</strong> las reglas<br />

paso a paso para sumar, restar, multiplicar y dividir números <strong>de</strong>cimales; la traducción al latín <strong>de</strong>l apellido<br />

<strong>en</strong> la palabra algorismus <strong>de</strong>rivó posteriorm<strong>en</strong>te <strong>en</strong> algoritmo. Eucli<strong>de</strong>s, el gran matemático griego<br />

(<strong>de</strong>l siglo IV a.c.) que inv<strong>en</strong>tó un método para <strong>en</strong>contrar el máximo común divisor <strong>de</strong> dos números, se<br />

consi<strong>de</strong>ra con Al-Khowirizmi el otro gran padre <strong>de</strong> la algoritmia (ci<strong>en</strong>cia que trata <strong>de</strong> los <strong>algoritmos</strong>).<br />

El profesor Niklaus Wirth -inv<strong>en</strong>tor <strong>de</strong> Pascal, Modula-2 y Oberon- tituló uno <strong>de</strong> sus más famosos<br />

libros, Algoritmos + Estructuras <strong>de</strong> <strong>datos</strong> = Programas, significándonos que sólo se pue<strong>de</strong> llegar a<br />

realizar un bu<strong>en</strong> programa con el diseño <strong>de</strong> un algoritmo y una correcta <strong>estructura</strong> <strong>de</strong> <strong>datos</strong>. Esta ecuación<br />

será una <strong>de</strong> las hipótesis fundam<strong>en</strong>tales consi<strong>de</strong>radas <strong>en</strong> esta obra.<br />

La resolución <strong>de</strong> un problema exige el diseño <strong>de</strong> un algoritmo que resuelva el problema propuesto.<br />

I I I I I I<br />

Problema<br />

Diseño <strong>de</strong>l<br />

algoritmo<br />

Programa <strong>de</strong><br />

computadora<br />

I I I I<br />

Figura 1.13. Resolución <strong>de</strong> un problema.<br />

Los pasos para la resolución <strong>de</strong> un problema son:<br />

I. Diseño <strong>de</strong>l algoritmo que <strong>de</strong>scribe la secu<strong>en</strong>cia ord<strong>en</strong>ada <strong>de</strong> pasos -sin ambigüeda<strong>de</strong>s- que<br />

conduc<strong>en</strong> a la solución <strong>de</strong> un problema dado. (Andisis <strong>de</strong>l problema y <strong>de</strong>sarrollo <strong>de</strong>l algoritmo.)<br />

2. Expresar el algoritmo como un programa <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación a<strong>de</strong>cuado. (Fase <strong>de</strong><br />

codificación.)<br />

3. Ejecución y validación <strong>de</strong>l programa por la computadora.<br />

Para llegar a la realización <strong>de</strong> un programa es necesario el diseño previo <strong>de</strong> un algoritmo, <strong>de</strong> modo<br />

que sin algoritmo no pue<strong>de</strong> existir un programa.<br />

Los <strong>algoritmos</strong> son in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes tanto <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> programación <strong>en</strong> que se expresan como <strong>de</strong><br />

la computadora que los ejecuta. En cada problema el algoritmo se pue<strong>de</strong> expresar <strong>en</strong> un l<strong>en</strong>guaje difer<strong>en</strong>te<br />

<strong>de</strong> programación y ejecutarse <strong>en</strong> una computadora distinta; sin embargo, el algoritmo será siempre<br />

el mismo. Así, por ejemplo, <strong>en</strong> una analogía con la vida diaria, una receta <strong>de</strong> un plato <strong>de</strong> cocina se<br />

pue<strong>de</strong> expresar <strong>en</strong> español, inglés o francés, pero cualquiera que sea el l<strong>en</strong>guaje, los pasos para la elaboración<br />

<strong>de</strong>l plato se realizarán sin importar el idioma <strong>de</strong>l cocinero.<br />

En la ci<strong>en</strong>cia <strong>de</strong> la computación y <strong>en</strong> la programación, los <strong>algoritmos</strong> son más importantes que los<br />

l<strong>en</strong>guajes <strong>de</strong> programación o las computadoras. Un l<strong>en</strong>guaje <strong>de</strong> programación es tan sólo un medio para<br />

expresar un algoritmo y una computadora es sólo un procesador para ejecutarlo. Tanto el l<strong>en</strong>guaje <strong>de</strong><br />

programación como la computadora son los medios para obt<strong>en</strong>er un fin: conseguir que el algoritmo se<br />

ejecute y se efectúe el proceso correspondi<strong>en</strong>te.


16 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Dada la importancia <strong>de</strong>l algoritmo <strong>en</strong> la ci<strong>en</strong>cia <strong>de</strong> la computación, un aspecto muy importante será<br />

el diseño <strong>de</strong> <strong>algoritmos</strong>. A la <strong>en</strong>señanza y práctica <strong>de</strong> esta tarea se <strong>de</strong>dica gran parte <strong>de</strong> este libro.<br />

El diseño <strong>de</strong> la mayoría <strong>de</strong> los <strong>algoritmos</strong> requiere creatividad y conocimi<strong>en</strong>tos profundos <strong>de</strong> la técnica<br />

<strong>de</strong> la programación. En es<strong>en</strong>cia, la solución <strong>de</strong> un problema se pue<strong>de</strong> expresar mediante un algoritmo.<br />

1.3.1. Características <strong>de</strong> los <strong>algoritmos</strong><br />

Las características fundam<strong>en</strong>tales que <strong>de</strong>be cumplir todo algoritmo son:<br />

Un algoritmo <strong>de</strong>be ser preciso e indicar el ord<strong>en</strong> <strong>de</strong> realización <strong>de</strong> cada paso.<br />

Un algoritmo <strong>de</strong>be estar <strong>de</strong>finido. Si se sigue un algoritmo dos veces, se <strong>de</strong>be obt<strong>en</strong>er el mismo<br />

resultado cada vez.<br />

Un algoritmo <strong>de</strong>be ser$nito. Si se sigue un algoritmo, se <strong>de</strong>be terminar <strong>en</strong> algún mom<strong>en</strong>to; o sea,<br />

<strong>de</strong>be t<strong>en</strong>er un número finito <strong>de</strong> pasos.<br />

La <strong>de</strong>finición <strong>de</strong> un algoritmo <strong>de</strong>be <strong>de</strong>scribir tres partes: Entrada, Proceso y Salida. En el algoritmo<br />

<strong>de</strong> receta <strong>de</strong> cocina citado anteriorm<strong>en</strong>te se t<strong>en</strong>drá:<br />

Entrada: ingredi<strong>en</strong>tes y ut<strong>en</strong>silios empleados.<br />

Proceso: elaboración <strong>de</strong> la receta <strong>en</strong> la cocina.<br />

Salida: terminación <strong>de</strong>l plato (por ejemplo, cor<strong>de</strong>ro).<br />

Ejemplo 1.1<br />

Un cli<strong>en</strong>te ejecuta un pedido u una fábrica. La fábrica examina <strong>en</strong> su banco <strong>de</strong> <strong>datos</strong> la ficha <strong>de</strong>l cli<strong>en</strong>te,<br />

si el cli<strong>en</strong>te es solv<strong>en</strong>te <strong>en</strong>tonces la empresa acepta el pedido; <strong>en</strong> caso contrario, rechazará el pedido.<br />

Redactar el algoritmo correspondi<strong>en</strong>te.<br />

Los pasos <strong>de</strong>l algoritmo son:<br />

1. Inicio.<br />

2. Leer el pedido.<br />

3. Examinar la ficha <strong>de</strong>l cli<strong>en</strong>te.<br />

4. Si el cli<strong>en</strong>te es solv<strong>en</strong>te, aceptar pedido; <strong>en</strong> caso contrario, recha<br />

zar pedido.<br />

5. Fin.<br />

Ejemplo 1.2<br />

Se <strong>de</strong>sea diseñar un algoritmo para saber si un número es primo o no.<br />

Un número es primo si sólo pue<strong>de</strong> dividirse por sí mismo y por la unidad (es <strong>de</strong>cir, no ti<strong>en</strong>e más divisores<br />

que él mismo y la unidad). Por ejemplo, 9,8,6,4, 12, 16,20, etc., no son primos, ya que son divisibles<br />

por números distintos a ellos mismos y a la unidad. Así, 9 es divisible por 3, 8 lo es por 2, etc. El<br />

algoritmo <strong>de</strong> resolución <strong>de</strong>l problema pasa por dividir sucesivam<strong>en</strong>te el número por 2, 3,4 ..., etc.<br />

1. Inicio.<br />

2. Poner X igual a 2 (X = 2, X variable que repres<strong>en</strong>ta a los divisores <strong>de</strong>l<br />

número que se busca N).<br />

3. Dividir N por X (N/X).<br />

4. Si el resultado <strong>de</strong> N/X es <strong>en</strong>tero, <strong>en</strong>tonces N no es un número primo y<br />

bifurcar al punto 7; <strong>en</strong> caso contrario, continuar el proceso.<br />

5. Suma 1 a X (X c X + 1).


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 17<br />

6. Si X es igual a N, <strong>en</strong>tonces N es un número primo; <strong>en</strong> caso contrario,<br />

bifurcar al punto 3.<br />

7. Fin.<br />

Por ejemplo, si N es 13 1, los pasos anteriores serían:<br />

1. Inicio.<br />

2. x = 2.<br />

3. 131/X. Como el resultado no es <strong>en</strong>tero, se continúa el proceso.<br />

5. X t 2 + 1, luego X = 3.<br />

6. Como X no es 131, se bifurca al punto 3.<br />

3. 131/X resultado no es <strong>en</strong>tero.<br />

5. x t 3 + 1, x = 4.<br />

6. Como X no es 131 bifurca al punto 3.<br />

3. 131/X ..., etc.<br />

7. Fin.<br />

Ejemplo 1.3<br />

Realizar la suma <strong>de</strong> todos los números pares <strong>en</strong>tre 2 y 1000.<br />

El problema consiste <strong>en</strong> sumar 2 + 4 + 6 + 8 .. . + 1000 . Utilizaremos las palabras SUMA<br />

y NUMERO (variables, serán d<strong>en</strong>ominadas más tar<strong>de</strong>) para repres<strong>en</strong>tar las sumas sucesivas ( 2 +4 ,<br />

( 2 + 4 + 6 ) , ( 2 + 4 + 6 + 8 ) , etc. La solución se pue<strong>de</strong> escribir con el sigui<strong>en</strong>te algoritmo:<br />

1. Inicio.<br />

2. Establecer SUMA a O.<br />

3. Establecer NUMERO a 2.<br />

4. Sumar NUMERO a SUMA. El resultado será el nuevo valor <strong>de</strong> la suma (SUMA).<br />

5. Increm<strong>en</strong>tar NUMERO <strong>en</strong> 2 unida<strong>de</strong>s.<br />

6. Si NUMERO =< 1000 bifurcar al paso 4 ; <strong>en</strong> caso contrario, escribir el<br />

ultimo valor <strong>de</strong> SUMA y terminar el proceso.<br />

7. Fin.<br />

1.4. EL SOFTWARE (LOS PROGRAMAS)<br />

Las operaciones que <strong>de</strong>be realizar el hardware son especificadas por una lista <strong>de</strong> instrucciones, Ilamadas<br />

programas, o software. El software se divi<strong>de</strong> <strong>en</strong> dos gran<strong>de</strong>s grupos: sofnvare <strong>de</strong>l sistema y software<br />

<strong>de</strong> aplicaciones.<br />

El software <strong>de</strong>l sistema es el conjunto <strong>de</strong> programas indisp<strong>en</strong>sables para que la máquina funcione; se<br />

d<strong>en</strong>ominan también programas <strong>de</strong>l sistema. Estos programas son, básicam<strong>en</strong>te, el sistema operativo, los<br />

editores <strong>de</strong> texto, los compiladores/intérpretes (l<strong>en</strong>guajes <strong>de</strong> programación) y los programas <strong>de</strong> utilidad.<br />

Uno <strong>de</strong> los programas más importante es el sistema operativo, que sirve, es<strong>en</strong>cialm<strong>en</strong>te, para facilitar<br />

la escritura y uso <strong>de</strong> sus propios programas. El sistema operativo dirige las operaciones globales <strong>de</strong><br />

la computadora, instruye a la computadora para ejecutar otros programas y controla el almac<strong>en</strong>ami<strong>en</strong>to<br />

y recuperación <strong>de</strong> archivos (programas y <strong>datos</strong>) <strong>de</strong> cintas y discos. Gracias al sistema operativo es<br />

posible que el programador pueda introducir y grabar nuevos programas, así como instruir a la computadora<br />

para que los ejecute. Los sistemas operativos pued<strong>en</strong> ser: monousuarios (un solo usuario) y multiusuarios,<br />

o tiempo compartido (difer<strong>en</strong>tes usuarios), at<strong>en</strong>di<strong>en</strong>do al número <strong>de</strong> usuarios y monocarga<br />

(una sola tarea) o multitarea (múltiples tareas) según las tareas (procesos) que pue<strong>de</strong> realizar simultáneam<strong>en</strong>te.<br />

C corre prácticam<strong>en</strong>te <strong>en</strong> todos los sistemas operativos, Windows 95, Windows NT, Windows<br />

2000, UNIX, Lynux.. ., y <strong>en</strong> casi todas las computadoras personales actuales PC, Mac, Sun, etc.<br />

Los l<strong>en</strong>guajes <strong>de</strong> programación sirv<strong>en</strong> para escribir programas que permitan la comunicación usuario/máquina.<br />

Unos programas especiales llamados traductores (compiladores o intérpretes) convier-


18 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Figura 1.14. Difer<strong>en</strong>tes programas <strong>de</strong> software.<br />

t<strong>en</strong> las instrucciones escritas <strong>en</strong> l<strong>en</strong>guajes <strong>de</strong> programación <strong>en</strong> instrucciones escritas <strong>en</strong> l<strong>en</strong>guajes máquina<br />

(O y 1, bits) que ésta pueda <strong>en</strong>t<strong>en</strong><strong>de</strong>r.<br />

Los programas <strong>de</strong> utilidad' facilitan el uso <strong>de</strong> la computadora. Un bu<strong>en</strong> ejemplo es un editor <strong>de</strong><br />

textos que permite la escritura y edición <strong>de</strong> docum<strong>en</strong>tos. Este libro ha sido escrito <strong>en</strong> un editor <strong>de</strong> textos<br />

o procesador <strong>de</strong> palabras («wordprocesor»).<br />

Los programas que realizan tareas concretas, nóminas, contabilidad, análisis estadístico, etc. es<br />

<strong>de</strong>cir, los programas que podrá escribir <strong>en</strong> C, se d<strong>en</strong>ominan programas <strong>de</strong> aplicación. A lo largo <strong>de</strong>l<br />

libro se verán pequeños programas <strong>de</strong> aplicación que muestran los principios <strong>de</strong> una bu<strong>en</strong>a programación<br />

<strong>de</strong> computadora.<br />

Se <strong>de</strong>be difer<strong>en</strong>ciar <strong>en</strong>tre el acto <strong>de</strong> crear un programa y la acción <strong>de</strong> la computadora cuando ejecuta<br />

las instrucciones <strong>de</strong>l programa. La creación <strong>de</strong> un programa se hace inicialm<strong>en</strong>te <strong>en</strong> papel y a continuación<br />

se introduce <strong>en</strong> la computadora y se convierte <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>t<strong>en</strong>dible por la computadora.<br />

Figura 1.15. Relación <strong>en</strong>tre programas <strong>de</strong> aplicación y programas <strong>de</strong>l sistema.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación<br />

19<br />

Terminal<br />

Memoria<br />

externa<br />

r- - ---<br />

1 UCP<br />

I<br />

1<br />

-2<br />

L--_<br />

- 1<br />

Programador<br />

Datos <strong>de</strong> salida<br />

(rociiitadocl<br />

externos<br />

Figura 1.16. Acción <strong>de</strong> un programador<br />

La Figura 1.16 muestra el proceso g<strong>en</strong>eral <strong>de</strong> ejecución <strong>de</strong> un programa: aplicación <strong>de</strong> una <strong>en</strong>trada<br />

(<strong>datos</strong>) al programa y obt<strong>en</strong>ción <strong>de</strong> una salida (re.sultados). La <strong>en</strong>trada pue<strong>de</strong> t<strong>en</strong>er una variedad <strong>de</strong> formas,<br />

tales como números o caracteres alfabéticos. La salida pue<strong>de</strong> también t<strong>en</strong>er formas, tales como<br />

<strong>datos</strong> numéricos o caracteres, señales para controlar equipos o robots, etc.<br />

La ejecución <strong>de</strong> un programa requiere -g<strong>en</strong>eralm<strong>en</strong>te- unos <strong>datos</strong> como <strong>en</strong>trada (Fig. 1.17), a<strong>de</strong>más<br />

<strong>de</strong>l propio programa, para po<strong>de</strong>r producir una salida.<br />

UCP<br />

Memoria<br />

externa<br />

'-<br />

I1 IIIIIIIIIIIIII<br />

Entrada<br />

Salida<br />

(<strong>datos</strong>)<br />

(resultados)<br />

Figura 1.17. Ejecución <strong>de</strong> un programa<br />

I .5. LOS LENGUAJES DE PROGRAMACI~N<br />

Como se ha visto <strong>en</strong> el apartado anterior, para que un procesador realice un proceso se le <strong>de</strong>be suministrar<br />

<strong>en</strong> primer lugar un algoritmo a<strong>de</strong>cuado. El procesador <strong>de</strong>be ser capaz <strong>de</strong> interpretar el algoritmo,<br />

lo que significa:<br />

Compr<strong>en</strong><strong>de</strong>r las instrucciones <strong>de</strong> cada paso.<br />

Realizar las operaciones correspondi<strong>en</strong>tes.<br />

Cuando el procesador es una computadora, el algoritmo se ha <strong>de</strong> expresar <strong>en</strong> un formato que se<br />

d<strong>en</strong>omina programa. Un programa se escribe <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación y las operaciones que<br />

conduc<strong>en</strong> a expresar un algoritmo <strong>en</strong> forma <strong>de</strong> programa se llaman programación. Así pues, los l<strong>en</strong>guajes<br />

utilizados para escribir programas <strong>de</strong> computadoras son los l<strong>en</strong>guajes <strong>de</strong> programación y programadores<br />

son los escritores y diseñadores <strong>de</strong> programas.


P<br />

20 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Los principales tipos <strong>de</strong> l<strong>en</strong>guajes utilizados <strong>en</strong> la actualidad son tres:<br />

L<strong>en</strong>guajes máquina.<br />

L<strong>en</strong>guaje <strong>de</strong> bajo nivel (<strong>en</strong>samblador).<br />

L<strong>en</strong>guajes <strong>de</strong> alto nivel.<br />

1.5.1. Instrucciones a la computadora<br />

Los difer<strong>en</strong>tes pasos (acciones) <strong>de</strong> un algoritmo se expresan <strong>en</strong> los programas como instrucciones, s<strong>en</strong>t<strong>en</strong>cias<br />

o proposiciones (normalm<strong>en</strong>te el término instrucción se suele referir a los l<strong>en</strong>guajes máquina y<br />

bajo nivel, reservando la s<strong>en</strong>t<strong>en</strong>cia o proposición para los l<strong>en</strong>guajes <strong>de</strong> alto nivel). Por consigui<strong>en</strong>te, un<br />

programa consta <strong>de</strong> una secu<strong>en</strong>cia <strong>de</strong> instrucciones, cada una <strong>de</strong> las cuales especifica ciertas operaciones<br />

que <strong>de</strong>be ejecutar la computadora.<br />

La elaboración <strong>de</strong> un programa requerirá conocer el juego o repertorio <strong>de</strong> instrucciones <strong>de</strong>l l<strong>en</strong>guaje.<br />

Aunque <strong>en</strong> el Capítulo 3 se analizarán con más <strong>de</strong>talle las instrucciones, a<strong>de</strong>lantaremos los tipos fundam<strong>en</strong>tales<br />

<strong>de</strong> instrucciones que una computadora es capaz <strong>de</strong> manipular y ejecutar. Las instrucciones<br />

básicas y comunes a casi todos los l<strong>en</strong>guajes <strong>de</strong> programación se pued<strong>en</strong> cond<strong>en</strong>sar <strong>en</strong> cuatro grupos:<br />

Instrucciones <strong>de</strong> <strong>en</strong>rraúdsalidu. Instrucciones <strong>de</strong> transfer<strong>en</strong>cia <strong>de</strong> información y <strong>datos</strong> <strong>en</strong>tre dispositivos<br />

periféricos (teclado, impresora, unidad <strong>de</strong> disco, etc.) y la memoria c<strong>en</strong>tral.<br />

lnstrucciones aritmético-lógicas. Instrucciones que ejecutan operaciones aritméticas (suma, resta,<br />

multiplicación, división, pot<strong>en</strong>ciación), lógicas (operaciones and, or, not, etc.).<br />

Instrucciones selectivas. Instrucciones que permit<strong>en</strong> la selección <strong>de</strong> tareas alternativas <strong>en</strong> función<br />

<strong>de</strong> los resultados <strong>de</strong> difer<strong>en</strong>tes expresiones condicionales.<br />

0 Instrucciones repetitivas. Instrucciones que permit<strong>en</strong> la repetición <strong>de</strong> secu<strong>en</strong>cias <strong>de</strong> instrucciones<br />

un número <strong>de</strong>terminado <strong>de</strong> veces.<br />

1.5.2. L<strong>en</strong>guajes máquina<br />

Los l<strong>en</strong>guajes máquina son aquellos que están escritos <strong>en</strong> l<strong>en</strong>guajes directam<strong>en</strong>te inteligibles por la<br />

máquina (computadora), ya que sus instrucciones son cad<strong>en</strong>as binarias (cad<strong>en</strong>as o series <strong>de</strong> caracteres<br />

-dígitos- O y 1) que especifican una operación, y las posiciones (dirección) <strong>de</strong> memoria implicadas<br />

<strong>en</strong> la operación se d<strong>en</strong>ominan instrucciones <strong>de</strong> máquina o código máquina. El código máquina es el<br />

conocido código binario.<br />

Las instrucciones <strong>en</strong> l<strong>en</strong>guaje máquina <strong>de</strong>p<strong>en</strong>d<strong>en</strong> <strong>de</strong>l hardware <strong>de</strong> la computadora y, por tanto, diferirán<br />

<strong>de</strong> una computadora a otra. El l<strong>en</strong>guaje máquina <strong>de</strong> un PC (computadora personal) será difer<strong>en</strong>te<br />

<strong>de</strong> un sistema HP (Hewlett Packard), Compaq o un sistema <strong>de</strong> IBM.<br />

Las v<strong>en</strong>tajas <strong>de</strong> programar <strong>en</strong> l<strong>en</strong>guaje máquina son la posibilidad <strong>de</strong> cargar (transferir un programa<br />

a la memoria) sin necesidad <strong>de</strong> traducción posterior, lo que supone una velocidad <strong>de</strong> ejecución superior<br />

a cualquier otro l<strong>en</strong>guaje <strong>de</strong> programación.<br />

Los inconv<strong>en</strong>i<strong>en</strong>tes -<strong>en</strong> la actualidad- superan a las v<strong>en</strong>tajas, lo que hace prácticam<strong>en</strong>te no recom<strong>en</strong>dables<br />

los l<strong>en</strong>guajes máquina. Estos inconv<strong>en</strong>i<strong>en</strong>tes son:<br />

Dificultad y l<strong>en</strong>titud <strong>en</strong> la codificación.<br />

Poca fiabilidad.<br />

Dificultad gran<strong>de</strong> <strong>de</strong> verificar y poner a punto los programas.<br />

Los programas sólo son ejecutables <strong>en</strong> el mismo procesador (UPC, ünidad C<strong>en</strong>tral <strong>de</strong> Proceso).<br />

Para evitar los l<strong>en</strong>guajes máquina, <strong>de</strong>s<strong>de</strong> el punto <strong>de</strong> vista <strong>de</strong>l usuario, se han creado otros l<strong>en</strong>guajes<br />

que permit<strong>en</strong> escribir programas con instrucciones similares al l<strong>en</strong>guaje humano (por <strong>de</strong>sgracia casi<br />

siempre inglés, aunque exist<strong>en</strong> raras excepciones, como es el caso <strong>de</strong> las versiones españolas <strong>de</strong>l l<strong>en</strong>guaje<br />

LOGO). Estos l<strong>en</strong>guajes se d<strong>en</strong>ominan <strong>de</strong> alto y hujo nivel.


1.5.3. L<strong>en</strong>guajes <strong>de</strong> bajo nivel<br />

Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 21<br />

Los l<strong>en</strong>guajes <strong>de</strong> bajo nivel son más fáciles <strong>de</strong> utilizar que los l<strong>en</strong>guajes máquina, pero, al igual, que<br />

ellos, <strong>de</strong>p<strong>en</strong>d<strong>en</strong> <strong>de</strong> la máquina <strong>en</strong> particular. El l<strong>en</strong>guaje <strong>de</strong> bajo nivel por excel<strong>en</strong>cia es el <strong>en</strong>sumhlacfor<br />

(assembly languuje). Las instrucciones <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador son instrucciones conocidas como<br />

nernotécnicos (mnemonics). Por ejemplo, nemotécnicos típicos <strong>de</strong> operaciones aritméticas son: <strong>en</strong><br />

inglés, ADD, SUB, DIV, etc.; <strong>en</strong> español, SUM, RES, DIV, etc.<br />

Una instrucción típica <strong>de</strong> suma sería:<br />

ADD M, N, P<br />

Esta instrucción podía significar «.sutnar el número cont<strong>en</strong>ido e~i la posicicín <strong>de</strong> memoria M u1 niimero<br />

almac<strong>en</strong>ado <strong>en</strong> la posicicín <strong>de</strong> memoria N y situar el resultado <strong>en</strong> la posicicín <strong>de</strong> memoria P ». Evid<strong>en</strong>tem<strong>en</strong>te,<br />

es mucho más s<strong>en</strong>cillo recordar la instrucción anterior con un nemotécnico que su equival<strong>en</strong>te<br />

<strong>en</strong> código máquina:<br />

0110 1001 1010 1011<br />

Un programa escrito <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador no pue<strong>de</strong> ser ejecutado directam<strong>en</strong>te por la coinputadora<br />

-<strong>en</strong> esto se difer<strong>en</strong>cia es<strong>en</strong>cialm<strong>en</strong>te <strong>de</strong>l l<strong>en</strong>guaje máquina-, sino que requiere una fase <strong>de</strong> traduccicín<br />

al l<strong>en</strong>guaje máquina.<br />

El programa original escrito <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador se d<strong>en</strong>omina programa fu<strong>en</strong>te y el programa<br />

traducido <strong>en</strong> l<strong>en</strong>guaje máquina se conoce como programa objero, ya directam<strong>en</strong>te inteligible por la<br />

computadora.<br />

El traductor <strong>de</strong> programas fu<strong>en</strong>te a objeto es un programa llamado <strong>en</strong>samhludor (assemhler),<br />

exist<strong>en</strong>te <strong>en</strong> casi todas las computadoras (Fig. 1.18).<br />

No se <strong>de</strong>be confundir -aunque <strong>en</strong> español adoptan el mismo nombre- el programa <strong>en</strong>samhlador<br />

(assembler), <strong>en</strong>cargado <strong>de</strong> efectuar la traducción <strong>de</strong>l programa fu<strong>en</strong>te escrito a l<strong>en</strong>guaje máquina, con<br />

el l<strong>en</strong>guaje <strong>en</strong>samhlador (assembly languaje), l<strong>en</strong>guaje <strong>de</strong> programación con una <strong>estructura</strong> y grainática<br />

<strong>de</strong>finidas.<br />

Los l<strong>en</strong>guajes <strong>en</strong>sambladores pres<strong>en</strong>tan la v<strong>en</strong>tuju fr<strong>en</strong>te a los l<strong>en</strong>guajes máquina <strong>de</strong> su mayor facilidad<br />

<strong>de</strong> codificación y, <strong>en</strong> g<strong>en</strong>eral, su velocidad <strong>de</strong> cálculo.<br />

Programa fu<strong>en</strong>te <strong>en</strong><br />

<strong>en</strong>sam blador<br />

(assembly)<br />

Figura 1.18. Programa <strong>en</strong>samblador.<br />

I<br />

3rna oDjero <strong>en</strong><br />

iáquina<br />

Los inconv<strong>en</strong>i<strong>en</strong>tes más notables <strong>de</strong> los l<strong>en</strong>guajes <strong>en</strong>sambladores son:<br />

Dep<strong>en</strong>d<strong>en</strong>cia total <strong>de</strong> la máquina, lo que impi<strong>de</strong> la transportabilidad <strong>de</strong> los programas (posibilidad<br />

<strong>de</strong> ejecutar un programa <strong>en</strong> difer<strong>en</strong>tes máquinas). El l<strong>en</strong>guaje <strong>en</strong>samblador <strong>de</strong>l PC es distinto<br />

<strong>de</strong>l l<strong>en</strong>guaje <strong>en</strong>samblador <strong>de</strong>l Apple Macintosh.<br />

La formación <strong>de</strong> los programas es más compleja que la correspondi<strong>en</strong>te a los programadores <strong>de</strong><br />

alto nivel, ya que exige no sólo las técnicas <strong>de</strong> programación, sino también el conocimi<strong>en</strong>to <strong>de</strong>l<br />

interior <strong>de</strong> la máquina.<br />

Hoy día los l<strong>en</strong>guajes <strong>en</strong>sambladores ti<strong>en</strong>e sus aplicaciones muy reducidas <strong>en</strong> la programación <strong>de</strong><br />

aplicaciones y se c<strong>en</strong>tran <strong>en</strong> aplicaciones <strong>de</strong> tiempo real, control <strong>de</strong> procesos y <strong>de</strong> dispositivos electrónicos,<br />

etc.


22 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1.5.4. L<strong>en</strong>guajes <strong>de</strong> alto nivel<br />

Los l<strong>en</strong>guajes <strong>de</strong> alto nivel son los más utilizados por los programadores. Están diseñados para que las<br />

personas escriban y <strong>en</strong>ti<strong>en</strong>dan los programas <strong>de</strong> un modo mucho más fácil que los l<strong>en</strong>guajes máquina<br />

y <strong>en</strong>sambladores. Otra razón es que un programa escrito <strong>en</strong> l<strong>en</strong>guaje <strong>de</strong> alto nivel es in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te <strong>de</strong><br />

la máquina; esto es, las instrucciones <strong>de</strong>l programa <strong>de</strong> la computadora no <strong>de</strong>p<strong>en</strong>d<strong>en</strong> <strong>de</strong>l diseño <strong>de</strong>l<br />

hurdware o <strong>de</strong> una computadora <strong>en</strong> particular. En consecu<strong>en</strong>cia, los programas escritos <strong>en</strong> l<strong>en</strong>guaje<br />

<strong>de</strong> alto nivel son portables o transportables, lo que significa la posibilidad <strong>de</strong> po<strong>de</strong>r ser ejecutados con<br />

poca o ninguna modificación <strong>en</strong> difer<strong>en</strong>tes tipos <strong>de</strong> computadoras; al contrario que los programas <strong>en</strong><br />

l<strong>en</strong>guaje máquina o <strong>en</strong>samblador, que sólo se pued<strong>en</strong> ejecutar <strong>en</strong> un <strong>de</strong>terminado tipo <strong>de</strong> computadora.<br />

Los l<strong>en</strong>guajes <strong>de</strong> alto nivel pres<strong>en</strong>tan las sigui<strong>en</strong>tes v<strong>en</strong>tajas:<br />

El tiempo <strong>de</strong> formación <strong>de</strong> los programadores es relativam<strong>en</strong>te corto comparado con otros l<strong>en</strong>guajes.<br />

La escritura <strong>de</strong> programas se basa <strong>en</strong> reglas sintácticas similares a los l<strong>en</strong>guajes humanos. Nombres<br />

<strong>de</strong> las instrucciones, tales como READ, WRITE:, PRINT, OPEN, etc.<br />

Las modificaciones y puestas a punto <strong>de</strong> los programas son más fáciles.<br />

Reducción <strong>de</strong>l coste <strong>de</strong> los programas.<br />

Transportabilidad.<br />

Los inconv<strong>en</strong>i<strong>en</strong>tes se concretan <strong>en</strong>:<br />

Increm<strong>en</strong>to <strong>de</strong>l tiempo <strong>de</strong> puesta a punto, al necesitarse difer<strong>en</strong>tes traducciones <strong>de</strong>l programa<br />

fu<strong>en</strong>te para conseguir el programa <strong>de</strong>finitivo.<br />

No se aprovechan los recursos internos <strong>de</strong> la máquina, que se explotan mucho mejor <strong>en</strong> l<strong>en</strong>guajes<br />

máquina y <strong>en</strong>sambladores.<br />

Aum<strong>en</strong>to <strong>de</strong> la ocupación <strong>de</strong> memoria.<br />

El tiempo <strong>de</strong> ejecución <strong>de</strong> los programas es mucho mayor.<br />

AI igual que suce<strong>de</strong> con los l<strong>en</strong>guajes <strong>en</strong>sambladores, los programas fu<strong>en</strong>te ti<strong>en</strong><strong>en</strong> que ser traducidos<br />

por los programas traductores, llamados <strong>en</strong> este caso compiladores e intérpretes.<br />

Los l<strong>en</strong>guajes <strong>de</strong> programación <strong>de</strong> alto nivel exist<strong>en</strong>tes hoy son muy numerosos aunque la práctica<br />

<strong>de</strong>muestra que su uso mayoritario se reduce a<br />

C C++ # COBOL FORTRAN Pascal Visual BASIC Java<br />

están muy ext<strong>en</strong>didos:<br />

Ada-95 Modula-2 Prolog LISP Smalltalk Eiffel<br />

son <strong>de</strong> gran uso <strong>en</strong> el mundo profesional:<br />

Borland Delphi C++ Buil<strong>de</strong>r Power Buil<strong>de</strong>r<br />

Aunque hoy día el mundo Internet consume gran cantidad <strong>de</strong> recursos <strong>en</strong> forma <strong>de</strong> l<strong>en</strong>guajes <strong>de</strong><br />

programación tales como HTML, XML, JavaScript,. . .<br />

1.5.5. Traductores <strong>de</strong> l<strong>en</strong>guaje<br />

Los traducrores <strong>de</strong> l<strong>en</strong>guaje son programas que traduc<strong>en</strong> a su vez los programas fu<strong>en</strong>te escritos <strong>en</strong> l<strong>en</strong>guajes<br />

<strong>de</strong> alto nivel a código máquina.


Los traductores se divid<strong>en</strong> <strong>en</strong>:<br />

Intérpretes.<br />

Compiladores.<br />

7.5.5.7. Intérpretes<br />

Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 23<br />

Un intérprete es un traductor que toma un programa fu<strong>en</strong>te, lo traduce y a continuación lo ejecuta.<br />

Los programas intérpretes clásicos como BASIC. prácticam<strong>en</strong>te ya no se utilizan, aunque las versiones<br />

Qbasic y QuickBASIC todavía se pued<strong>en</strong> <strong>en</strong>contrar y corr<strong>en</strong> <strong>en</strong> las computadoras personales. Sin embargo,<br />

está muy ext<strong>en</strong>dida la versión interpretada <strong>de</strong>l l<strong>en</strong>guaje Smalltalk, un l<strong>en</strong>guaje ori<strong>en</strong>tado a objetos<br />

puro.<br />

Programa fu<strong>en</strong>te<br />

Programa fu<strong>en</strong>te<br />

Intérprete<br />

Cornpilador<br />

Traducción y ejecución<br />

Línea a línea<br />

Programa objeto<br />

Figura 1.19. Intérprete.<br />

Figura 1.20. La compilación <strong>de</strong> programas.<br />

1.5.5.2. Compiladores<br />

Un compilador es un programa que traduce los programas fu<strong>en</strong>te escritos <strong>en</strong> l<strong>en</strong>guaje <strong>de</strong> alto nivel<br />

-C, FORTRAN ...- a l<strong>en</strong>guaje máquina.<br />

Los programas escritos <strong>en</strong> l<strong>en</strong>guaje <strong>de</strong> alto nivel se llaman programas.fu<strong>en</strong>te y el programa traducido<br />

programa objeto o código objeto. El compilador traduce -s<strong>en</strong>t<strong>en</strong>cia a s<strong>en</strong>t<strong>en</strong>cia- el programa<br />

fu<strong>en</strong>te. Los l<strong>en</strong>guajes compiladores típicos son: C, C++, Pascal, Java y COBOL.<br />

1.5.6. La compilación y sus fases<br />

La conzpilación es el proceso <strong>de</strong> traducción <strong>de</strong> programas fu<strong>en</strong>te a programas objeto. El programa objeto<br />

obt<strong>en</strong>ido <strong>de</strong> la compilación ha sido traducido normalm<strong>en</strong>te a código máquina.<br />

Para conseguir el programa máquina real se <strong>de</strong>be utilizar un programa llamado montador o <strong>en</strong>lazador<br />

(linker). El proceso <strong>de</strong> montaje conduce a un programa <strong>en</strong> l<strong>en</strong>guaje máquina directam<strong>en</strong>te ejecutable<br />

(Fig. 1.21).<br />

El proceso <strong>de</strong> ejecución <strong>de</strong> un programa escrito <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación y mediante un<br />

compilador suele t<strong>en</strong>er los sigui<strong>en</strong>tes pasos:<br />

1. Escritura <strong>de</strong>l progranza~fu<strong>en</strong>te con un editor (programa que permite a una computadora actuar<br />

<strong>de</strong> modo similar a una máquina <strong>de</strong> escribir electrónica) y guardarlo <strong>en</strong> un dispositivo <strong>de</strong> alrnac<strong>en</strong>ami<strong>en</strong>to<br />

(por ejemplo, un disco).<br />

Figura 1.21. Fases <strong>de</strong> la compilación.


24 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.<br />

3.<br />

4.<br />

5. Obt<strong>en</strong>ción <strong>de</strong>l programa objeto.<br />

6.<br />

7.<br />

Introducir el programa fu<strong>en</strong>te <strong>en</strong> memoria.<br />

Compilar el programa con el compilador C.<br />

Verijcar y corregir errores <strong>de</strong> compilación (listado <strong>de</strong> errores).<br />

El <strong>en</strong>lazador (linker) obti<strong>en</strong>e el programa ejecutable.<br />

Se ejecuta el programa y, si no exist<strong>en</strong> errores, se t<strong>en</strong>drá la salida <strong>de</strong>l programa.<br />

El proceso <strong>de</strong> ejecución sería el mostrado <strong>en</strong> las Figuras 1.22 y 1.23. En el Capítulo 3 se <strong>de</strong>scribirá<br />

<strong>en</strong> <strong>de</strong>talle el proceso completo y específico <strong>de</strong> ejecución <strong>de</strong> programas <strong>en</strong> l<strong>en</strong>guaje C.<br />

- Programa Computadora<br />

Ejecutable<br />

Programa<br />

Resultados<br />

Figura 1.22. Ejecución <strong>de</strong> un programa.<br />

Modificación<br />

programa<br />

fu<strong>en</strong>te<br />

Programa<br />

fu<strong>en</strong>te<br />

$.<br />

Compilador<br />

errores <strong>en</strong> la<br />

u<br />

Programa<br />

rq--y<br />

ejecutable<br />

I<br />

I<br />

I Ejecución<br />

I<br />

Figura 1.23. Fases <strong>de</strong> ejecución <strong>de</strong> un programa.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación<br />

1.6. EL LENGUAJE C: HISTORIA Y CARACTERISTICAS<br />

C es el l<strong>en</strong>guaje <strong>de</strong> programación <strong>de</strong> propósito g<strong>en</strong>eral asociado, <strong>de</strong> modo universal, al sistema operativo<br />

UNIX. Sin embargo, la popularidad, eficacia y pot<strong>en</strong>cia <strong>de</strong> C, se ha producido porque este l<strong>en</strong>guaje<br />

no está prácticam<strong>en</strong>te asociado a ningún sistema operativo, ni a ninguna máquina, <strong>en</strong> especial. Ésta<br />

es la razón fundam<strong>en</strong>tal, por la cual C, es conocido como el l<strong>en</strong>guaje <strong>de</strong> programación <strong>de</strong> sistemas, por<br />

excel<strong>en</strong>cia.<br />

C es una evolución <strong>de</strong> los l<strong>en</strong>guajes BCPL -<strong>de</strong>sarrollado por Martin Richards- y B -<strong>de</strong>sarrollado<br />

por K<strong>en</strong> Thompson <strong>en</strong> 1970- para el primitivo UNIX <strong>de</strong> la computadora DEC PDP-7.<br />

C nació realm<strong>en</strong>te <strong>en</strong> 1978, con la publicación <strong>de</strong> The C Programming Languaje, por Brian Kernighan<br />

y D<strong>en</strong>nis Ritchie (Pr<strong>en</strong>tice Hall, 1978). Des<strong>de</strong> su nacimi<strong>en</strong>to, C fue creci<strong>en</strong>do <strong>en</strong> popularidad y<br />

los sucesivos cambios <strong>en</strong> el l<strong>en</strong>guaje a lo largo <strong>de</strong> los años junto a la creación <strong>de</strong> compiladores por grupos<br />

no involucrados <strong>en</strong> su diseño, hicieron necesario p<strong>en</strong>sar <strong>en</strong> la estandarización <strong>de</strong> la <strong>de</strong>finición <strong>de</strong>l<br />

l<strong>en</strong>guaje C.<br />

Así, <strong>en</strong> 1983, el American National Estándar Institute (ANSI), una organización internacional <strong>de</strong><br />

estandarización, creó un comité (el d<strong>en</strong>ominado X3J11) cuya tarea fundam<strong>en</strong>tal consistía <strong>en</strong> hacer «una<br />

<strong>de</strong>finición no ambigua <strong>de</strong>l l<strong>en</strong>guaje C, e in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te <strong>de</strong> la máquina». Había nacido el estándar ANSI<br />

<strong>de</strong>l l<strong>en</strong>guaje C. Con esta <strong>de</strong>finición <strong>de</strong> C se asegura que cualquier fabricante <strong>de</strong> software que v<strong>en</strong><strong>de</strong> un<br />

compilador ANSI C incorpora todas las características <strong>de</strong>l l<strong>en</strong>guaje, especificadas por el estándar. Esto<br />

significa también que los programadores que escriban programas <strong>en</strong> C estándar t<strong>en</strong>drán la seguridad <strong>de</strong><br />

que correrán sus modificaciones <strong>en</strong> cualquier sistema que t<strong>en</strong>ga un compilador C.<br />

C es un l<strong>en</strong>guaje <strong>de</strong> alto nivel, que permite programar con instrucciones <strong>de</strong> l<strong>en</strong>guaje <strong>de</strong> propósito<br />

g<strong>en</strong>eral. También, C se <strong>de</strong>fine como un l<strong>en</strong>guaje <strong>de</strong> programación <strong>estructura</strong>do <strong>de</strong> propósito g<strong>en</strong>eral;<br />

aunque <strong>en</strong> su diseño también primó el hecho <strong>de</strong> que fuera especificado como un l<strong>en</strong>guaje <strong>de</strong> programación<br />

<strong>de</strong> Sistemas, lo que proporciona una <strong>en</strong>orme cantidad <strong>de</strong> pot<strong>en</strong>cia y flexibilidad.<br />

El estándar ANSI C formaliza construcciones no propuestas <strong>en</strong> la primera versión <strong>de</strong> C, <strong>en</strong> especial,<br />

asignación <strong>de</strong> <strong>estructura</strong>s y <strong>en</strong>umeraciones. Entre otras aportaciones, se <strong>de</strong>finió es<strong>en</strong>cialm<strong>en</strong>te, una nueva<br />

forma <strong>de</strong> <strong>de</strong>claración <strong>de</strong> funciones (prototipos). Pero, es es<strong>en</strong>cialm<strong>en</strong>te la biblioteca estándar <strong>de</strong> funciones,<br />

otra <strong>de</strong> las gran<strong>de</strong>s aportaciones.<br />

Hoy, <strong>en</strong> el siglo XXI, C sigue si<strong>en</strong>do uno <strong>de</strong> los l<strong>en</strong>guajes <strong>de</strong> programación más utilizados <strong>en</strong> la<br />

industria <strong>de</strong>l software, así como <strong>en</strong> institutos tecnológicos, escuelas <strong>de</strong> ing<strong>en</strong>iería y universida<strong>de</strong>s. Prácticam<strong>en</strong>te<br />

todos los fabricantes <strong>de</strong> sistemas operativos, UNIX, LINUX, MacOS, SOLARIS, ... soportan<br />

difer<strong>en</strong>tes tipos <strong>de</strong> compiladores <strong>de</strong> l<strong>en</strong>guaje C.<br />

1.6.1. V<strong>en</strong>tajas <strong>de</strong> C<br />

El l<strong>en</strong>guaje C ti<strong>en</strong>e una gran cantidad <strong>de</strong> v<strong>en</strong>tajas sobre otros l<strong>en</strong>guajes, y son, precisam<strong>en</strong>te la razón<br />

fundam<strong>en</strong>tal <strong>de</strong> que <strong>de</strong>spués <strong>de</strong> casi dos décadas <strong>de</strong> uso, C siga si<strong>en</strong>do uno <strong>de</strong> los l<strong>en</strong>guajes más populares<br />

y utilizados <strong>en</strong> empresas, organizaciones y fábricas <strong>de</strong> software <strong>de</strong> todo el mundo.<br />

Algunas v<strong>en</strong>tajas que justifican el uso todavía creci<strong>en</strong>te <strong>de</strong>l l<strong>en</strong>guaje C <strong>en</strong> la programación <strong>de</strong> computadoras<br />

son:<br />

El l<strong>en</strong>guaje C es po<strong>de</strong>roso y flexible, con órd<strong>en</strong>es, operaciones y funciones <strong>de</strong> biblioteca que se<br />

pued<strong>en</strong> utilizar para escribir la mayoría <strong>de</strong> los programas que corr<strong>en</strong> <strong>en</strong> la computadora.<br />

C se utiliza por programadores profesionales para <strong>de</strong>sarrollar software <strong>en</strong> la mayoría <strong>de</strong> los<br />

mo<strong>de</strong>rnos sistemas <strong>de</strong> computadora.<br />

Se pue<strong>de</strong> utilizar C para <strong>de</strong>sarrollar sistemas operativos, compiladores, sistemas <strong>de</strong> tiempo real y<br />

aplicaciones <strong>de</strong> comunicaciones.<br />

Un programa C pue<strong>de</strong> ser escrito para un tipo <strong>de</strong> computadora y trasladarse a otra computadora<br />

con pocas o ninguna modificación -propiedad conocida como portabilidad-. El hecho <strong>de</strong> que<br />

C sea portable es importante ya que la mayoría <strong>de</strong> los mo<strong>de</strong>rnos computadores ti<strong>en</strong><strong>en</strong> un compi-


26 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

lador C, una vez que se apr<strong>en</strong><strong>de</strong> C no ti<strong>en</strong>e que apr<strong>en</strong><strong>de</strong>rse un nuevo l<strong>en</strong>guaje cuando se escriba<br />

un programa para otro tipo <strong>de</strong> computadora. No es necesario reescribir un problema para ejecutarse<br />

<strong>en</strong> otra computadora.<br />

C se caracteriza por su velocidad <strong>de</strong> ejecución. En los primeros días <strong>de</strong> la informática, los problemas<br />

<strong>de</strong> tiempo <strong>de</strong> ejecución se resolvían escribi<strong>en</strong>do todo o parte <strong>de</strong> una aplicación <strong>en</strong> l<strong>en</strong>guaje <strong>en</strong>samblador<br />

(l<strong>en</strong>guaje muy cercano al l<strong>en</strong>guaje máquina).<br />

Debido a que exist<strong>en</strong> muchos programas escritos <strong>en</strong> C, se han creado numerosas bibliotecas C para<br />

programadores profesionales que soportan gran variedad <strong>de</strong> aplicaciones. Exist<strong>en</strong> bibliotecas <strong>de</strong>l l<strong>en</strong>guaje<br />

C que soportan aplicaciones <strong>de</strong> bases <strong>de</strong> <strong>datos</strong>, gráficos, edición <strong>de</strong> texto, comunicaciones, etc.<br />

1.6.2. Características técnicas <strong>de</strong> C<br />

Hay numerosas características que difer<strong>en</strong>cian a C <strong>de</strong> otros l<strong>en</strong>guajes y lo hac<strong>en</strong> efici<strong>en</strong>te y pot<strong>en</strong>te a la<br />

vez.<br />

Una nueva sintaxis para <strong>de</strong>clarar funciones. Una <strong>de</strong>claración <strong>de</strong> función pue<strong>de</strong> añadir una <strong>de</strong>scripción<br />

<strong>de</strong> los argum<strong>en</strong>tos <strong>de</strong> la función. Esta información adicional sirve para que los compiladores<br />

<strong>de</strong>tect<strong>en</strong> más fácilm<strong>en</strong>te los errores causados por argum<strong>en</strong>tos que no coincid<strong>en</strong>.<br />

Asignación <strong>de</strong> <strong>estructura</strong>s (registros) y <strong>en</strong>umeraciones.<br />

Preprocesador más sofisticado.<br />

Una nueva <strong>de</strong>finición <strong>de</strong> la biblioteca que acompaña a C. Entre otras funciones se incluy<strong>en</strong>: acceso<br />

al sistema operativo (por ejemplo, lectura y escritura <strong>de</strong> archivos), <strong>en</strong>trada y salida con formato,<br />

asignación dinámica <strong>de</strong> memoria, manejo <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres.<br />

Una colección <strong>de</strong> cabeceras estándar que proporciona acceso uniforme a las <strong>de</strong>claraciones <strong>de</strong> funciones<br />

y tipos <strong>de</strong> <strong>datos</strong>.<br />

1.6.3. Versiones actuales <strong>de</strong> C<br />

En la actualidad son muchos los fabricantes <strong>de</strong> compiladores C, aunque los más populares <strong>en</strong>tre los<br />

fabricantes <strong>de</strong> software son: Microsoft, Imprise, etc.<br />

Una evolución <strong>de</strong> C, el l<strong>en</strong>guaje C++ (C con clases) que conti<strong>en</strong>e <strong>en</strong>tre otras, todas las características<br />

<strong>de</strong> ANSI C. Los compiladores más empleados Visual C++ <strong>de</strong> Microsoft. Buil<strong>de</strong>r C++ <strong>de</strong> lmpriseantigua<br />

Borland, C++ bajo UNIX y LINUX.<br />

En el verano <strong>de</strong>l 2000, Microsoft pat<strong>en</strong>tó una nueva versión <strong>de</strong> C++, que es C#, una evolución <strong>de</strong>l<br />

C++ estándar, con propieda<strong>de</strong>s <strong>de</strong> Java y diseñado para aplicaciones <strong>en</strong> línea, Internet (on line) y<br />

fuerra <strong>de</strong> línea.


Introducción a la ci<strong>en</strong>cia <strong>de</strong> la computación y a la programación 27<br />

1.7. RESUMEN<br />

Una computadora es una máquina para procesar información<br />

y obt<strong>en</strong>er resultados <strong>en</strong> función <strong>de</strong> unos <strong>datos</strong><br />

<strong>de</strong> <strong>en</strong>trada.<br />

Hurdwure: parte física <strong>de</strong> una computadora (dispositivos<br />

electrónicos).<br />

Software: parte lógica <strong>de</strong> una computadora<br />

(program=).<br />

Las computadoras se compon<strong>en</strong> <strong>de</strong>:<br />

Dispositivos <strong>de</strong> Entrada/Salida (WS).<br />

0 Unidad C<strong>en</strong>tral <strong>de</strong> Proceso (Unidad <strong>de</strong> Control<br />

y Unidad Lógica y Aritmética).<br />

Memoria c<strong>en</strong>tral.<br />

Dispositivos <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to masivo <strong>de</strong><br />

información (memoria auxiliar o externa).<br />

El softwure <strong>de</strong>l sistema compr<strong>en</strong><strong>de</strong>, <strong>en</strong>tro otros,<br />

el sistema operativo MSDOS, UNIX, Linux... <strong>en</strong><br />

computadoras personales y los l<strong>en</strong>guajes <strong>de</strong> programación.<br />

Los l<strong>en</strong>guajes <strong>de</strong> programación se clasifican <strong>en</strong>:<br />

alto nivel: Pascal, FORTRAN, VISUAL,<br />

BASIC, C, Ada, Modula-2, Ctt-, Java, Delphi,<br />

C, etc.<br />

bajo nivel: Ensamblador.<br />

máquina: Código máquina.<br />

Los programas traductores <strong>de</strong> l<strong>en</strong>guajes son:<br />

comjdudores.<br />

intérpretes.<br />

C es un l<strong>en</strong>guaje <strong>de</strong> programación que conti<strong>en</strong>e<br />

excel<strong>en</strong>tes características como l<strong>en</strong>guaje para apr<strong>en</strong>dizaje<br />

<strong>de</strong> programación y l<strong>en</strong>guaje profesional <strong>de</strong> propósito<br />

g<strong>en</strong>eral; básicam<strong>en</strong>te es un <strong>en</strong>torno <strong>de</strong> programación<br />

con editor y compilador incorporado.


CAPITULO 2<br />

FUNDAMENTOS<br />

DE PROGRAMACIÓN<br />

CONTENIDO<br />

2.1. Fases <strong>en</strong> la resolución <strong>de</strong><br />

problemas.<br />

2.2. Programación modular.<br />

2.3. Programación <strong>estructura</strong>da.<br />

2.4. Repres<strong>en</strong>tación gráfica <strong>de</strong><br />

<strong>algoritmos</strong>.<br />

2.6. Diagrama <strong>de</strong> Nmsi<br />

Schnei<strong>de</strong>rman .<br />

2.6. El ciclo <strong>de</strong> vida <strong>de</strong>l software.<br />

2.7. Métodos formales <strong>de</strong><br />

verificación <strong>de</strong> programas.<br />

2.8. Factores <strong>de</strong> calidad <strong>de</strong>l<br />

soft ware.<br />

2.9. Resum<strong>en</strong>.<br />

2.10. Ejercicios.<br />

2.11. Ejercicios resueltos.<br />

* /


INTRODUCCI~N<br />

Este capítulo le introduce a la metodología a seguir para la resolución <strong>de</strong><br />

problemas con computadoras y con un l<strong>en</strong>guaje <strong>de</strong> programación como C.<br />

La resolución <strong>de</strong> un problema con una computadora se hace escribi<strong>en</strong>do<br />

un programa, que exige al m<strong>en</strong>os los sigui<strong>en</strong>tes pasos:<br />

1. Definición o análisis <strong>de</strong>l problema.<br />

2. Diseño <strong>de</strong>l algoritmo.<br />

3. Transformación <strong>de</strong>l algoritmo <strong>en</strong> un programa.<br />

4. Ejecución y validación <strong>de</strong>l programa.<br />

Uno <strong>de</strong> los objetivos fundam<strong>en</strong>tales <strong>de</strong> este libro es el apr<strong>en</strong>dizaje y diseño<br />

<strong>de</strong> los dgoritmos. Este capítulo introduce al lector <strong>en</strong> el concepto <strong>de</strong> algoritmo<br />

y <strong>de</strong> programa, así como las herrami<strong>en</strong>tas que permit<strong>en</strong> «dialogar» al usuario<br />

con la máquina: los l<strong>en</strong>guajes <strong>de</strong> programación.<br />

CONCEPTUS CLAVE<br />

0 A1goritmo. O Programación <strong>estructura</strong>da.<br />

O Ciclo <strong>de</strong> vida. O Diseño <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te.<br />

O Diagrama Nssi Schnei<strong>de</strong>rman.<br />

O Pruebas,<br />

O Diagramas <strong>de</strong> flujo.<br />

O Dominio <strong>de</strong>l problema.<br />

O Métodos formales. O Pseudoeódigo.<br />

O Rwtcondieiones. O Factores <strong>de</strong> calidad.<br />

.r Precondiciones. O invariantes.<br />

O Prograrnación modular. O Verificación.<br />

O Diseño.<br />

29


30 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.1. FASES EN LA RESOLUCIÓN DE PROBLEMAS<br />

El proceso <strong>de</strong> resolución <strong>de</strong> un problema con una computadora conduce a la escritura <strong>de</strong> un programa<br />

y a su ejecución <strong>en</strong> la misma. Aunque el proceso <strong>de</strong> diseñar programas es -es<strong>en</strong>cialm<strong>en</strong>te- un proceso<br />

creativo, se pue<strong>de</strong> consi<strong>de</strong>rar una serie <strong>de</strong> fases o pasos comunes, que g<strong>en</strong>eralm<strong>en</strong>te <strong>de</strong>b<strong>en</strong> seguir todos<br />

los programadores.<br />

Las fases <strong>de</strong> resolución <strong>de</strong> un problema con computadora son:<br />

Análisis <strong>de</strong>l problema.<br />

Diseño <strong>de</strong>l algoritmo.<br />

Codificación.<br />

Compilación y ejecución.<br />

o Verijicación.<br />

Depuración.<br />

o Mant<strong>en</strong>imi<strong>en</strong>to.<br />

Docum<strong>en</strong>tación.<br />

Constituy<strong>en</strong> el ciclo <strong>de</strong> vida <strong>de</strong>l software y las fases o etapas usuales son:<br />

Análisis. El problema se analiza t<strong>en</strong>i<strong>en</strong>do pres<strong>en</strong>te la especificación <strong>de</strong> los requisitos dados por<br />

el cli<strong>en</strong>te <strong>de</strong> la empresa o por la persona que <strong>en</strong>carga el programa.<br />

Diseño. Una vez analizado el problema, se diseña una solución que conducirá a un algoritmo que<br />

resuelva el problema.<br />

Codificación (implem<strong>en</strong>tación). La solución se escribe <strong>en</strong> la sintaxis <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> alto nivel<br />

(por ejemplo, C) y se obti<strong>en</strong>e un programa.<br />

Ejecución, verificación y <strong>de</strong>puración. El programa se ejecuta, se comprueba rigurosam<strong>en</strong>te y se<br />

eliminan todos los errores (d<strong>en</strong>ominados «bugs», <strong>en</strong> inglés) que puedan aparecer.<br />

Mant<strong>en</strong>imi<strong>en</strong>to. El programa se actualiza y modifica, cada vez que sea necesario, <strong>de</strong> modo que<br />

se cumplan todas las necesida<strong>de</strong>s <strong>de</strong> cambio <strong>de</strong> sus usuarios.<br />

Docum<strong>en</strong>tación. Escritura <strong>de</strong> las difer<strong>en</strong>tes fases <strong>de</strong>l ciclo <strong>de</strong> vida <strong>de</strong>l software, es<strong>en</strong>cialm<strong>en</strong>te el<br />

análisis, diseño y codificación, unidos a manuales <strong>de</strong> usuario y <strong>de</strong> refer<strong>en</strong>cia, así como normas<br />

para el mant<strong>en</strong>imi<strong>en</strong>to.<br />

Las dos primeras fases conduc<strong>en</strong> a un diseño <strong>de</strong>tallado escrito <strong>en</strong> forma <strong>de</strong> algoritmo. Durante la tercera<br />

etapa (cod$cación) se implem<strong>en</strong>ta’ el algoritmo <strong>en</strong> un código escrito <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación,<br />

reflejando las i<strong>de</strong>as <strong>de</strong>sarrolladas <strong>en</strong> las fases <strong>de</strong> análisis y diseño.<br />

La fase <strong>de</strong> compilación y ejecución traduce y ejecuta el programa. En las fases <strong>de</strong> verijicación y<br />

<strong>de</strong>puración el programador busca errores <strong>de</strong> las etapas anteriores y los elimina. Comprobará que mi<strong>en</strong>tras<br />

más tiempo se gaste <strong>en</strong> la fase <strong>de</strong> análisis y diseño, m<strong>en</strong>os se gastará <strong>en</strong> la <strong>de</strong>puración <strong>de</strong>l programa.<br />

Por último, se <strong>de</strong>be realizar la docum<strong>en</strong>tación <strong>de</strong>l programa.<br />

Antes <strong>de</strong> conocer las tareas a realizar <strong>en</strong> cada fase, vamos a consi<strong>de</strong>rar el concepto y significado <strong>de</strong><br />

la palabra algoritmo. La palabra algoritmo se <strong>de</strong>riva <strong>de</strong> la traducción al latín <strong>de</strong> la palabra Alkh6-<br />

wafizmi’, nombre <strong>de</strong> un matemático y astrónomo árabe que escribió un tratado sobre manipulación <strong>de</strong><br />

números y ecuaciones <strong>en</strong> el siglo IX. Un algoritmo es un método para resolver un problema mediante<br />

una serie <strong>de</strong> pasos precisos, <strong>de</strong>finidos y finitos.<br />

’ En la últinia edición (21.”) <strong>de</strong>l DRAE (Diccionario <strong>de</strong> la Real Aca<strong>de</strong>mia Española) se ha aceptado cl término i,nplrmc,nfur:<br />

(Informática) «Poner <strong>en</strong> funcionami<strong>en</strong>to, aplicar métodos, medidas, etc. para llevar algo a cabo».<br />

’ Escribió un tratado matemático famoso sobre manipulacich <strong>de</strong> números y ecuacioncs titulado Kit& d:juhr ~~‘cilnzugcihala.<br />

La palabra álgebra se <strong>de</strong>rivó, por su semejanza sonora, <strong>de</strong> aí,jahr.


Fundam<strong>en</strong>tos <strong>de</strong><br />

Características <strong>de</strong> un algoritmo<br />

preciso (indicar el ord<strong>en</strong> <strong>de</strong> realización <strong>en</strong> cada paso),<br />

<strong>de</strong>finido (si se sigue dos veces, obti<strong>en</strong>e el mismo resultado cada vez),<br />

finito (ti<strong>en</strong>e fin; un número <strong>de</strong>terminado <strong>de</strong> pasos).<br />

Un algoritmo <strong>de</strong>be producir un resultado <strong>en</strong> un tiempo finito. Los métodos que utilizan <strong>algoritmos</strong><br />

se d<strong>en</strong>ominan métodos algorítmicos, <strong>en</strong> oposición a los métodos que implican algún juicio o interpretación<br />

que se d<strong>en</strong>ominan métodos heurísticos. Los métodos algorítmicos se pued<strong>en</strong> implem<strong>en</strong>tar <strong>en</strong><br />

computadoras; sin embargo, los procesos heurísticos no han sido convertidos fácilm<strong>en</strong>te <strong>en</strong> las computadoras.<br />

En los últimos años las técnicas <strong>de</strong> intelig<strong>en</strong>cia artificial han hecho posible la implem<strong>en</strong>tacicín<br />

<strong>de</strong>l proceso heurístico <strong>en</strong> computadoras.<br />

Ejemplos <strong>de</strong> <strong>algoritmos</strong> son: instrucciones para inontar <strong>en</strong> una bicicleta, hacer una receta <strong>de</strong> cocina,<br />

obt<strong>en</strong>er el máximo común divisor <strong>de</strong> dos números, etc. Los <strong>algoritmos</strong> se pued<strong>en</strong> expresar porfijrmulas,<br />

diagramas <strong>de</strong> pujo o N-S y pseudocódigos. Esta última repres<strong>en</strong>tación es la más utilizada <strong>en</strong><br />

l<strong>en</strong>guajes <strong>estructura</strong>dos como C.<br />

2.1 .I. Análisis <strong>de</strong>l problema<br />

La primera fase <strong>de</strong> la resolución <strong>de</strong> un problema con computadora es el análisis <strong>de</strong>l problema. Esta fase<br />

requiere una clara <strong>de</strong>finición, don<strong>de</strong> se contemple exactam<strong>en</strong>te lo que <strong>de</strong>be hacer el programa y el resultado<br />

o solución <strong>de</strong>seada.<br />

Dado que se busca una solución por computadora, se precisan especificaciones <strong>de</strong>talladas <strong>de</strong> <strong>en</strong>trada<br />

y salida. La Figura 2.1 muestra los requisitos que se <strong>de</strong>b<strong>en</strong> <strong>de</strong>finir <strong>en</strong> el análisis.<br />

Resolución<br />

<strong>de</strong>un<br />

problema<br />

I<br />

Análisis<br />

problema<br />

Diseño<br />

algoritmo<br />

Resolución <strong>de</strong>l<br />

problema con<br />

Figura 2.1. Análisis <strong>de</strong>l problema.<br />

Para po<strong>de</strong>r <strong>de</strong>finir bi<strong>en</strong> un problema es conv<strong>en</strong>i<strong>en</strong>te respon<strong>de</strong>r a las sigui<strong>en</strong>tes preguntas:<br />

¿,Qué <strong>en</strong>tradas se requier<strong>en</strong>? (tipo y cantidad).<br />

o ¿Cuál es la salida <strong>de</strong>seada? (tipo y cantidad).<br />

¿Qué método produce la salida <strong>de</strong>seada'?<br />

Problema 2.1<br />

Se <strong>de</strong>sea obt<strong>en</strong>er una tabla con las <strong>de</strong>prwiuciones acumuladas y Ins valores reales <strong>de</strong> cada año, <strong>de</strong> un<br />

automcívil comprado <strong>en</strong> I. 800.000 pesetas <strong>en</strong> el año 1996, durante los seis años sigui<strong>en</strong>tes suponi<strong>en</strong>do<br />

un valor <strong>de</strong> recuperacicín o rescate <strong>de</strong> 120.000. Realizar el anúlisis <strong>de</strong>l problema, conoci<strong>en</strong>do lu,fórmula<br />

<strong>de</strong> la <strong>de</strong>preciacicín anual constante D para cudu año <strong>de</strong> vida útil.


~ ~~<br />

32 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

D =<br />

coste - valor <strong>de</strong> recuperación<br />

vida útil<br />

1.800.000 - 120.000 - 1.680.000 = 280.000<br />

D = -<br />

6 6<br />

Entrada<br />

Salida<br />

Proceso<br />

I<br />

! cálculo<br />

coste original<br />

vida útil<br />

valor <strong>de</strong> recuperación<br />

<strong>de</strong>preciación anual por año<br />

<strong>de</strong>preciación acumulada <strong>en</strong> cada año<br />

valor <strong>de</strong>l automóvil <strong>en</strong> cada año<br />

<strong>de</strong>preciación acumulada<br />

cálculo <strong>de</strong> la <strong>de</strong>preciación acumulada cada año<br />

<strong>de</strong>l valor <strong>de</strong>l automóvil <strong>en</strong> cada año<br />

La tabla sigui<strong>en</strong>te muestra la salida solicitada<br />

Año Depreciación Depreciación<br />

acumulada<br />

Valor anual<br />

1 (1996) 280.000 280.000<br />

2 (1997) 280.000 560.000<br />

3 (1998) 280.000 840.000<br />

4 (1999) 280.000 1.120.000<br />

5 (2000) 280.000 1.400.000<br />

6 (2001) 280.000 2.180.000<br />

1 S20.000<br />

1.240.000<br />

960.000<br />

680.000<br />

400.000<br />

120.000<br />

c<br />

2.1.2. Diseño <strong>de</strong>l algoritmo<br />

En la etapa <strong>de</strong> análisis <strong>de</strong>l proceso <strong>de</strong> programación se <strong>de</strong>termina qué hace el programa. En la etapa <strong>de</strong><br />

diseño se <strong>de</strong>termina como hace el programa la tarea solicitada. Los métodos más eficaces para el proceso<br />

<strong>de</strong> diseño se basan <strong>en</strong> el conocido por divi<strong>de</strong> y v<strong>en</strong>cerás. Es <strong>de</strong>cir, la resolución <strong>de</strong> un problema<br />

complejo se realiza dividi<strong>en</strong>do el problema <strong>en</strong> subproblemas y a continuación dividir estos subproblemas<br />

<strong>en</strong> otros <strong>de</strong> nivel más bajo, hasta que pueda ser implem<strong>en</strong>tada una solución <strong>en</strong> la computadora.<br />

Este método se conoce técnicam<strong>en</strong>te como diseño <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te (top-down) o modular. El proceso <strong>de</strong><br />

romper el problema <strong>en</strong> cada etapa y expresar cada paso <strong>en</strong> forma más <strong>de</strong>tallada se d<strong>en</strong>omina refinami<strong>en</strong>to<br />

sucesivo.<br />

Cada subprograma es resuelto mediante un módulo (subprograma) que ti<strong>en</strong>e un solo punto <strong>de</strong> <strong>en</strong>trada<br />

y un solo punto <strong>de</strong> salida.<br />

Cualquier programa bi<strong>en</strong> diseñado consta <strong>de</strong> un programa principal (el módulo <strong>de</strong> nivel más alto)<br />

que llama a subprogramas (módulos <strong>de</strong> nivel más bajo) que a su vez pued<strong>en</strong> llamar a otros subprogramas.<br />

Los programas <strong>estructura</strong>dos <strong>de</strong> esta forma se dice que ti<strong>en</strong><strong>en</strong> un diseño modular y el método <strong>de</strong><br />

romper el programa <strong>en</strong> módulos más pequeños se llama programación modular. Los módulos pued<strong>en</strong><br />

ser planeados, codificados, comprobados y <strong>de</strong>purados in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te (incluso por difer<strong>en</strong>tes programadores)<br />

y a continuación combinarlos <strong>en</strong>tre sí. El proceso implica la ejecución <strong>de</strong> los sigui<strong>en</strong>tes<br />

pasos hasta que el programa se termina:<br />

1. Programar un módulo.<br />

2. Comprobar el módulo.


-<br />

Y<br />

Fundam<strong>en</strong>tos <strong>de</strong> programación 33<br />

3. Si es necesario, <strong>de</strong>purar el módulo.<br />

4. Combinar el módulo con los módulos anteriores.<br />

El proceso que convierte los resultados <strong>de</strong>l análisis <strong>de</strong>l problema <strong>en</strong> un diseño modular con refinami<strong>en</strong>tos<br />

sucesivos que permitan una posterior traducción a un l<strong>en</strong>guaje se d<strong>en</strong>omina diseño <strong>de</strong>l algoritmo.<br />

El diseño <strong>de</strong>l algoritmo es in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> programación <strong>en</strong> el que se vaya a codificar<br />

posteriorm<strong>en</strong>te.<br />

2.1.3. Herrami<strong>en</strong>tas <strong>de</strong> programación<br />

Las dos herrami<strong>en</strong>tas más utilizadas comúnm<strong>en</strong>te para diseñar <strong>algoritmos</strong> son: diagramas <strong>de</strong> pujo ,Y<br />

pseudocódigos.<br />

Diagramas <strong>de</strong> flujo<br />

Un diagrama <strong>de</strong> flujo íJowchart) es una repres<strong>en</strong>tación gráfica <strong>de</strong> un algoritmo. Los símbolos utilizados<br />

han sido normalizados por el Instituto Norteamericano <strong>de</strong> Normalización (ANSI), y los más frecu<strong>en</strong>tem<strong>en</strong>te<br />

empleados se muestran <strong>en</strong> la Figura 2.2, junto con una plantilla utilizada para el dibujo <strong>de</strong><br />

los diagramas <strong>de</strong> flujo (Fig. 2.3). En la Figura 2.4 se repres<strong>en</strong>ta el diagrama <strong>de</strong> flujo que resuelve el<br />

Problema 2.1.<br />

Pseudocódigo<br />

El pseudocódigo es una herrami<strong>en</strong>ta <strong>de</strong> programación <strong>en</strong> la que las instrucciones se escrib<strong>en</strong> <strong>en</strong> palabras<br />

similares al inglés o español, que facilitan tanto la escritura como la lectura <strong>de</strong> programas. En es<strong>en</strong>cia,<br />

el pseudocódigo se pue<strong>de</strong> <strong>de</strong>finir como un l<strong>en</strong>guaje <strong>de</strong> especijicaciones <strong>de</strong> <strong>algoritmos</strong>.<br />

Subprograma<br />

J<br />

Entrada!<br />

salida<br />

f<br />

Si<br />

"3<br />

Conectores<br />

Figura 2.2. Símbolos más utilizados <strong>en</strong> los diagramas <strong>de</strong> flujo.<br />

Aunque no exist<strong>en</strong> reglas para escritura <strong>de</strong>l pseudocódigo <strong>en</strong> español, se ha recogido una notación<br />

estándar que se utilizará <strong>en</strong> el libro y que ya es muy empleada <strong>en</strong> los libros <strong>de</strong> programación <strong>en</strong> espa-


34 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

ñol'. Las palabras reservadas básicas se repres<strong>en</strong>tarán <strong>en</strong> letras negritas minúsculas. estas palabras son<br />

traducción libre <strong>de</strong> palabras reservadas <strong>de</strong> l<strong>en</strong>guajes como C, Pascal, etc. Más a<strong>de</strong>lante se indicarán los<br />

pseudocódigos fundam<strong>en</strong>tales a utilizar <strong>en</strong> esta obra.<br />

Figura 2.3. Plantilla para dibujo <strong>de</strong> diagramas <strong>de</strong> flujo.<br />

El pseudocódigo que resuelve el Problema 2. I es:<br />

Previsiones <strong>de</strong> <strong>de</strong>preciacion<br />

Introducir coste<br />

vida util<br />

valor final <strong>de</strong> rescate (recuperacion)<br />

imprimir cabeceras<br />

Establecer el valor inicial <strong>de</strong>l Año<br />

Calcular <strong>de</strong>preciacion<br />

mi<strong>en</strong>tras valor año =< vida util hacer<br />

calcular <strong>de</strong>preciacion acumulada<br />

calcular valor actual<br />

imprimir una linea <strong>en</strong> la Labla<br />

increm<strong>en</strong>tar el valor <strong>de</strong>l año<br />

fin <strong>de</strong> mi<strong>en</strong>tras<br />

Ejemplo 2.1<br />

Calcular la paga neta <strong>de</strong> un trabajador conoci<strong>en</strong>do el número <strong>de</strong> horas trabajadas, la tarifa horaria y<br />

la tasa <strong>de</strong> impuestos.<br />

Al gori tmo<br />

1. Leer Horas, Tarifa, tasa<br />

2. Calcular PagaBruta = Horas * Tarifa<br />

3. Calcular impuestos = PagaRrutd * 'i'usd<br />

4. Calcular PagaNeta = PagaBruta - Impuestos<br />

5. Visualizar PdgaBrutei, Impuestos, PdgdNetd


Fundam<strong>en</strong>tos <strong>de</strong> programación 35<br />

( Inicio<br />

Coste, Vida<br />

Leer Año<br />

Valor actual t Coste<br />

Depreciación t<br />

(Coste-ValorRescate)/<br />

VidaUtil<br />

Acumulada t O<br />

Acumulada +<br />

Valor Actual t<br />

Valor actual +<br />

I AñotAño+l I<br />

Figura 2.4. Diagrama <strong>de</strong> flujo (Ejemplo 2.1).<br />

Ejemplo 2.2<br />

Calcular el valor <strong>de</strong> la suma 1+2+3+ ...+ 100.<br />

Al gori tmo<br />

Se utiliza una variable Contador como un contador que g<strong>en</strong>ere los sucesivos números <strong>en</strong>teros, y Suma<br />

para almac<strong>en</strong>ar las sumas parciales 1, I +2,1+2+3.. .<br />

,<br />

1. Establecer Contador d 1<br />

2. Establecer Suma a O<br />

3. mi<strong>en</strong>tras Contador < = 100 hacer<br />

Sumar Contador a Suma<br />

Increm<strong>en</strong>tar Contador <strong>en</strong> 1<br />

fin-mi<strong>en</strong>tras<br />

4. Visualizar Sumd


36 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.1.4. Codificación <strong>de</strong> un programa<br />

Codificación es la escritura <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación <strong>de</strong> la repres<strong>en</strong>tación <strong>de</strong>l algoritmo <strong>de</strong>sarrollada<br />

<strong>en</strong> las etapas preced<strong>en</strong>tes. Dado que el diseño <strong>de</strong> un algoritmo es in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong><br />

programación utilizado para su implem<strong>en</strong>tación, el código pue<strong>de</strong> ser escrito con igual facilidad <strong>en</strong> un<br />

l<strong>en</strong>guaje o <strong>en</strong> otro.<br />

Para realizar la conversión <strong>de</strong>l algoritmo <strong>en</strong> programa se <strong>de</strong>b<strong>en</strong> sustituir las palabras reservadas <strong>en</strong><br />

español por sus homónimos <strong>en</strong> inglés, y las operaciones/instrucciones indicadas <strong>en</strong> l<strong>en</strong>guaje natural<br />

expresarlas <strong>en</strong> el l<strong>en</strong>guaje <strong>de</strong> programación correspondi<strong>en</strong>te.<br />

/*<br />

Este programa obti<strong>en</strong>e una tabla <strong>de</strong> <strong>de</strong>preciaciones acumuladas y<br />

valores reales <strong>de</strong> cada año <strong>de</strong> un <strong>de</strong>terminado producto<br />

*/<br />

#inclu<strong>de</strong> <br />

void main0<br />

i<br />

double Coste, Depreciacion,<br />

Valor-Recuperacion,<br />

Valor-actual,<br />

Acumulado,<br />

ValorAnual;<br />

int Anio, Vida-util;<br />

puts("1ntroduzca coste, valor recuperación y vida Útil");<br />

scanf("%lf %lf %lf",&Coste,&Valor-Recuperacion,&Vida-ütil);<br />

puts ("Introduzca año actual") ;<br />

scanf ("%d",&Anio);<br />

ValorActual = Coste;<br />

Depreciación = (Coste-Valor-Recuperac¡on)/V¡da-Util;<br />

Acumulado = O;<br />

puts ("Año Depreciación Dep. Acumulada") ;<br />

while (Anio < Vida-Util)<br />

I<br />

1<br />

i<br />

Acumulado = Acumulado + Depreciacion;<br />

ValorActual = ValorActual - Depreciacion;<br />

printf ("Año:%d, Depreciacion:%.21f, R.21f Acumulada",<br />

Anio,Depreciacion,Acumulado) ;<br />

Anio = Ani0 + 1;<br />

Docum<strong>en</strong>tación interna<br />

Como se verá más tar<strong>de</strong>, la docum<strong>en</strong>tación <strong>de</strong> un programa se clasifica <strong>en</strong> interna y externa. La dumm<strong>en</strong>tación<br />

interna es la que se incluye d<strong>en</strong>tro <strong>de</strong>l código <strong>de</strong>l programa fu<strong>en</strong>te mediante com<strong>en</strong>tarios<br />

que ayudan a la compr<strong>en</strong>sión <strong>de</strong>l código. Todas las líneas <strong>de</strong> programas que comi<strong>en</strong>c<strong>en</strong> con un símbolo<br />

/ * son com<strong>en</strong>tarios. El programa no los necesita y la computadora los ignora. Estas líneas <strong>de</strong> com<strong>en</strong>tarios<br />

sólo sirv<strong>en</strong> para hacer los programas más fáciles <strong>de</strong> compr<strong>en</strong><strong>de</strong>r. El objetivo <strong>de</strong>l programador<br />

<strong>de</strong>be ser escribir códigos s<strong>en</strong>cillos y limpios.<br />

Debido a que las máquinas actuales soportan gran<strong>de</strong>s memorias (64 Mb o 128 Mb <strong>de</strong> memoria c<strong>en</strong>tral<br />

mínima <strong>en</strong> computadoras personales) no es necesario recurrir a técnicas <strong>de</strong> ahorro <strong>de</strong> memoria, por<br />

lo que es recom<strong>en</strong>dable que incluya el mayor número <strong>de</strong> com<strong>en</strong>tarios posibles, pero, eso sí, que sean<br />

significativos.


--__I<br />

-_<br />

A<br />

Fundam<strong>en</strong>tos <strong>de</strong> programación 37<br />

2.1.5. Compilación y ejecución <strong>de</strong> un programa<br />

Una vez que el algoritmo se ha convertido <strong>en</strong> un programa fu<strong>en</strong>te, es preciso introducirlo <strong>en</strong> memoria<br />

mediante el teclado y almac<strong>en</strong>arlo posteriorm<strong>en</strong>te <strong>en</strong> un disco. Esta operación se realiza con un programa<br />

editor, posteriorm<strong>en</strong>te el programa fu<strong>en</strong>te se convierte <strong>en</strong> un archivo <strong>de</strong> programa que se guarda<br />

(graba) <strong>en</strong> disco.<br />

El programa fu<strong>en</strong>te <strong>de</strong>be ser traducido a l<strong>en</strong>guaje máquina, este proceso se realiza con el compilador<br />

y el sistema operativo que se <strong>en</strong>carga prácticam<strong>en</strong>te <strong>de</strong> la compilación.<br />

Si tras la compilación se pres<strong>en</strong>tan errores (errores <strong>de</strong> compilación) <strong>en</strong> el programa fu<strong>en</strong>te, es preciso<br />

volver a editar el programa, corregir los errores y compilar <strong>de</strong> nuevo. Este proceso se repite hasta<br />

que no se produc<strong>en</strong> errores, obt<strong>en</strong>iéndose el programa objeto que todavía no es ejecutable directam<strong>en</strong>te.<br />

Suponi<strong>en</strong>do que no exist<strong>en</strong> errores <strong>en</strong> el programa fu<strong>en</strong>te, se <strong>de</strong>be instruir al sistema operativo<br />

para que realice la fase <strong>de</strong> montaje o <strong>en</strong>lace (link), carga, <strong>de</strong>l programa objeto con las librerías <strong>de</strong>l programa<br />

<strong>de</strong>l compilador. El proceso <strong>de</strong> montaje produce un programa ejecutable. La Figura 2.5 <strong>de</strong>scribe<br />

el proceso completo <strong>de</strong> compilació<strong>de</strong>jecución <strong>de</strong> un programa.<br />

UCP<br />

1 ElDCiDE 1<br />

Memoria<br />

externa<br />

Teclado<br />

I'""\<br />

<strong>de</strong> textos<br />

Memoria<br />

Campilador<br />

UCP<br />

Memoria<br />

externa<br />

objeto<br />

I<br />

t<br />

I<br />

i . 1<br />

I<br />

I<br />

C)<br />

Figura 25 Fases <strong>de</strong> /a comp/~ac;Ón/eiecuc;Ón <strong>de</strong> un programa: a/ ed/c/on; 6/ compAac/Ün; c/ montale o <strong>en</strong>/ace.


38 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Cuando el programa ejecutable se ha creado, se pue<strong>de</strong> ya ejecutar (correr o rodar) <strong>de</strong>s<strong>de</strong> el sistema<br />

operativo con sólo teclear su nombre (<strong>en</strong> el caso <strong>de</strong> DOS). Suponi<strong>en</strong>do que no exist<strong>en</strong> errores durante<br />

la ejecución (llamados errores <strong>en</strong> tiempo <strong>de</strong> ejecución), se obt<strong>en</strong>drá la salida <strong>de</strong> resultados <strong>de</strong>l programa.<br />

Las instrucciones u Órd<strong>en</strong>es para compilar y ejecutar un programa <strong>en</strong> C pue<strong>de</strong> variar según el tipo<br />

<strong>de</strong> compilador. Así el proceso <strong>de</strong> Visual C++ 6 es difer<strong>en</strong>te <strong>de</strong> C bajo UNIX o bajo Linux.<br />

2.1.6. Verificación y <strong>de</strong>puración <strong>de</strong> un programa<br />

La verijkución o compilacirín <strong>de</strong> un programa es el proceso <strong>de</strong> ejecución <strong>de</strong>l programa con una amplia<br />

variedad <strong>de</strong> <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada, llamados <strong>datos</strong> <strong>de</strong> rest o pruehu, que <strong>de</strong>terminarán si el programa ti<strong>en</strong>e<br />

errores in bug^»). Para realizar la verificación se <strong>de</strong>be <strong>de</strong>sarrollar una amplia gama <strong>de</strong> <strong>datos</strong> <strong>de</strong> test: valores<br />

normales <strong>de</strong> <strong>en</strong>trada, valores extremos <strong>de</strong> <strong>en</strong>trada que comprueb<strong>en</strong> los límites <strong>de</strong>l programa y valores<br />

<strong>de</strong> <strong>en</strong>trada que comprueb<strong>en</strong> aspectos especiales <strong>de</strong>l programa.<br />

La <strong>de</strong>puración es el proceso <strong>de</strong> <strong>en</strong>contrar los errores <strong>de</strong>l programa y corregir o eliminar dichos<br />

errores.<br />

Cuando se ejecuta un programa, se pued<strong>en</strong> producir tres tipos <strong>de</strong> errores:<br />

1. Errores <strong>de</strong> compilación. Se produc<strong>en</strong> normalm<strong>en</strong>te por un uso incorrecto <strong>de</strong> las reglas <strong>de</strong>l l<strong>en</strong>guaje<br />

<strong>de</strong> programación y suel<strong>en</strong> ser errores <strong>de</strong> sintusis. Si existe un error <strong>de</strong> sintaxis, la computadora<br />

no pue<strong>de</strong> compr<strong>en</strong><strong>de</strong>r la instrucción, no se obt<strong>en</strong>drá el programa objeto y el compilador<br />

imprimirá una lista <strong>de</strong> todos los errores <strong>en</strong>contrados durante la compilación.<br />

2. Errores <strong>de</strong> ejecución. Estos errores se produc<strong>en</strong> por instrucciones que la computadora pue<strong>de</strong><br />

compr<strong>en</strong><strong>de</strong>r pero no ejecutar. Ejemplos típicos son: división por cero y raíces cuadradas <strong>de</strong> números<br />

negativos. En estos casos se <strong>de</strong>ti<strong>en</strong>e la ejecución <strong>de</strong>l programa y se imprime un m<strong>en</strong>saje <strong>de</strong><br />

error.<br />

3. Errores lógicos. Se produc<strong>en</strong> <strong>en</strong> la lógica <strong>de</strong>l programa y la fu<strong>en</strong>te <strong>de</strong>l error suele ser el diseño<br />

<strong>de</strong>l algoritmo. Estos errores son los más difíciles <strong>de</strong> <strong>de</strong>tectar, ya que el programa pue<strong>de</strong><br />

funcionar y no producir errores <strong>de</strong> compilación ni <strong>de</strong> ejecución, y sólo pue<strong>de</strong> advertir el error<br />

por la obt<strong>en</strong>ción <strong>de</strong> resultados incorrectos. En este caso se <strong>de</strong>be volver a la fase <strong>de</strong> diseño <strong>de</strong>l<br />

algoritmo, modificar el algoritmo, cambiar el programa fu<strong>en</strong>te y compilar y ejecutar una vez<br />

más.<br />

2.1.7. Docum<strong>en</strong>tación y mant<strong>en</strong>imi<strong>en</strong>to<br />

La docum<strong>en</strong>tación <strong>de</strong> un problema consta <strong>de</strong> las <strong>de</strong>scripciones <strong>de</strong> los pasos a dar <strong>en</strong> el proceso <strong>de</strong> resolución<br />

<strong>de</strong> un problema. La importancia <strong>de</strong> la docum<strong>en</strong>tación <strong>de</strong>be ser <strong>de</strong>stacada por su <strong>de</strong>cisiva influ<strong>en</strong>cia<br />

<strong>en</strong> el producto final. Programas pobrem<strong>en</strong>te docum<strong>en</strong>tados son difíciles <strong>de</strong> leer, más difíciles <strong>de</strong><br />

<strong>de</strong>purar y casi imposibles <strong>de</strong> mant<strong>en</strong>er y modificar.<br />

La docum<strong>en</strong>tación <strong>de</strong> un programa pue<strong>de</strong> ser interriu y externa. La docum<strong>en</strong>tación interna es la<br />

cont<strong>en</strong>ida <strong>en</strong> líneas <strong>de</strong> com<strong>en</strong>tarios. La docum<strong>en</strong>racicín exrema incluye análisis, diagramas <strong>de</strong> flujo y/o<br />

pseudocódigos, manuales <strong>de</strong> usuario con instrucciones para ejecutar el programa y para interpretar los<br />

resultados.<br />

La docum<strong>en</strong>tación es vital cuando se <strong>de</strong>sea corregir posibles errores futuros o bi<strong>en</strong> cambiar el programa.<br />

Tales cambios se d<strong>en</strong>ominan munt<strong>en</strong>imi<strong>en</strong>to <strong>de</strong>l progruma. Después <strong>de</strong> cada cambio la docum<strong>en</strong>tación<br />

<strong>de</strong>be ser actualizada para facilitar cambios posteriores. Es práctica frecu<strong>en</strong>te numerar las<br />

sucesivas versiones <strong>de</strong> los programas 1.0, 1.1, 2.0, 2.1, etc. (Si los cambios introducidos son importantes,<br />

se varía el primer dígito [1.0, 2.0, ...I, <strong>en</strong> caso <strong>de</strong> pequeños cambios sólo se varía el segundo<br />

dígito [2.0,2.1...I.)


Fundam<strong>en</strong>tos <strong>de</strong> programación 39<br />

2.2. PROGRAMACIÓN MODULAR<br />

La programación modular es uno <strong>de</strong> los métodos <strong>de</strong> diseño más flexible y pot<strong>en</strong>tes para mejorar la productividad<br />

<strong>de</strong> un programa. En programación modular el programa se divi<strong>de</strong> <strong>en</strong> módulos (partes in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes),<br />

cada una <strong>de</strong> las cuales ejecuta una Única actividad o tarea y se codifican in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te<br />

<strong>de</strong> otros módulos. Cada uno <strong>de</strong> estos módulos se analizan, codifican y pon<strong>en</strong> a punto por separado.<br />

Cada programa conti<strong>en</strong>e un módulo d<strong>en</strong>ominado progruma principul que controla todo lo que suce<strong>de</strong>;<br />

se transfiere el control a submódulos (posteriorm<strong>en</strong>te se d<strong>en</strong>ominarán subprogramas), <strong>de</strong> modo que<br />

ellos puedan ejecutar sus funciones; sin embargo, cada submódulo <strong>de</strong>vuelve el control al módulo principal<br />

cuando se haya completado su tarea. Si la tarea asignada a cada submódulo es <strong>de</strong>masiado compleja,<br />

éste <strong>de</strong>berá romperse <strong>en</strong> otros módulos más pequeños. El proceso sucesivo <strong>de</strong> subdivisión <strong>de</strong> módulos<br />

continúa hasta que cada módulo t<strong>en</strong>ga solam<strong>en</strong>te una tarea específica que ejecutar. Esta tarea pue<strong>de</strong> ser<br />

<strong>en</strong>trada, salidu, manipulación <strong>de</strong> <strong>datos</strong>, control <strong>de</strong> otros módulos o alguna combinación <strong>de</strong> éstos. Un<br />

módulo pue<strong>de</strong> transferir temporalm<strong>en</strong>te (hifurcur) el control a otro módulo; sin embargo, cada módulo<br />

<strong>de</strong>be ev<strong>en</strong>tualm<strong>en</strong>te <strong>de</strong>volver el control al módulo <strong>de</strong>l cual se recibe originalm<strong>en</strong>te el control.<br />

Los módulos son in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes <strong>en</strong> el s<strong>en</strong>tido <strong>en</strong> que ningún módulo pue<strong>de</strong> t<strong>en</strong>er acceso directo a<br />

cualquier otro módulo excepto el módulo al que llama y sus propios submódulos. Sin embargo, los<br />

resultados producidos por un módulo pued<strong>en</strong> ser utilizados por cualquier otro módulo cuando se transfiera<br />

a ellos el control.<br />

7<br />

Raíz<br />

Módulo 1 Módulo 2<br />

Módulo 3 Módulo 4<br />

Módulo 11 Módulo 12<br />

Módulo 31 Módulo 41 Módulo 42<br />

pGqpGGzq<br />

Figura 2.6. Programación modular.<br />

Dado que los módulos son in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes, difer<strong>en</strong>tes programadores pued<strong>en</strong> trabajar simultáneam<strong>en</strong>te<br />

<strong>en</strong> difer<strong>en</strong>tes partes <strong>de</strong>l mismo programa. Esto reducirá el tiempo <strong>de</strong>l diseño <strong>de</strong>l algoritmo y posterior<br />

codificación <strong>de</strong>l programa. A<strong>de</strong>más, un módulo se pue<strong>de</strong> modificar radicalm<strong>en</strong>te sin afectar a<br />

otros módulos, incluso sin alterar su función principal.<br />

La <strong>de</strong>scomposición <strong>de</strong> un programa <strong>en</strong> módulos in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes más simples se conoce también<br />

como el método <strong>de</strong> «divi<strong>de</strong> y v<strong>en</strong>cerás» (divi<strong>de</strong> and conquer). Se diseña cada módulo con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia<br />

<strong>de</strong> los <strong>de</strong>más, y sigui<strong>en</strong>do un método asc<strong>en</strong>d<strong>en</strong>te o <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te se llegará hasta la <strong>de</strong>scomposición<br />

final <strong>de</strong>l problema <strong>en</strong> módulos <strong>en</strong> forma jerárquica.


40 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.3. PROGRAMACIÓN ESTRUCTURADA<br />

Los términos programación modular; programación <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te y programación <strong>estructura</strong>da se introdujeron<br />

<strong>en</strong> la segunda mitad <strong>de</strong> la década <strong>de</strong> los ses<strong>en</strong>ta y a m<strong>en</strong>udo sus términos se utilizan como sinónimos<br />

aunque no significan lo mismo. La programación modular y <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te ya se ha examinado<br />

anteriorm<strong>en</strong>te. La programación <strong>estructura</strong>da significa escribir un programa <strong>de</strong> acuerdo a las sigui<strong>en</strong>tes<br />

reglas:<br />

El programa ti<strong>en</strong>e un diseño modular.<br />

Los módulos son diseñados <strong>de</strong> modo <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te.<br />

Cada módulo se codifica utilizando las tres <strong>estructura</strong>s <strong>de</strong> control básicas: secu<strong>en</strong>cia, selección y<br />

repetición.<br />

Si está familiarizado con l<strong>en</strong>guajes como BASIC, Pascal, FORTRAN o C, la programación<br />

<strong>estructura</strong>da significa también progrumación sin GOTO (C no requiere el uso <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia<br />

GOTO).<br />

El término programación <strong>estructura</strong>da se refiere a un conjunto <strong>de</strong> técnicas que han ido evolucionando<br />

<strong>de</strong>s<strong>de</strong> los primeros trabajos <strong>de</strong> Edgar Dijkstra. Estas técnicas aum<strong>en</strong>tan consi<strong>de</strong>rablem<strong>en</strong>te la<br />

productividad <strong>de</strong>l programa reduci<strong>en</strong>do <strong>en</strong> elevado grado el tiempo requerido para escribir, verificar,<br />

<strong>de</strong>purar y mant<strong>en</strong>er los programas. La programación <strong>estructura</strong>da utiliza un número limitado <strong>de</strong> <strong>estructura</strong>s<br />

<strong>de</strong> control que minimizan la complejidad <strong>de</strong> los programas y, por consigui<strong>en</strong>te, reduc<strong>en</strong> los errores;<br />

hace los programas más fáciles <strong>de</strong> escribir, verificar, leer y mant<strong>en</strong>er. Los programas <strong>de</strong>b<strong>en</strong> estar<br />

dotados <strong>de</strong> una <strong>estructura</strong>.<br />

La programación <strong>estructura</strong>da es el conjunto <strong>de</strong> técnicas que incorporan:<br />

recursos abstractos,<br />

diseño <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te (top-down),<br />

<strong>estructura</strong>s básicas.<br />

2.3.1. Recursos abstractos<br />

La programación <strong>estructura</strong>da se auxilia <strong>de</strong> los recursos abstractos <strong>en</strong> lugar <strong>de</strong> los recursos concretos <strong>de</strong><br />

que dispone un <strong>de</strong>terminado l<strong>en</strong>guaje <strong>de</strong> programación.<br />

Descomponer un programa <strong>en</strong> términos <strong>de</strong> recursos abstractos -según Dijkstra- consiste <strong>en</strong> <strong>de</strong>scomponer<br />

una <strong>de</strong>terminada acción compleja <strong>en</strong> términos <strong>de</strong> un número <strong>de</strong> acciones más simples capaces<br />

<strong>de</strong> ejecutarlas o que constituyan instrucciones <strong>de</strong> computadoras disponibles.<br />

2.3.2. Diseño <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te (topdown)<br />

El diseño <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te (top-down) es el proceso mediante el cual un problema se <strong>de</strong>scompone <strong>en</strong> una<br />

serie <strong>de</strong> niveles o pasos sucesivos <strong>de</strong> refinami<strong>en</strong>to (stepwise). La metodología <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te consiste <strong>en</strong><br />

efectuar una relación <strong>en</strong>tre las sucesivas etapas <strong>de</strong> <strong>estructura</strong>ción <strong>de</strong> modo que se relacionas<strong>en</strong> unas con<br />

otras mediante <strong>en</strong>tradas y salidas <strong>de</strong> información. Es <strong>de</strong>cir, se <strong>de</strong>scompone el problema <strong>en</strong> etapas o<br />

<strong>estructura</strong>s jerárquicas, <strong>de</strong> forma que se pue<strong>de</strong> consi<strong>de</strong>rar cada <strong>estructura</strong> <strong>de</strong>s<strong>de</strong> dos puntos <strong>de</strong> vista:<br />

¿qué hace? y ¿cómo lo hace?<br />

Si se consi<strong>de</strong>ra un nivel n <strong>de</strong> refinami<strong>en</strong>to, las <strong>estructura</strong>s se consi<strong>de</strong>ran <strong>de</strong> la sigui<strong>en</strong>te manera:


Fundam<strong>en</strong>tos <strong>de</strong> programación 41<br />

Nivel n: <strong>de</strong>s<strong>de</strong> el exterior<br />

,,¿lo que hace?»<br />

Nivel n + 7: Vista <strong>de</strong>s<strong>de</strong> el interior<br />


fórmulas<br />

42 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

2.3.4. Teorema <strong>de</strong> la programación <strong>estructura</strong>da: <strong>estructura</strong>s básicas<br />

En mayo <strong>de</strong> 1966, Bohm y Jacopini <strong>de</strong>mostraron que un programa propio pue<strong>de</strong> ser escrito utilizando<br />

solam<strong>en</strong>te tres tipos <strong>de</strong> <strong>estructura</strong>s <strong>de</strong> control.<br />

0 secu<strong>en</strong>ciales,<br />

e selectivas,<br />

repetitivas.<br />

Un programa se <strong>de</strong>fine como propio si cumple las sigui<strong>en</strong>tes características:<br />

o Posee un solo punto <strong>de</strong> <strong>en</strong>trada y uno <strong>de</strong> salida ofin para control <strong>de</strong>l programa.<br />

o Exist<strong>en</strong> caminos <strong>de</strong>s<strong>de</strong> la <strong>en</strong>trada hasta la salida que .se pued<strong>en</strong> seguir y que pasan por todas lus<br />

partes <strong>de</strong>l programa.<br />

o Todas las instrucciones son ejecutahles y no exist<strong>en</strong> /ai.os CJ bucles infinitos (sin fin).<br />

Los Capítulos 5 y 6 se <strong>de</strong>dican al estudio <strong>de</strong> las <strong>estructura</strong>s <strong>de</strong> control selectivas y repetitivas<br />

La programación <strong>estructura</strong>da signijica:<br />

o El programa completo ti<strong>en</strong>e un diseño modular.<br />

O Los módulos se diseñan con metodología <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te (pue<strong>de</strong> hacerse también asc<strong>en</strong>d<strong>en</strong>te).<br />

O Cada módulo se codifica utilizando las tres <strong>estructura</strong>s <strong>de</strong> control básicas: secu<strong>en</strong>ciales,<br />

selectivas y repetitivas (aus<strong>en</strong>cia total <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias GOTO).<br />

0 Estructuración y modularidad son conceptos complem<strong>en</strong>tarios (se solapan).<br />

2.4. REPRESENTACI~N GRÁFICA DE LOS ALGORITMOS<br />

Para repres<strong>en</strong>tar un algoritmo se <strong>de</strong>be utilizar algún método que permita in<strong>de</strong>p<strong>en</strong>dizar dicho algoritmo<br />

<strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> programación elegido. Ello permitirá que un algoritmo pueda ser codificado indistintam<strong>en</strong>te<br />

<strong>en</strong> cualquier l<strong>en</strong>guaje. Para conseguir este objetivo se precisa que el algoritmo sea repres<strong>en</strong>tado<br />

gráfica o numéricam<strong>en</strong>te, <strong>de</strong> modo que las sucesivas acciones no <strong>de</strong>p<strong>en</strong>dan <strong>de</strong> la sintaxis <strong>de</strong> ningún l<strong>en</strong>guaje<br />

<strong>de</strong> programación, sino que la <strong>de</strong>scripción pueda servir fácilm<strong>en</strong>te para su transformación <strong>en</strong> un<br />

programa, es <strong>de</strong>cir, su codi$cución.<br />

Los métodos usuales para repres<strong>en</strong>tar un algoritmo son:<br />

1. diagrama <strong>de</strong> flujo,<br />

2. diagrama N-S (Nassi-Schnei<strong>de</strong>rman),<br />

3. l<strong>en</strong>guaje <strong>de</strong> especificación <strong>de</strong> <strong>algoritmos</strong>: pLseudoccídigo,<br />

4. l<strong>en</strong>guaje esparlol, inglés ...<br />

5. ~ .<br />

Los métodos 4 y 5 no suel<strong>en</strong> ser fáciles <strong>de</strong> transformar <strong>en</strong> programas. Una <strong>de</strong>scripción <strong>en</strong> español<br />

narrativo no es satisfactoria, ya que es <strong>de</strong>masiado prolija y g<strong>en</strong>eralm<strong>en</strong>te ambigua. Una fórmula, sin<br />

embargo, es bu<strong>en</strong> sistema <strong>de</strong> repres<strong>en</strong>tación. Por ejemplo, las fórmulas para la solución <strong>de</strong> una ecuación<br />

cuadrática (<strong>de</strong> segundo grado) es un medio sucinto <strong>de</strong> expresar el procedimi<strong>en</strong>to algorítmico que se<br />

<strong>de</strong>be ejecutar para obt<strong>en</strong>er las raíces <strong>de</strong> dicha ecuación.<br />

-~<br />

XI = (- b + m) / 2a x2 = (- b -


a<br />

Fundam<strong>en</strong>tos <strong>de</strong> programación 43<br />

Sin embargo, no es frecu<strong>en</strong>te que un algoritmo pueda ser expresado por medio <strong>de</strong> una simple fórmula.<br />

2.4.1. Diagramas <strong>de</strong> flujo<br />

Un diagrama <strong>de</strong> flujo (fZowchart) es una <strong>de</strong> las técnicas <strong>de</strong> repres<strong>en</strong>tación <strong>de</strong> <strong>algoritmos</strong> más antigua<br />

y a la vez más utilizada, aunque su empleo ha disminuido consi<strong>de</strong>rablem<strong>en</strong>te, sobro todo, <strong>de</strong>s<strong>de</strong> la aparición<br />

<strong>de</strong> l<strong>en</strong>guajes <strong>de</strong> programación <strong>estructura</strong>dos. Un diagrama <strong>de</strong> flujo es un diagrama que utiliza los<br />

símbolos (cajas) estándar mostrados <strong>en</strong> la Tabla 2.1 y que ti<strong>en</strong>e los pasos <strong>de</strong> algoritmo escritos <strong>en</strong> esas<br />

cajas unidas por flechas, d<strong>en</strong>ominadas líneas <strong>de</strong>flujo, que indican la secu<strong>en</strong>cia <strong>en</strong> que se <strong>de</strong>be ejecutar.<br />

La Figura 2.8 es un diagrama <strong>de</strong> flujo básico. Este diagrama repres<strong>en</strong>ta la resolución <strong>de</strong> un programa<br />

que <strong>de</strong>duce el salario neto <strong>de</strong> un trabajador a partir <strong>de</strong> la lectura <strong>de</strong>l nombre, horas trabajadas, precio<br />

<strong>de</strong> la hora, y sabi<strong>en</strong>do que los impuestos aplicados son el 25 por 100 sobre el salario bruto.<br />

Los símbolos estándar normalizados por ANSI (abreviatura <strong>de</strong> American National Stanúars Znstitute)<br />

son muy variados. En la Figura 2.9 se repres<strong>en</strong>ta una plantilla <strong>de</strong> dibujo típica don<strong>de</strong> se contemplan<br />

la mayoría <strong>de</strong> los símbolos utilizados <strong>en</strong> el diagrama; sin embargo, los símbolos más utilizados<br />

repres<strong>en</strong>tan:<br />

Tabla 2.1. Símbolos <strong>de</strong> diagrama <strong>de</strong> flujo<br />

Símbolos<br />

principales<br />

(-)<br />

/T<br />

O I<br />

O<br />

O<br />

Función<br />

Terminal (repres<strong>en</strong>ta el comi<strong>en</strong>zo, «inicio». y el final, «fin» <strong>de</strong> un programa. Pue<strong>de</strong> repres<strong>en</strong>tar también<br />

una parada o interrupción programada que sea necesario realizar <strong>en</strong> un programa.<br />

EntradaíSalida (cualquier tipo <strong>de</strong> introducción <strong>de</strong> <strong>datos</strong> <strong>en</strong> la memoria <strong>de</strong>s<strong>de</strong> los periféricos. «<strong>en</strong>trada»,<br />

o registro <strong>de</strong> la información procesada <strong>en</strong> un periférico. «salida».<br />

Proceso (cualquier tipo <strong>de</strong> operación que pueda originar cambio <strong>de</strong> valor, formato o posición <strong>de</strong> la<br />

información almac<strong>en</strong>ada <strong>en</strong> memoria, operaciones aritméticas, <strong>de</strong> transfer<strong>en</strong>cia, etc.).<br />

Decisión (indica operaciones lógicas o <strong>de</strong> comparación <strong>en</strong>tre <strong>datos</strong> -normalm<strong>en</strong>te dos- y <strong>en</strong> función<br />

<strong>de</strong>l resultado <strong>de</strong> la misma <strong>de</strong>termina cuál <strong>de</strong> los distintos caminos alternativos <strong>de</strong>l programa se<br />

<strong>de</strong>be seguir; normalm<strong>en</strong>te ti<strong>en</strong>e dos salidas -respuestas SI o NO- pero pue<strong>de</strong> t<strong>en</strong>er tres o más,<br />

según los casos).<br />

Decisión múltiple (<strong>en</strong> función <strong>de</strong>l resultado <strong>de</strong> la comparación se seguirá uno <strong>de</strong> los difer<strong>en</strong>tes caminos<br />

<strong>de</strong> acuerdo con dicho resultado).<br />

Conector (sirve para <strong>en</strong>lazar dos partes cualesquiera <strong>de</strong> un ordinograma a través <strong>de</strong> un conector <strong>en</strong><br />

la salida y otro conector <strong>en</strong> la <strong>en</strong>trada. Se refiere a la conexión <strong>en</strong> la misma página <strong>de</strong>l diagrama.<br />

Indicador <strong>de</strong> dirección o línea <strong>de</strong> flujo (indica el s<strong>en</strong>tido <strong>de</strong> eiecución <strong>de</strong> las operaciones).<br />

Línea conectora (sirve <strong>de</strong> unión <strong>en</strong>tre dos símbolos).<br />

Conector (conexión <strong>en</strong>tre dos puntos <strong>de</strong>l organigrama situado <strong>en</strong> páginas difer<strong>en</strong>tes).<br />

Llamada subrutina o a un proceso pre<strong>de</strong>terminado (una subrutina es un módulo in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te <strong>de</strong>l<br />

programa principal, que recibe una <strong>en</strong>trada proced<strong>en</strong>te <strong>de</strong> dicho programa, realiza una tarea <strong>de</strong>terminada<br />

y regresa, al terminar, al programa principal).<br />

(C.otziinikiJ


~<br />

44 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

(Cotiririuucih)<br />

Símbolos<br />

secundarios Función<br />

Pantalla (se utiliza <strong>en</strong> ocasiones <strong>en</strong> lugar <strong>de</strong>l símbolo <strong>de</strong> E/S).<br />

c-]<br />

Impresora (se utiliza <strong>en</strong> ocasiones <strong>en</strong> lugar <strong>de</strong>l sínibolo <strong>de</strong> E/S).<br />

Teclado (se utiliza <strong>en</strong> ocasiones <strong>en</strong> lugar <strong>de</strong>l símbolo <strong>de</strong> E/S).<br />

Com<strong>en</strong>tarios (se utiliza para añadir com<strong>en</strong>tarios clasificadores a otros símbolos <strong>de</strong>l diagrama <strong>de</strong> flujo.<br />

Se pued<strong>en</strong> dibujar a cualquier lado <strong>de</strong>l símbolo).<br />

9<br />

inicio<br />

leer nombre,<br />

horas,<br />

precio<br />

bruto t<br />

tasas t<br />

neto t<br />

escribir nombre,<br />

bruto tasas,<br />

Figura 2.8. Diagrama <strong>de</strong> flujo.


Fundam<strong>en</strong>tos <strong>de</strong> programación 45<br />

O proceso,<br />

o <strong>de</strong>cisión,<br />

o conectores,<br />

O fin,<br />

o <strong>en</strong>traddsalida,<br />

O dirección <strong>de</strong>l flujo.<br />

Se resume <strong>en</strong> la Figura 2.8 <strong>en</strong> un diagrama <strong>de</strong> flujo:<br />

o existe una caja etiquetada "inicio", que es <strong>de</strong> tipo elíptico,<br />

O existe una caja etiquetada I' fin" <strong>de</strong> igual forma que la anterior,<br />

O si exist<strong>en</strong> otras cajas, normalm<strong>en</strong>te son rectangulares, tipo rombo o paralelogramo (el resto <strong>de</strong><br />

las figuras se utilizan sólo <strong>en</strong> diagramas <strong>de</strong> flujo g<strong>en</strong>erales o <strong>de</strong> <strong>de</strong>talle y no siempre son imprescindibles).<br />

Se pue<strong>de</strong> escribir más <strong>de</strong> un paso <strong>de</strong>l algoritmo <strong>en</strong> una sola caja rectangular. El uso <strong>de</strong> flechas significa<br />

que la caja no necesita ser escrita <strong>de</strong>bajo <strong>de</strong> su pre<strong>de</strong>cesora. Sin embargo, abusar <strong>de</strong>masiado <strong>de</strong><br />

esta flexibilidad conduce a diagramas <strong>de</strong> flujo complicados e ininteligibles.<br />

,/ E2ir;F<br />

,/ 0 0 lp'.c.-1<br />

1<br />

Figura 2.9. Plantilla típica para diagramas <strong>de</strong> flujo.<br />

Ejemplo 2.3<br />

Calcular la media <strong>de</strong> una serie <strong>de</strong> números positivos, suponi<strong>en</strong>do que los <strong>datos</strong> se le<strong>en</strong> <strong>de</strong>s<strong>de</strong> un terminal.<br />

Un valor <strong>de</strong> cero -como <strong>en</strong>trada- indicará que se ha alcanzado el final <strong>de</strong> la serie <strong>de</strong> niimeros<br />

positivos.<br />

El primer paso a dar <strong>en</strong> el <strong>de</strong>sarrollo <strong>de</strong>l algoritmo es <strong>de</strong>scomponer el problema <strong>en</strong> una serie <strong>de</strong><br />

pasos secu<strong>en</strong>ciales. Para calcular una media se necesita sumar y contar los valores. Por consigui<strong>en</strong>te,<br />

nuestro algoritmo <strong>en</strong> forma <strong>de</strong>scriptiva sería:<br />

1. inicializar contador <strong>de</strong> numeros C y variable suma S.<br />

2. Leer un numero<br />

3. Si el numero leído es cero :<br />

O calcular la media ;<br />

O imprimir la media ;<br />

O fin <strong>de</strong>l proceso.<br />

Si el numero leido no es cero :<br />

O calcular la suma ;<br />

0 increm<strong>en</strong>tar <strong>en</strong> uno el contador <strong>de</strong> números ;<br />

O ir al paso 2.<br />

4. Fin.


46 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El refinami<strong>en</strong>to <strong>de</strong>l algoritmo conduce a los pasos sucesivos necesarios para realizar las operaciones<br />

<strong>de</strong> lectura, verificación <strong>de</strong>l Último dato, suma y media <strong>de</strong> los <strong>datos</strong>.<br />

Si el primer dato leído es O , la división s / c produciría un error si se ejecutara el algoritmo <strong>en</strong> una<br />

computadora, ya que no está permitida <strong>en</strong> ella la división por cero.<br />

S - sumador <strong>de</strong> números<br />

leer dato<br />

v<br />

no<br />

dato O O<br />

S f S + dato<br />

Si el primer dato leído es O, la división s /C'<br />

producirá un error si se ejecutara el<br />

Media 4- SIC<br />

algoritmo <strong>en</strong> una computadora, ya que no<br />

está permitida <strong>en</strong> ella la división por cero.<br />

i<br />

Imprimir<br />

media


Fundam<strong>en</strong>tos <strong>de</strong> programación 47 ' j<br />

Ejemplo 2.4<br />

Suma <strong>de</strong> los números pares compr<strong>en</strong>didos <strong>en</strong>tre 2 y I O O.<br />

I<br />

I<br />

Inicio<br />

<br />

.<br />

SUMA f- 2<br />

SUMA f-<br />

9<br />

NUMERO = < 100<br />

1<br />

Escribir<br />

SUMA ,/<br />

Ejemplo 2.5<br />

Se <strong>de</strong>sea realizar el algoritmo que resuelva el sigui<strong>en</strong>te problema: Cúlculo <strong>de</strong> Ins salarios m<strong>en</strong>suales <strong>de</strong><br />

los empleados <strong>de</strong> una empresa, subi<strong>en</strong>do que éstos se calculan <strong>en</strong> base a las horas semanales trahajadas<br />

JJ <strong>de</strong> acuerdo a un precio especificado por horas. Si se pasan <strong>de</strong> cuar<strong>en</strong>ta horas semanales, las<br />

horas extraordinarias se pagarán a razón <strong>de</strong> 1.5 veces lu hora ordinaria.<br />

Los cálculos son:<br />

1. Leer <strong>datos</strong> <strong>de</strong>l archivo <strong>de</strong> 1u. empresd, hasta que se <strong>en</strong>cu<strong>en</strong>tre la ficha<br />

final <strong>de</strong>l archivo (HORAS, PRECIO-HORA, NOMBRE).<br />

2. Si HORAS 40, <strong>en</strong>tonces SAL,ARIO es Id sum <strong>de</strong> 40 veces PRECIO-HORA más<br />

1.5 veces PRECIO-HORA por (HORAS-40).


48 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El diagrama <strong>de</strong> flujo completo <strong>de</strong>l algoritmo se indica a continuación:<br />

Inicio<br />

HORAS, PRECIO-HORA<br />

<<br />

NOMBRE<br />

HORAS =40<br />

HORAS*<br />

PRECIO-HORA<br />

40* PRECIOpHORA+<br />

1,5* PRECIO-HORA*<br />

(HORAS-40)<br />

Si<br />

7<br />

Escribir<br />

SALARIO<br />

4


~<br />

Fundam<strong>en</strong>tos <strong>de</strong> programación 49 'I<br />

i<br />

Una variante también válida al diagrama <strong>de</strong> flujo anterior es:<br />

0<br />

¿más <strong>datos</strong>?<br />

JI si<br />

Leer<br />

HORAS, PRECIO-HORA<br />

NOMBRE<br />


50 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3. Si no quedan numeros, imprimir el valor <strong>de</strong> TOTAL y fin.<br />

4. Si exist<strong>en</strong> mas numeros, ejecutar los pasos 5 a 8.<br />

5. Leer el sigui<strong>en</strong>te numero y dar su valor a la variable NUMERO.<br />

6. Si NUMERO = O, increm<strong>en</strong>tar TOTAL <strong>en</strong> 1<br />

7. Si NUMERO O, no modificar TOTAL.<br />

8. Retornar al paso 2.<br />

El diagrama <strong>de</strong> flujo correspondi<strong>en</strong>te es:<br />

Inicio<br />

TOTAL +- o<br />

f si<br />

“7<br />

NUMERO<br />

TOTAL t-<br />

TOTAL + 1<br />

&<br />

Escribir<br />

TOTAL<br />

~~<br />

Ejemplo 2.7<br />

Dados tres números, <strong>de</strong>terminar si la suma <strong>de</strong> cualquier preju <strong>de</strong> ellos es igual u1 tercer número. Si se<br />

cumple esta condición, escribir «Iguales» y, <strong>en</strong> cuso contrurio, escribir «Distintas».<br />

En el caso <strong>de</strong> que los números sean: 3 9 6


Fundam<strong>en</strong>tos <strong>de</strong> programación 51<br />

la respuesta es "Iguales", ya que 3 + 6 = 9. Sin embargo, si los números fueran:<br />

2 3 4<br />

el resultado sería 'Distintas".<br />

Para resolver este problema, se pue<strong>de</strong> comparar la suma <strong>de</strong> cada pareja con el tercer número. Con<br />

tres números solam<strong>en</strong>te exist<strong>en</strong> tres parejas distintas y el algoritmo <strong>de</strong> resolución <strong>de</strong>l problema será<br />

fácil.<br />

1. Leer los tres valores, A, B y C.<br />

2. Si A + B = C escribir "Iguales" y parar.<br />

3. Si A + C = B escribir "Iguales" y parar.<br />

4. Si B + C = A escribir "Iguales" y parar.<br />

5. Escribir 'Distintas" y parar.<br />

El diagrama <strong>de</strong> flujo correspondi<strong>en</strong>te es la Figura 2. I O.<br />

a<br />

Inicio<br />

A+C=B<br />

-<br />

B+C=A<br />


52 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.5. DIAGRAMAS DE NASSI-SCHNEIDERMAN (N-S)<br />

El diagrama N-S <strong>de</strong> Nassi Schnei<strong>de</strong>rman -también conocido como diagrama <strong>de</strong> Chapin- es como<br />

un diagrama <strong>de</strong> flujo <strong>en</strong> el que se omit<strong>en</strong> las flechas <strong>de</strong> unión y las cajas son contiguas. Las acciones<br />

sucesivas se escrib<strong>en</strong> <strong>en</strong> cajas sucesivas y, como <strong>en</strong> los diagramas <strong>de</strong> flujo, se pued<strong>en</strong> escribir difer<strong>en</strong>tes<br />

acciones <strong>en</strong> una caja.<br />

Un algoritmo se repres<strong>en</strong>ta con un rectángulo <strong>en</strong> el que cada banda es una acción a realizar:<br />

nombre, horas, precio<br />

calcular<br />

I<br />

I<br />

calcular<br />

impuestos t 0.25 * salario<br />

calcular<br />

neto t salario - impuestos<br />

escribir<br />

nombre, salario, impuestos, neto<br />

I<br />

nombre <strong>de</strong>l algoritmo<br />

<br />

<br />

<br />

...<br />

fin<br />

Figura 2.11. Repres<strong>en</strong>tación gráfica N-S <strong>de</strong> un algoritmo.<br />

Otro ejemplo es la repres<strong>en</strong>tación <strong>de</strong> la <strong>estructura</strong> condicional (Fig. 2.12).<br />

'condición?<br />

1<br />

acción acción<br />

u<br />

(b)<br />

2<br />

<br />

<br />

Figura 2.12. Estructura condicional o selectiva: (a) diagrama <strong>de</strong> flujo: (b) diagrama N-S.


Fundam<strong>en</strong>tos <strong>de</strong> programación 53<br />

Ejemplo 2.8<br />

Se <strong>de</strong>sea calcular el salario neto semanal <strong>de</strong> un trabajador <strong>en</strong> función <strong>de</strong>l número <strong>de</strong> horas trabajadas<br />

y la tasa <strong>de</strong> impuestos:<br />

o las primeras 35 horas se pagan a tarifa normal,<br />

las horas que pas<strong>en</strong> <strong>de</strong> 35 se pagan a 1,5 veces la tarifa normal,<br />

o las tasas <strong>de</strong> impuestos son:<br />

a) las primeras 60.000 pesetas son libres <strong>de</strong> impuestos,<br />

b) las sigui<strong>en</strong>tes 40.000peseta.s ti<strong>en</strong><strong>en</strong> un 25 por 100 <strong>de</strong> impuesto,<br />

c) las restantes, un 45 por 100 <strong>de</strong> impuestos,<br />

o la tarifa horaria es 800peseta.s.<br />

También se <strong>de</strong>sea escribir el nombre, salario bruto, tasas y salario neto (este ejemplo se <strong>de</strong>ja como<br />

ejercicio al alumno).<br />

2.6. EL CICLO DE VIDA DEL SOFTWARE<br />

Exist<strong>en</strong> dos niveles <strong>en</strong> la construcción <strong>de</strong> programas: aquéllos relativos a pequeños programas (los que<br />

normalm<strong>en</strong>te realizan programadores individuales) y aquellos que se refier<strong>en</strong> a sistemas <strong>de</strong> <strong>de</strong>sarrollo<br />

<strong>de</strong> programas gran<strong>de</strong>s (proyectos <strong>de</strong> software) y que, g<strong>en</strong>eralm<strong>en</strong>te, requier<strong>en</strong> un equipo <strong>de</strong> programadores<br />

<strong>en</strong> lugar <strong>de</strong> personas individuales. El primer nivel se d<strong>en</strong>omina programación a pequeña escala;<br />

el segundo nivel se d<strong>en</strong>omina programación a gran escala.<br />

La programación <strong>en</strong> pequeña escala se preocupa <strong>de</strong> los conceptos que ayudan a crear pequeños programas<br />

-aquellos que vm'an <strong>en</strong> longitud <strong>de</strong>s<strong>de</strong> unas pocas líneas a unas pocas páginas-. En estos programas<br />

se suele requerir claridad y precisión m<strong>en</strong>tal y técnica. En realidad, el interés mayor <strong>de</strong>s<strong>de</strong> el<br />

punto <strong>de</strong> vista <strong>de</strong>l futuro programador profesional está <strong>en</strong> los programas <strong>de</strong> gran escala que requiere <strong>de</strong><br />

unos principios sólidos y firmes <strong>de</strong> lo que se conoce como ing<strong>en</strong>ieria <strong>de</strong> software y que constituye un<br />

conjunto <strong>de</strong> técnicas para facilitar el <strong>de</strong>sarrollo <strong>de</strong> programas <strong>de</strong> computadora. Estos programas o mejor<br />

proyectos <strong>de</strong> software están realizados por equipos <strong>de</strong> personas dirigidos por un director <strong>de</strong> proyectos<br />

(analista o ing<strong>en</strong>iero <strong>de</strong> software) y los programas pued<strong>en</strong> t<strong>en</strong>er más <strong>de</strong> 100.000 líneas <strong>de</strong> código.<br />

El <strong>de</strong>sarrollo <strong>de</strong> un bu<strong>en</strong> sistema <strong>de</strong> software se realiza durante el ciclo <strong>de</strong> vida que es el período <strong>de</strong><br />

tiempo que se exti<strong>en</strong><strong>de</strong> <strong>de</strong>s<strong>de</strong> la concepción inicial <strong>de</strong>l sistema hasta su ev<strong>en</strong>tual retirada <strong>de</strong> la comercialización<br />

o uso <strong>de</strong>l mismo. Las activida<strong>de</strong>s humanas relacionadas con el ciclo <strong>de</strong> vida implican procesos<br />

tales como análisis <strong>de</strong> requisitos, diseño, implem<strong>en</strong>tación, codificación, pruebas, verificación,<br />

docum<strong>en</strong>tación, mant<strong>en</strong>imi<strong>en</strong>to y evolución <strong>de</strong>l sistema y obsolesc<strong>en</strong>cia. En es<strong>en</strong>cia el ciclo <strong>de</strong> vida <strong>de</strong>l<br />

software comi<strong>en</strong>za con una i<strong>de</strong>a inicial, incluye la escritura y <strong>de</strong>puración <strong>de</strong> programas, y continúa<br />

durante años con correcciones y mejoras al software original4.<br />

pi-/-$<br />

IMPLEMENTACI~N<br />

3.<br />

DEPURACI~N<br />

+<br />

Figura 2.13. Ciclo <strong>de</strong> vida <strong>de</strong>l software.<br />

MANTENIMIENTO<br />

' Camano, Hellman y Verof: Dutti .structur


54 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El ciclo <strong>de</strong> vida <strong>de</strong>l software es un proceso iterativo, <strong>de</strong> modo que se modificarán las sucesivas etapas<br />

<strong>en</strong> función <strong>de</strong> la modificación <strong>de</strong> las especificaciones <strong>de</strong> los requisitos producidos <strong>en</strong> la fase <strong>de</strong> diseño<br />

o implem<strong>en</strong>tación, o bi<strong>en</strong> una vez que el sistema se ha implem<strong>en</strong>tado, y probado, pued<strong>en</strong> aparecer<br />

errores que será necesario corregir y <strong>de</strong>purar, y que requier<strong>en</strong> la repetición <strong>de</strong> etapas anteriores.<br />

La Figura 2.13 muestra el ciclo <strong>de</strong> vida <strong>de</strong> software y la disposición típica <strong>de</strong> sus difer<strong>en</strong>tes etapas<br />

<strong>en</strong> el sistema conocido como ciclo <strong>de</strong> vida <strong>en</strong> cascada, que supone que la salida <strong>de</strong> cada etapa es la<br />

<strong>en</strong>trada <strong>de</strong> la etapa sigui<strong>en</strong>te.<br />

2.6.1. Análisis<br />

La primera etapa <strong>en</strong> la producción <strong>de</strong> un sistema <strong>de</strong> software es <strong>de</strong>cidir exactam<strong>en</strong>te qué se supone ha<br />

<strong>de</strong> hacer el sistema. Esta etapa se conoce también como análisis <strong>de</strong> requisitos o especificaciones y por<br />

esta circunstancia muchos tratadistas suel<strong>en</strong> subdividir la etapa <strong>en</strong> otras dos:<br />

Análisis y <strong>de</strong>finición <strong>de</strong>l problema.<br />

Especificación <strong>de</strong> requisitos.<br />

La parte más difícil <strong>en</strong> la tarea <strong>de</strong> crear un sistema <strong>de</strong> software es <strong>de</strong>finir cuál es el problema, y a<br />

continuación especificar lo que se necesita para resolverlo. Normalm<strong>en</strong>te la <strong>de</strong>finición <strong>de</strong>l problema<br />

comi<strong>en</strong>za analizando los requisitos <strong>de</strong>l usuario, pero estos requisitos, con frecu<strong>en</strong>cia, suel<strong>en</strong> ser imprecisos<br />

y difíciles <strong>de</strong> <strong>de</strong>scribir. Se <strong>de</strong>b<strong>en</strong> especificar todos los aspectos <strong>de</strong>l problema, pero con frecu<strong>en</strong>cia<br />

las personas que <strong>de</strong>scrib<strong>en</strong> el problema no son programadores y eso hace imprecisa la <strong>de</strong>finición. La fase<br />

<strong>de</strong> especificación requiere normalm<strong>en</strong>te la comunicación <strong>en</strong>tre los programadores y los futuros usuarios<br />

<strong>de</strong>l sistema e iterar la especificación, hasta que tanto el especificador como los usuarios estén satisfechos<br />

<strong>de</strong> las especificaciones y hayan resuelto el problema normalm<strong>en</strong>te.<br />

En la etapa <strong>de</strong> especificaciones pue<strong>de</strong> ser muy Útil para mejorar la comunicación <strong>en</strong>tre las difer<strong>en</strong>tes<br />

partes implicadas construir un prototipo o mo<strong>de</strong>lo s<strong>en</strong>cillo <strong>de</strong>l sistema final; es <strong>de</strong>cir, escribir un<br />

programa prototipo que simule el comportami<strong>en</strong>to <strong>de</strong> las partes <strong>de</strong>l producto software <strong>de</strong>seado. Por<br />

ejemplo, un programa s<strong>en</strong>cillo -incluso inefici<strong>en</strong>te- pue<strong>de</strong> <strong>de</strong>mostrar al usuario la interfaz propuesta<br />

por el analista. Es mejor <strong>de</strong>scubrir cualquier dificultad o cambiar su i<strong>de</strong>a original ahora que <strong>de</strong>spués<br />

<strong>de</strong> que la programación se <strong>en</strong>cu<strong>en</strong>tre <strong>en</strong> estado avanzado o, incluso, terminada. El mo<strong>de</strong>lado <strong>de</strong> <strong>datos</strong> es<br />

una herrami<strong>en</strong>ta muy importante <strong>en</strong> la etapa <strong>de</strong> <strong>de</strong>finición <strong>de</strong>l problema. Esta herrami<strong>en</strong>ta es muy utilizada<br />

<strong>en</strong> el diseño y construcción <strong>de</strong> bases <strong>de</strong> <strong>datos</strong>.<br />

T<strong>en</strong>ga pres<strong>en</strong>te que el usuario final, normalm<strong>en</strong>te, no conoce exactam<strong>en</strong>te lo que <strong>de</strong>sea que haga el<br />

sistema. Por consigui<strong>en</strong>te, el analista <strong>de</strong> software o programador, <strong>en</strong> su caso, <strong>de</strong>be interactuar con el<br />

usuario para <strong>en</strong>contrar lo que el usuario <strong>de</strong>seará que haga el sistema. En esta etapa se <strong>de</strong>be respon<strong>de</strong>r<br />

a preguntas tales como:<br />

¿Cuáles son los <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada?<br />

¿Qué <strong>datos</strong> son válidos y qué <strong>datos</strong> no son válidos'?<br />

¿Quién utilizará el sistema: especialistas cualificados o usuarios cualesquiera (sin formación)?<br />

¿Qué interfaces <strong>de</strong> usuario se utilizarán?<br />

¿,Cuáles son los m<strong>en</strong>sajes <strong>de</strong> error y <strong>de</strong> <strong>de</strong>tección <strong>de</strong> errores <strong>de</strong>seables? ¿Cómo <strong>de</strong>be actuar el sistema<br />

cuando el usuario cometa un error <strong>en</strong> la <strong>en</strong>trada?<br />

¿Qué hipótesis son posibles?<br />

¿Exist<strong>en</strong> casos especiales?<br />

¿,Cuál es el formato <strong>de</strong> la salida?<br />

¿Qué docum<strong>en</strong>tación es necesaria?<br />

¿Qué mejoras se introducirán -probablem<strong>en</strong>te- al programa <strong>en</strong> el futuro?<br />

¿,Cómo <strong>de</strong>be ser <strong>de</strong> rápido el sistema?<br />

¿Cada cuanto tiempo ha <strong>de</strong> cambiarse el sistema <strong>de</strong>spués que se haya <strong>en</strong>tregado?<br />

El resultado final <strong>de</strong> la fase <strong>de</strong> análisis es una especificación <strong>de</strong> los requisitos <strong>de</strong>l sc$ware.


Fundam<strong>en</strong>tos <strong>de</strong> programación 55<br />

0 Descripción <strong>de</strong>l problema previa y <strong>de</strong>talladam<strong>en</strong>te.<br />

0 Prototipos <strong>de</strong> programas que pued<strong>en</strong> ayudar a resolver el problema.<br />

2.6.2. Diseño<br />

La especificación <strong>de</strong> un sistema indica lo que el sistema <strong>de</strong>be hacer. La etapa <strong>de</strong> diseño <strong>de</strong>l sistema<br />

indica cómo ha <strong>de</strong> hacerse. Para un sistema pequeño, la etapa <strong>de</strong> diseño pue<strong>de</strong> ser tan s<strong>en</strong>cilla como<br />

escribir un algoritmo <strong>en</strong> pseudocódigo. Para un sistema gran<strong>de</strong>, esta etapa incluye también la fase <strong>de</strong><br />

diseño <strong>de</strong> <strong>algoritmos</strong>, pero incluye el diseño e interacción <strong>de</strong> un número <strong>de</strong> <strong>algoritmos</strong> difer<strong>en</strong>tes, con<br />

frecu<strong>en</strong>cia sólo bosquejados, así como una estrategia para cumplir todos los <strong>de</strong>talles y producir el código<br />

correspondi<strong>en</strong>te.<br />

Es preciso <strong>de</strong>terminar si se pued<strong>en</strong> utilizar programas o subprogramas que ya exist<strong>en</strong> o es preciso<br />

construirlos totalm<strong>en</strong>te. El proyecto se ha <strong>de</strong> dividir <strong>en</strong> módulos utilizando los principios <strong>de</strong> diseño <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te.<br />

A continuación, se <strong>de</strong>be indicar la interacción <strong>en</strong>tre módulos; un diagrama <strong>de</strong> <strong>estructura</strong>s proporciona<br />

un esquema claro <strong>de</strong> estas relaciones’.<br />

En este punto, es importante especificar claram<strong>en</strong>te no sólo el propósito <strong>de</strong> cada módulo, sino también<br />

elpujo <strong>de</strong> duros <strong>en</strong>tre módulos. Por ejemplo, se <strong>de</strong>be respon<strong>de</strong>r a las sigui<strong>en</strong>tes preguntas: ¿Qué<br />

<strong>datos</strong> están disponibles al módulo antes <strong>de</strong> su ejecución? ¿Qué supone el módulo? ¿Qué hac<strong>en</strong> los <strong>datos</strong><br />

<strong>de</strong>spués <strong>de</strong> que se ejecuta el módulo? Por consigui<strong>en</strong>te, se <strong>de</strong>b<strong>en</strong> especificar <strong>en</strong> <strong>de</strong>talle las hipótesis,<br />

<strong>en</strong>trada y salida para cada módulo. Un medio para realizar estas especificaciones es escribir una precondición,<br />

que es una <strong>de</strong>scripción <strong>de</strong> las condiciones que <strong>de</strong>b<strong>en</strong> cumplirse al principio <strong>de</strong>l módulo y<br />

una postcondición, que es una <strong>de</strong>scripción <strong>de</strong> las condiciones al final <strong>de</strong> un módulo. Por ejemplo, se<br />

pue<strong>de</strong> <strong>de</strong>scribir un subprograma que ord<strong>en</strong>a una lista (un array) <strong>de</strong> la forma sigui<strong>en</strong>te:<br />

subprograma ord<strong>en</strong>ar (A, n)<br />

{Ord<strong>en</strong>a una lista <strong>en</strong> ord<strong>en</strong> asc<strong>en</strong>d<strong>en</strong>te}<br />

precondición: A es un array <strong>de</strong> n <strong>en</strong>teros, 1


56 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

han <strong>de</strong> traducirse codificados <strong>en</strong> un l<strong>en</strong>guaje que <strong>en</strong>ti<strong>en</strong><strong>de</strong> la computadora: PASCAL, FORTRAN,<br />

COBOL, C, C++, C# o Java.<br />

La codificacion cuando un problema se divi<strong>de</strong> <strong>en</strong> subproblemas, los <strong>algoritmos</strong> que resuelv<strong>en</strong> cada<br />

subproblema (tarea o módulo) <strong>de</strong>b<strong>en</strong> ser codificados, <strong>de</strong>purados y probados in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te.<br />

Es relativam<strong>en</strong>te fácil <strong>en</strong>contrar un error <strong>en</strong> un procedimi<strong>en</strong>to pequeño. Es casi imposible <strong>en</strong>contrar<br />

todos los errores <strong>de</strong> un programa gran<strong>de</strong>, que se codificó y comprobó como una sola unidad <strong>en</strong> lugar <strong>de</strong><br />

como una colección <strong>de</strong> módulos (procedimi<strong>en</strong>tos) bi<strong>en</strong> <strong>de</strong>finidos.<br />

Las reglas <strong>de</strong>l sangrado (ind<strong>en</strong>tación) y bu<strong>en</strong>os com<strong>en</strong>tarios facilitan la escritura <strong>de</strong>l código. El<br />

pseudocódigo es una herrami<strong>en</strong>ta excel<strong>en</strong>te que facilita notablem<strong>en</strong>te la codificación.<br />

2.6.4. Pruebas e integración<br />

Cuando los difer<strong>en</strong>tes compon<strong>en</strong>tes <strong>de</strong> un programa se han implem<strong>en</strong>tado y comprobado individualm<strong>en</strong>te,<br />

el sistema completo se <strong>en</strong>sambla y se integra.<br />

La etapa <strong>de</strong> pruebas sirve para mostrar que un programa es correcto. Las pruebas nunca son fáciles.<br />

Edgar Dijkstra ha escrito que mi<strong>en</strong>tras que las pruebas realm<strong>en</strong>te muestran lapres<strong>en</strong>cia <strong>de</strong> errores, nunca<br />

pue<strong>de</strong> mostrar su aus<strong>en</strong>cia. Una prueba con «éxito» <strong>en</strong> la ejecución significa sólo que no se han <strong>de</strong>scubierto<br />

errores <strong>en</strong> esas circunstancias específicas, pero no se dice nada <strong>de</strong> otras circunstancias. En teoría<br />

el Único modo que una prueba pue<strong>de</strong> mostrar que un programa es correcto si todos los casos posibles<br />

se han int<strong>en</strong>tado y comprobado (es lo que se conoce como prueba exhaustiva); es una situación técnicam<strong>en</strong>te<br />

imposible incluso para los programas más s<strong>en</strong>cillos. Supongamos, por ejemplo, que se ha escrito<br />

un programa que calcule la nota media <strong>de</strong> un exam<strong>en</strong>. Una prueba exhaustiva requerirá todas las combinaciones<br />

posibles <strong>de</strong> marcas y tamaños <strong>de</strong> clases; pue<strong>de</strong> llevar muchos años completar la prueba.<br />

La fase <strong>de</strong> pruebas es una parte es<strong>en</strong>cial <strong>de</strong> un proyecto <strong>de</strong> programación. Durante la fase <strong>de</strong> pruebas<br />

se necesita eliminar tantos errores lógicos como pueda. En primer lugar, se <strong>de</strong>be probar el programa<br />

con <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada válidos que conduc<strong>en</strong> a una solución conocida. Si ciertos <strong>datos</strong> <strong>de</strong>b<strong>en</strong> estar<br />

d<strong>en</strong>tro <strong>de</strong> un rango, se <strong>de</strong>b<strong>en</strong> incluir los valores <strong>en</strong> los extremos finales <strong>de</strong>l rango. Por ejemplo, si el<br />

valor <strong>de</strong> <strong>en</strong>trada <strong>de</strong> n cae <strong>en</strong> el rango <strong>de</strong> 1 a 10, se ha <strong>de</strong> asegurar incluir casos <strong>de</strong> prueba <strong>en</strong> los que n<br />

esté <strong>en</strong>tre 1 y 10. También se <strong>de</strong>b<strong>en</strong> incluir <strong>datos</strong> no válidos para comprobar la capacidad <strong>de</strong> <strong>de</strong>tección<br />

<strong>de</strong> errores <strong>de</strong>l programa. Se han <strong>de</strong> probar también algunos <strong>datos</strong> aleatorios y, por Último, int<strong>en</strong>tar algunos<br />

<strong>datos</strong> reales.<br />

2.6.5. Verificación<br />

La etapa <strong>de</strong> pruebas ha <strong>de</strong> com<strong>en</strong>zar tan pronto como sea posible <strong>en</strong> la fase <strong>de</strong> diseño y continuará a lo<br />

largo <strong>de</strong> la implem<strong>en</strong>tación <strong>de</strong>l sistema. Incluso aunque las pruebas son herrami<strong>en</strong>tas extremadam<strong>en</strong>te<br />

válidas para proporcionar la evid<strong>en</strong>cia <strong>de</strong> que un programa es correcto y cumple sus especificaciones,<br />

es difícil conocer si las pruebas realizadas son sufici<strong>en</strong>tes. Por ejemplo,.¿cómo se pue<strong>de</strong> conocer que son<br />

sufici<strong>en</strong>tes los difer<strong>en</strong>tes conjuntos <strong>de</strong> <strong>datos</strong> <strong>de</strong> prueba o que se han ejecutado todos los caminos posibles<br />

a través <strong>de</strong>l programa?<br />

Por esas razones se ha <strong>de</strong>sarrollado un segundo método para <strong>de</strong>mostrar la corrección o exactitud <strong>de</strong><br />

un programa. Este método, d<strong>en</strong>ominado verijicación formal implica la construcción <strong>de</strong> pruebas matemáticas<br />

que ayudan a <strong>de</strong>terminar si los programas hac<strong>en</strong> lo que se supone han <strong>de</strong> hacer. La verificación<br />

formal implica la aplicación <strong>de</strong> reglas formales para mostrar que un programa cumple su especificación:<br />

la verificación. La verificación formal funciona bi<strong>en</strong> <strong>en</strong> programas pequeños, pero es compleja cuando<br />

se utiliza <strong>en</strong> programas gran<strong>de</strong>s. La teoría <strong>de</strong> la verificación requiere conocimi<strong>en</strong>tos matemáticos avanzados<br />

y, por otra parte, se sale fuera <strong>de</strong> los objetivos <strong>de</strong> este libro; por esta razón sólo hemos constatado<br />

la importancia <strong>de</strong> esta etapa.<br />

La prueba <strong>de</strong> que un algoritmo es correcto es como probar un teorema matemático. Por ejemplo,<br />

probar que un módulo es exacto (correcto) comi<strong>en</strong>za con las precondiciones (axiomas e hipótesis <strong>en</strong>


Fundam<strong>en</strong>tos <strong>de</strong> programación 57<br />

matemáticas) y muestra que las etapas <strong>de</strong>l algoritmo conduc<strong>en</strong> a las postcondiciones. La verificación trata<br />

<strong>de</strong> probar con medios matemáticos que los <strong>algoritmos</strong> son correctos.<br />

Si se <strong>de</strong>scubre un error durante el proceso <strong>de</strong> verificación, se <strong>de</strong>be corregir su algoritmo y posiblem<strong>en</strong>te<br />

se han <strong>de</strong> modificar las especificaciones <strong>de</strong>l problema. Un método es utilizar invariantes (una<br />

condición que siempre es verda<strong>de</strong>ra <strong>en</strong> un punto específico <strong>de</strong> un algoritmo) lo que probablem<strong>en</strong>te hará<br />

que su algoritmo cont<strong>en</strong>ga pocos errores antes <strong>de</strong> que comi<strong>en</strong>ce la codificación. Como resultado se gastará<br />

m<strong>en</strong>os tiempo <strong>en</strong> la <strong>de</strong>puración <strong>de</strong> su programa.<br />

2.6.6. Mant<strong>en</strong>imi<strong>en</strong>to<br />

Cuando el producto software (el programa) se ha terminado, se distribuye <strong>en</strong>tre los posibles usuarios, se<br />

instala <strong>en</strong> las computadoras y se utiliza (producción). Sin embargo, y aunque, a priori, el programa<br />

funcione correctam<strong>en</strong>te, el software <strong>de</strong>be ser mant<strong>en</strong>ido y actualizado. De hecho, el coste típico <strong>de</strong>l<br />

mant<strong>en</strong>imi<strong>en</strong>to exce<strong>de</strong>, con creces, el coste <strong>de</strong> producción <strong>de</strong>l sistema original.<br />

Un sistema <strong>de</strong> software producirá errores que serán <strong>de</strong>tectados, casi con seguridad, por los usuanos<br />

<strong>de</strong>l sistema y que no se <strong>de</strong>scubrieron durante la fase <strong>de</strong> prueba. La corrección <strong>de</strong> estos errores es parte<br />

<strong>de</strong>l mant<strong>en</strong>imi<strong>en</strong>to <strong>de</strong>l software. Otro aspecto <strong>de</strong> la fase <strong>de</strong> mant<strong>en</strong>imi<strong>en</strong>to es la mejora <strong>de</strong>l software<br />

añadi<strong>en</strong>do más características o modificando partes exist<strong>en</strong>tes que se adapt<strong>en</strong> mejor a los usuarios.<br />

Otras causas que obligarán a revisar el sistema <strong>de</strong> software <strong>en</strong> la etapa <strong>de</strong> mant<strong>en</strong>imi<strong>en</strong>to son las<br />

sigui<strong>en</strong>tes: 1) Cuando un nuevo hardware se introduce, el sistema pue<strong>de</strong> ser modificado para ejecutarlo<br />

<strong>en</strong> un nuevo <strong>en</strong>torno; 2) Si cambian las necesida<strong>de</strong>s <strong>de</strong>l usuario, suele ser m<strong>en</strong>os caro y más rápido,<br />

modificar el sistema exist<strong>en</strong>te que producir un sistema totalm<strong>en</strong>te nuevo. La mayor parte <strong>de</strong>l tiempo <strong>de</strong><br />

los programadores <strong>de</strong> un sistema se gasta <strong>en</strong> el mant<strong>en</strong>imi<strong>en</strong>to <strong>de</strong> los sistemas exist<strong>en</strong>tes y no <strong>en</strong> el diseño<br />

<strong>de</strong> sistemas totalm<strong>en</strong>te nuevos. Por esta causa, <strong>en</strong>tre otras, se ha <strong>de</strong> tratar siempre <strong>de</strong> diseñar programas<br />

<strong>de</strong> modo que sean fáciles <strong>de</strong> compr<strong>en</strong><strong>de</strong>r y <strong>en</strong>t<strong>en</strong><strong>de</strong>r (legibles) y fáciles <strong>de</strong> cambiar.<br />

2.6.7. La obsolesc<strong>en</strong>cia: prog r a mas o bso I et os<br />

La última etapa <strong>en</strong> el ciclo <strong>de</strong> vida <strong>de</strong>l software es la evolución <strong>de</strong>l mismo, pasando por su vida útil hasta<br />

su ohsolesc<strong>en</strong>cia o fase <strong>en</strong> la que el software se queda anticuado y es preciso actualizarlo o escribir<br />

un nuevo programa sustitutorio <strong>de</strong>l antiguo.<br />

La <strong>de</strong>cisión <strong>de</strong> dar <strong>de</strong> baja un software por obsoleto no es una <strong>de</strong>cisión fácil. Un sistema gran<strong>de</strong><br />

repres<strong>en</strong>ta una inversión <strong>en</strong>orme <strong>de</strong> capital que parece, a primera vista, más barato modificar el sistema<br />

exist<strong>en</strong>te, <strong>en</strong> vez <strong>de</strong> construir un sistema totalm<strong>en</strong>te nuevo. Este criterio suele ser, normalm<strong>en</strong>te, correcto<br />

y por esta causa los sistemas gran<strong>de</strong>s se diseñan para ser modificados. Un sistema pue<strong>de</strong> ser productivam<strong>en</strong>te<br />

revisado muchas veces. Sin embargo, incluso los programas gran<strong>de</strong>s se quedan obsoletos por<br />

caducidad <strong>de</strong> tiempo al pasar una fecha límite <strong>de</strong>terminada. A m<strong>en</strong>os que un programa gran<strong>de</strong> esté bi<strong>en</strong><br />

escrito y a<strong>de</strong>cuado a la tarea a realizar, como <strong>en</strong> el caso <strong>de</strong> programas pequeños, suele ser más efici<strong>en</strong>te<br />

escribir un nuevo programa que corregir el programa antiguo.<br />

2.6.8. Iteración y evolución <strong>de</strong>l software<br />

Las etapas <strong>de</strong> vida <strong>de</strong>l software suel<strong>en</strong> formar parte <strong>de</strong> un ciclo o bucle, como su nombre sugiere y no<br />

son simplem<strong>en</strong>te una lista lineal. Es probable, por ejemplo, que durante la fase <strong>de</strong> mant<strong>en</strong>imi<strong>en</strong>to t<strong>en</strong>ga<br />

que volver a las especificaciones <strong>de</strong>l problema para verificarlas o modificarlas.<br />

Obsérvese <strong>en</strong> la Figura 2.14 las difer<strong>en</strong>tes etapas que ro<strong>de</strong>an al núcleo: docum<strong>en</strong>tación. La docum<strong>en</strong>tación<br />

no es una etapa in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te como se pue<strong>de</strong> esperar sino que está integrada <strong>en</strong> todas las<br />

etapas <strong>de</strong>l ciclo <strong>de</strong> vida <strong>de</strong>l software.


58 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Figura 2.14. Etapas <strong>de</strong>l ciclo <strong>de</strong> vida <strong>de</strong>l software cuyo núcleo aglutinador es la docum<strong>en</strong>tación.<br />

2.7. MÉTODOS FORMALES DE VERIFICACIÓN DE PROGRAMAS<br />

Aunque la verificación formal <strong>de</strong> programas se sale fuera <strong>de</strong>l ámbito <strong>de</strong> este libro, por su importancia<br />

vamos a consi<strong>de</strong>rar dos conceptos clave, aser’os (afirmaciones) y precondiciones/postcondiciones invariantes<br />

que ayud<strong>en</strong> a docum<strong>en</strong>tar, corregir y clarificar el diseño <strong>de</strong> módulos y <strong>de</strong> programas.<br />

2.7.1. Aserciones’<br />

Una parte importante <strong>de</strong> una verificación fmmal es la docum<strong>en</strong>tación <strong>de</strong> un programa a través <strong>de</strong> asertos<br />

o afirmaciones -s<strong>en</strong>t<strong>en</strong>cias lógicas acerca <strong>de</strong>l programa que se <strong>de</strong>claran «verda<strong>de</strong>ras»-. Un aserto<br />

se escribe como un com<strong>en</strong>tario y <strong>de</strong>scribe lo que se supone sea verda<strong>de</strong>ro sobre las variables <strong>de</strong>l programa<br />

<strong>en</strong> ese punto.<br />

Un aserto es una frase sobre una condición específica <strong>en</strong> un cierto punto <strong>de</strong> un algoritmo o programa.<br />

Ejemplo 2.9<br />

El sigui<strong>en</strong>te fragm<strong>en</strong>to <strong>de</strong> programa conti<strong>en</strong>e una secu<strong>en</strong>cia <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> asignación, seguidas por<br />

un aserto.<br />

A = 10; { aserto: A es 10 1<br />

X = A; { aserto: X es 10 1<br />

Y=X+A; { aserto: Y es 20 1<br />

’ Este término se suck traducir también por c/firtncrcion


Fundam<strong>en</strong>tos <strong>de</strong> programación 59<br />

La verdad <strong>de</strong> la primera afirmación {A es 1 O 1, sigue a la ejecución <strong>de</strong> la primera s<strong>en</strong>t<strong>en</strong>cia con el<br />

conocimi<strong>en</strong>to <strong>de</strong> que 10 es una constante. La verdad <strong>de</strong> la segunda afirmación { x es i 0 }, sigue <strong>de</strong><br />

la ejecución <strong>de</strong> x = A con el conocimi<strong>en</strong>to <strong>de</strong> que A es 1 O. La verdad <strong>de</strong> la tercera afirmación {Y es<br />

2 o } sigue <strong>de</strong> la ejecución Y = x + A con el conocimi<strong>en</strong>to <strong>de</strong> que x es 1 O y A es 1 O. En este segm<strong>en</strong>to<br />

<strong>de</strong>l programa se utilizan afirmaciones como com<strong>en</strong>tarios para docum<strong>en</strong>tar el cambio <strong>en</strong> una variable<br />

<strong>de</strong> programa <strong>de</strong>spués que se ejecuta cada s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> afirmación.<br />

La tarea <strong>de</strong> utilizar verificación formal es probar que un segm<strong>en</strong>to <strong>de</strong> programa cumple su especificación.<br />

La afirmación final se llamapostcondicicín (<strong>en</strong> este caso, {Y es 2 O 1 y sigue a la presunción<br />

inicial o precondición (<strong>en</strong> este caso { 1 O es una constante}), <strong>de</strong>spués que se ejecuta el segm<strong>en</strong>to <strong>de</strong> programa.<br />

A 2.7.2. Precondiciones y postcondiciones<br />

Las precondiciones y postcondiciones son afirmaciones s<strong>en</strong>cillas sobre condiciones al principio y al<br />

final <strong>de</strong> los módulos. Una precondición <strong>de</strong> un procedimi<strong>en</strong>to es una afirmación lógica sobre sus parámetros<br />

<strong>de</strong> <strong>en</strong>trada; se supone que es verda<strong>de</strong>ra cuando se llama al procedimi<strong>en</strong>to. Unapostcondición <strong>de</strong><br />

un procedimi<strong>en</strong>to pue<strong>de</strong> ser una afirmación lógica que <strong>de</strong>scribe el cambio <strong>en</strong> el estado <strong>de</strong>lprograma producido<br />

por la ejecución <strong>de</strong>l procedimi<strong>en</strong>to; la postcondición <strong>de</strong>scribe el efecto <strong>de</strong> llamar al procedimi<strong>en</strong>to.<br />

En otras palabras, la postcondición indica que sera verda<strong>de</strong>ra <strong>de</strong>spués que se ejecute el procedimi<strong>en</strong>to.<br />

Ejemplo 2.10<br />

{Precondiciones y postcondiciones <strong>de</strong>l subprograma LeerEnteros)<br />

subprograma LeerEnteros (Min, Max: Entero;var N: Entero);<br />

{<br />

Lectura <strong>de</strong> un <strong>en</strong>tero <strong>en</strong>tre Min y Max <strong>en</strong> N<br />

Pre : Min y Max son valores asignados<br />

Post: <strong>de</strong>vuelve <strong>en</strong> N el primer valor <strong>de</strong>l dato <strong>en</strong>tre Min y Max<br />

si Min


60 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.7.3. Reglas para prueba <strong>de</strong> programas<br />

Un medio útil para probar que un programa P hace lo que realm<strong>en</strong>te ha <strong>de</strong> hacer es proporcionar aserciones<br />

que expres<strong>en</strong> las condiciones antes y <strong>de</strong>spués <strong>de</strong> que P sea ejecutada. En realidad las aserciones<br />

son como s<strong>en</strong>t<strong>en</strong>cias o <strong>de</strong>claraciones que pued<strong>en</strong> ser o bi<strong>en</strong> verda<strong>de</strong>ras o bi<strong>en</strong> falsas.<br />

La primera aserción, la precondición <strong>de</strong>scribe las condiciones que han <strong>de</strong> ser verda<strong>de</strong>ras antes <strong>de</strong><br />

ejecutar P. La segunda aserción, la postcondicicín, <strong>de</strong>scribe las condiciones que han <strong>de</strong> ser verda<strong>de</strong>ras<br />

<strong>de</strong>spués <strong>de</strong> que P se ha ejecutado (suponi<strong>en</strong>do que la precondición fue verda<strong>de</strong>ra antes). El mo<strong>de</strong>lo<br />

g<strong>en</strong>eral es:<br />

{precondición) {= condiciones logicas que son verda<strong>de</strong>ras antes <strong>de</strong> que P<br />

se ejecute}<br />

(postcondición) { = condiciones logicas que son verda<strong>de</strong>ras<br />

<strong>de</strong>spues <strong>de</strong> que P se ejecute}<br />

Ejemplo 2.1 1<br />

El procedimi<strong>en</strong>to Ord<strong>en</strong>arSeleccion (A, m, n) ord<strong>en</strong>a a los elem<strong>en</strong>tos <strong>de</strong>l array. A [m.. n] <strong>en</strong><br />

ord<strong>en</strong> <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te. El mo<strong>de</strong>lo correspondi<strong>en</strong>te pue<strong>de</strong> escribirse así:<br />

{m I n} {precondicion: A ha <strong>de</strong> t<strong>en</strong>er al m<strong>en</strong>os 1 elem<strong>en</strong>to}<br />

Ord<strong>en</strong>arSeleccion (A,m,n) {programa <strong>de</strong> ord<strong>en</strong>acion a ejecutar}<br />

{A[m] 2 A[m+l] 2 ... 2 A[n] {postcondicion: elem<strong>en</strong>tos <strong>de</strong> A <strong>en</strong> ord<strong>en</strong><br />

<strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te}<br />

Problema 2.2<br />

Encontrar la posición <strong>de</strong>l elem<strong>en</strong>to mayor <strong>de</strong> una lista con indicación <strong>de</strong> precondiciones y postcondiciones.<br />

int EncontrarMax (int* A,int m,int n)<br />

{<br />

/* precondicion : m < n<br />

postcondicion : <strong>de</strong>vuelve posicion elem<strong>en</strong>to mayor <strong>en</strong> A[m..n] */<br />

int i, j;<br />

i = m;<br />

J = n; {asercion}<br />

/* (i = m)"(j = m)"(m < n) */ {^, operador and)<br />

do i<br />

i=i+l;<br />

if (A[i] > A[jl)<br />

j = i;<br />

}while (i


Fundam<strong>en</strong>tos <strong>de</strong> programación 61<br />

Ejemplo 2.12<br />

Un bucle que calcula la suma <strong>de</strong> los n primeros elem<strong>en</strong>tos <strong>de</strong>l array (lista) A:<br />

Un invariante es un predicado que cumple tanto antes como <strong>de</strong>spués <strong>de</strong> cada iteración (vuelta)<br />

y que <strong>de</strong>scribe ia misión <strong>de</strong>l bucle.<br />

lnvariantes <strong>de</strong> bucle como herrami<strong>en</strong>tas <strong>de</strong> diseño<br />

Otra aplicación <strong>de</strong> los invariantes <strong>de</strong> bucle es la especificación <strong>de</strong>l bucle: iniciación, condición <strong>de</strong> repetición<br />

y cuerpo <strong>de</strong>l bucle.<br />

Ejemplo 2.13<br />

Si la invariante <strong>de</strong> un bucle es:<br />

{invariante : i


62 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

para <strong>de</strong>@nir su invariante se ha <strong>de</strong> consi<strong>de</strong>rar que dicha variable <strong>de</strong> control se increm<strong>en</strong>ta antes <strong>de</strong><br />

salir <strong>de</strong>l bucle y manti<strong>en</strong>e su valor~final.<br />

/*precondition n >= I*/<br />

Suma = O;<br />

for (i=l; i


~~~ ~<br />

7<br />

I<br />

Fundam<strong>en</strong>tos <strong>de</strong> <strong>programacion</strong> 63 I<br />

2. Una ejecución <strong>de</strong>l bucle <strong>de</strong>be mant<strong>en</strong>er el invariante. Esto es si el invariante es verda<strong>de</strong>ro<br />

antes <strong>de</strong> cualquier iteración <strong>de</strong>l bucle, <strong>en</strong>tonces se <strong>de</strong>be <strong>de</strong>mostrar que es verda<strong>de</strong>ro <strong>de</strong>spués <strong>de</strong><br />

la iteración. En el ejemplo, el bucle aña<strong>de</strong> A [ j I a Suma y a continuación increm<strong>en</strong>ta j <strong>en</strong> I. Por<br />

consigui<strong>en</strong>te, <strong>de</strong>spués <strong>de</strong> una ejecución <strong>de</strong>l bucle, el elem<strong>en</strong>to añadido más reci<strong>en</strong>tem<strong>en</strong>te a Suma<br />

es A [ j - 1 I ; esto es el invariante que es verda<strong>de</strong>ro <strong>de</strong>spués <strong>de</strong> la iteración.<br />

3. El invariante <strong>de</strong>be capturar la exactitud <strong>de</strong>l algoritmo. Esto es, <strong>de</strong>be <strong>de</strong>mostrar que si el invariante<br />

es verda<strong>de</strong>ro cuando termina el bucle, el algoritmo es correcto. Cuando el bucle <strong>de</strong>l ejemplo<br />

termina, j conti<strong>en</strong>e n y el invariante es verda<strong>de</strong>ro: Suma conti<strong>en</strong>e la suma <strong>de</strong> los elem<strong>en</strong>tos<br />

A [ O I a A [ j -1 I , que es la suma que se trata <strong>de</strong> calcular.<br />

4. El bucle <strong>de</strong>be terminar. Esto es, se <strong>de</strong>be <strong>de</strong>mostrar que el bucle termina <strong>de</strong>spués <strong>de</strong> un número<br />

finito <strong>de</strong> iteraciones. En el ejemplo, j comi<strong>en</strong>za <strong>en</strong> O y a continuación se increm<strong>en</strong>ta <strong>en</strong> 1 <strong>en</strong><br />

cada ejecución <strong>de</strong>l bucle. Por consigui<strong>en</strong>te, j ev<strong>en</strong>tualm<strong>en</strong>te exce<strong>de</strong>rá a n con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia <strong>de</strong>l<br />

valor <strong>de</strong> n . Este hecho y la característica fundam<strong>en</strong>tal <strong>de</strong> while garantizan que el bucle terminará.<br />

La id<strong>en</strong>tificación <strong>de</strong> invariantes <strong>de</strong> bucles, ayuda a escribir bucles correctos. Se repres<strong>en</strong>ta el<br />

invariante como un com<strong>en</strong>tario que prece<strong>de</strong> a cada bucle. En el ejemplo anterior<br />

{InvarianLe: O


64 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

int Suma,Indice;<br />

Suma = O;<br />

for (Indice= m; Indicei=n ;Indice++)<br />

Suma = Suma + Indice;<br />

return Suma;<br />

2.8. FACTORES EN LA CALIDAD DEL SOFTWARE<br />

La construcción <strong>de</strong> software requiere el cumplimi<strong>en</strong>to <strong>de</strong> numerosas características. Entre ellas se <strong>de</strong>stacan<br />

las sigui<strong>en</strong>tes:<br />

Ef ici<strong>en</strong>cia<br />

La efici<strong>en</strong>cia <strong>de</strong> un software es su capacidad para hacer un bu<strong>en</strong> uso <strong>de</strong> los recursos que manipula.<br />

Transportabilidad (portabilidad)<br />

La transportabilidad o portabilidad es la facilidad con la que un software pue<strong>de</strong> ser transportado sobre<br />

difer<strong>en</strong>tes sistemas físicos o lógicos.<br />

Ve rif icabilidad<br />

La verificabilidad -facilidad <strong>de</strong> verificación <strong>de</strong> un software- es su capacidad para soportar los procedimi<strong>en</strong>tos<br />

<strong>de</strong> validación y <strong>de</strong> aceptar juegos <strong>de</strong> test o <strong>en</strong>sayo <strong>de</strong> programas.<br />

Integridad<br />

La integridad es la capacidad <strong>de</strong> un software a proteger sus propios compon<strong>en</strong>tes contra los procesos que<br />

no t<strong>en</strong>ga el <strong>de</strong>recho <strong>de</strong> acce<strong>de</strong>r.<br />

Fácil <strong>de</strong> utilizar<br />

Un software es fácil <strong>de</strong> utilizar si se pue<strong>de</strong> comunicar consigo <strong>de</strong> manera cómoda.<br />

Corrección<br />

Capacidad <strong>de</strong> los productos software <strong>de</strong> realizar exactam<strong>en</strong>te las tareas <strong>de</strong>finidas por su especificación.<br />

Robustez<br />

Capacidad <strong>de</strong> los productos software <strong>de</strong> funcionar incluso <strong>en</strong> situaciones anormales.<br />

Ext<strong>en</strong>sibilidad<br />

Facilidad que ti<strong>en</strong><strong>en</strong> los productos <strong>de</strong> adaptarse a cambios <strong>en</strong> su especificación. Exist<strong>en</strong> dos principios<br />

fundam<strong>en</strong>tales para conseguir esta característica:<br />

O diseño simple;<br />

<strong>de</strong>sc<strong>en</strong>tralización.<br />

Reutilización<br />

Capacidad <strong>de</strong> los productos <strong>de</strong> ser reutilizados, <strong>en</strong> su totalidad o <strong>en</strong> parte, <strong>en</strong> nuevas aplicaciones.<br />

Compatibilidad<br />

Facilidad <strong>de</strong> los productos para ser combinados con otros.


--<br />

Fundam<strong>en</strong>tos <strong>de</strong> programación 65<br />

2.9. RESUMEM-<br />

Un método g<strong>en</strong>eral para la resolución <strong>de</strong> un problema<br />

con computadora ti<strong>en</strong>e las sigui<strong>en</strong>tes fases:<br />

I. Andlisis <strong>de</strong>l pmgramu,<br />

2. Diseño <strong>de</strong>l algoritmo.<br />

3. Codificación.<br />

4. Compilación y ejecución.<br />

5. Ver$cación y mant<strong>en</strong>imi<strong>en</strong>to.<br />

6. Docum<strong>en</strong>tación y mant<strong>en</strong>imi<strong>en</strong>to.<br />

El sistema más idóneo para resolver un problema<br />

es <strong>de</strong>scomponerlo <strong>en</strong> módulos más s<strong>en</strong>cillos y luego,<br />

mediante diseños <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>tes y refinami<strong>en</strong>to suce-<br />

sivo, llegar a móduios fácjlm<strong>en</strong>te codificables. Estos<br />

módulos se <strong>de</strong>b<strong>en</strong> codificar con las <strong>estructura</strong>s <strong>de</strong> control<br />

<strong>de</strong> programación <strong>estructura</strong>da.<br />

I. Secu<strong>en</strong>ciales: las instrucciones se ejecutan<br />

sucesivam<strong>en</strong>te una <strong>de</strong>spués <strong>de</strong> otra.<br />

2. Repctitivas: una serie <strong>de</strong> instrucciones se repit<strong>en</strong><br />

una y otra vez hasta que se cumple una cierta<br />

condición.<br />

3. Selectivas: permite elegir <strong>en</strong>tre dos alternativas<br />

(dos conjuntos <strong>de</strong> inshucciones) <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do<br />

<strong>de</strong> una condición <strong>de</strong>terminada).<br />

2.10. EJERCICIOS<br />

2.1. Diseñar una solución para resolver cada uno <strong>de</strong><br />

los sigui<strong>en</strong>tes problemas y trate <strong>de</strong> refinar sus<br />

soluciones mediante <strong>algoritmos</strong> a<strong>de</strong>cuados:<br />

a) Realizar una llamada telefónica <strong>de</strong>s<strong>de</strong> un<br />

teléfono público.<br />

b) Cocinar una tortilla.<br />

c) Arreglar un pinchazo <strong>de</strong> una bicicleta.<br />

6) Freír un huevo.<br />

2.2. Escribir un algoritmo para:<br />

a) Sumar dos números <strong>en</strong>teros.<br />

b) Restar dos números <strong>en</strong>teros.<br />

c) Multiplicar dos números <strong>en</strong>teros.<br />

6) Dividir un número <strong>en</strong>tero por otro.<br />

23. Escribir un algoritmo para <strong>de</strong>terminar el máximo<br />

común divisor <strong>de</strong> dos números <strong>en</strong>teros (MCD)<br />

por el algoritmo <strong>de</strong> Eucii<strong>de</strong>s:<br />

o Dividir el mayor <strong>de</strong> los dos <strong>en</strong>teros positivos<br />

por el más pequeño.<br />

o A continuación dividir el divisor por el resto.<br />

o Continuar el proceso <strong>de</strong> dividir el último divisor<br />

por el Último resto hasta que la división sea<br />

exacta.<br />

o El Último divisor es el mcd.<br />

2.4. Diseñar un algoritmo que lea e imprima una<br />

serie <strong>de</strong> números distintos <strong>de</strong> cero. El algoritmo<br />

<strong>de</strong>be terminar con un valor cero que no se <strong>de</strong>be<br />

imprimir. Visualizar el número <strong>de</strong> valores lefdos.<br />

2.5. Diseñar un algoritmo que imprima y sume la<br />

serie <strong>de</strong>. números 3,6,9, 12 ..., 99.<br />

2.6. Escribir un algoritmo que lea cuatro números<br />

y a continuación imprima 131 mayor <strong>de</strong> íos cuatro.<br />

2.7. Diseñar un algoritmo que lea tres números y<br />

<strong>en</strong>cu<strong>en</strong>tre si uno <strong>de</strong> ellos es la suma <strong>de</strong> 10s otros<br />

dos.<br />

2.8. Diseñar un algoritmo para calcular la velocidad<br />

(<strong>en</strong> mls) <strong>de</strong> los corredores <strong>de</strong> la carrera <strong>de</strong> 1 SO0<br />

metros. La <strong>en</strong>trada consistirá <strong>en</strong> parejas <strong>de</strong><br />

números (minutos, segundos) que dan el tiempo<br />

<strong>de</strong>l corredor; por cada corredor, el algoritmo<br />

<strong>de</strong>be imprimir el tiempo <strong>en</strong> minutos y segundos<br />

así como la velocidad media.<br />

Ejemplo <strong>de</strong> <strong>en</strong>trada <strong>de</strong> <strong>datos</strong>: (333) (3,40)<br />

(3,46) (332) (4,O) (0,O); el Último par <strong>de</strong> <strong>datos</strong><br />

se utilizará como fin <strong>de</strong> <strong>en</strong>trada <strong>de</strong> <strong>datos</strong>.<br />

2.9. Diseñar un algoritmo para <strong>de</strong>terminar si un<br />

número N es primo. (Un número primo sólo<br />

pue<strong>de</strong> ser divisible por él mismo y por la unidad.)<br />

2.10. Escribir un algoritmo que calcule la superficie<br />

<strong>de</strong> un tri6nguio <strong>en</strong> función <strong>de</strong> la base y la altura<br />

(S = 1/2 Base x Altura).<br />

2.1. Calcular y visualizar la longitud <strong>de</strong> la circunfer<strong>en</strong>cia<br />

y el área <strong>de</strong> un circulo <strong>de</strong> radio dado.


66 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

2.12. Escribir un algoritmo que <strong>en</strong>cu<strong>en</strong>tre el salario<br />

semanal <strong>de</strong> un trabajador, dada la tarifa horaria<br />

y el número <strong>de</strong> horas trabajadas diariam<strong>en</strong>te.<br />

2.13. Escribir un algoritmo que indique si una palabra<br />

leída <strong>de</strong>i teclado es un palíndromo. Un<br />

palfndromo (capicúa) es una palabra que se lee<br />

igual <strong>en</strong> ambos s<strong>en</strong>tidos como urdan>.<br />

2.14. Escribir un algoritmo que cu<strong>en</strong>te el número <strong>de</strong><br />

ocurr<strong>en</strong>cias <strong>de</strong> cada letra <strong>en</strong> una palabra leída<br />

como <strong>en</strong>trada. For ejemplo, *Mort imer»<br />

conti<strong>en</strong>e dos >,<br />

una «t» y una «e».<br />

2.15. Muchos bancos y cajas <strong>de</strong> ahorro calculan los<br />

intereses <strong>de</strong> las cantida<strong>de</strong>s <strong>de</strong>positadas por los<br />

cli<strong>en</strong>tes diariam<strong>en</strong>te <strong>en</strong> base a las sigui<strong>en</strong>tes<br />

premisas. Un capital <strong>de</strong> 1 .O00 pesetas, con una<br />

tasa <strong>de</strong> interés <strong>de</strong>l 6 por 100, r<strong>en</strong>ta un interés <strong>en</strong><br />

un día <strong>de</strong> 0,06 multiplicado por 1 .O00 y dividido<br />

por 365. Esta operación producirá O, 16 pesetas<br />

<strong>de</strong> interés y el capital acumulado será<br />

1 .OOO, 16. El interés para el segundo día se calculará<br />

multiplicando 0,06 por l .O00 y dividi<strong>en</strong>do<br />

el resultado por 365. Disefiar un algoritmo<br />

que reciba tres <strong>en</strong>tradas: el capital a <strong>de</strong>positar,<br />

la tasa <strong>de</strong> interés y la duración <strong>de</strong>l <strong>de</strong>pósito <strong>en</strong><br />

semanas, y calcule d capital total acumulado al<br />

final <strong>de</strong>l período <strong>de</strong> tiempo especificado.<br />

2.1 I. EJERCICIOS RESUELTOS<br />

Desarrolle los <strong>algoritmos</strong> que resuelvan los sigui<strong>en</strong>tes<br />

problemas:<br />

2.1. Ir al cine.<br />

Análisis <strong>de</strong>l problema<br />

DATOS DE SALIDA: Ver la película.<br />

DATOS DE ENTRADA: Nombre se la película,<br />

dirección <strong>de</strong> la sala, hora<br />

<strong>de</strong> proyección.<br />

DATOS AUXILIARES: Entrada, número <strong>de</strong> asi<strong>en</strong>to.<br />

Para solucionar el problema, se <strong>de</strong>be seleccionar<br />

una película <strong>de</strong> la cartelera <strong>de</strong>l periódico, ir a la sala y<br />

comprar la <strong>en</strong>trada para, finalm<strong>en</strong>te, po<strong>de</strong>r ver la película.<br />

Diseño <strong>de</strong>l algoritmo<br />

inicio<br />

< seleccionar la película ><br />

tomar el periódico<br />

mi<strong>en</strong>tras no lleguemos a la cartelera<br />

pasar la hoja<br />

mi<strong>en</strong>tras no se acabe la cartelera<br />

leer la película<br />

si nos gusta, recordarla<br />

elegir una <strong>de</strong> las películas seleccionadas<br />

leer la dirección <strong>de</strong> la sala y la<br />

hora <strong>de</strong> proyección<br />

< comprar la <strong>en</strong>trada ><br />

trasladarse a la sala<br />

si no hay <strong>en</strong>tradas, ir a fin<br />

si hay cola<br />

ponerse el último<br />

mi<strong>en</strong>tras no lleguemos a la<br />

t aqui 1 1 a<br />

avanzar<br />

si no hay <strong>en</strong>tradas, ir a fin<br />

comprar la <strong>en</strong>trada<br />

< ver la película<br />

leer el número <strong>de</strong> asi<strong>en</strong>to <strong>de</strong> la<br />

<strong>en</strong>trada<br />

buscar el asi<strong>en</strong>to<br />

s<strong>en</strong>tarse<br />

ver la película<br />

fin.<br />

2.2. Comprar una <strong>en</strong>trada para ir a los toros.<br />

Análisis <strong>de</strong>l problema<br />

DATOS DE SALIDA: La <strong>en</strong>trada.<br />

DATOS DE ENTRADA: Tipo <strong>de</strong> <strong>en</strong>trada (sol, sombra,<br />

t<strong>en</strong>dido, andanada.. .).<br />

DATOS AUXILIARES: Disponibilidad <strong>de</strong> la <strong>en</strong>trada.<br />

Hay que ir a la taquilla y elegir la <strong>en</strong>trada <strong>de</strong>seada.<br />

Si hay <strong>en</strong>tradas se compra (<strong>en</strong> taquilla o a los rev<strong>en</strong>tas).<br />

Si no la hay, se pue<strong>de</strong> seleccionar otro tipo <strong>de</strong><br />

<strong>en</strong>trada o <strong>de</strong>sistir, repiti<strong>en</strong>do esta acción hasta que se<br />

ha conseguido la <strong>en</strong>trada o el posible comprador ha<br />

<strong>de</strong>sistido.


Fundam<strong>en</strong>tos <strong>de</strong> programación 67<br />

Diseño<strong>de</strong>lalgori&o I<br />

inicio<br />

ir a la taquilla<br />

si no hay <strong>en</strong>tradas <strong>en</strong> taquilla<br />

si nos interesa comprarla <strong>en</strong> la<br />

rev<strong>en</strong>t a<br />

ir a comprar la <strong>en</strong>trada<br />

si no ir a fin<br />

< comprar la <strong>en</strong>trada ><br />

seleccionar sol o sombra<br />

seleccionar barrera, t<strong>en</strong>dido,<br />

andanada o palco<br />

seleccionar número <strong>de</strong> asi<strong>en</strong>to<br />

solicitar la <strong>en</strong>trada<br />

si la ti<strong>en</strong><strong>en</strong> disponible<br />

adquirir la <strong>en</strong>trada<br />

si no<br />

si queremos otro tipo <strong>de</strong><br />

<strong>en</strong>trada<br />

ir a comprar la <strong>en</strong>trada<br />

fin.<br />

2.3. Hacer una taza <strong>de</strong> té.<br />

DATOS DE SALIDA: taza <strong>de</strong> té.<br />

DATOS DE ENTRADA: bolsa <strong>de</strong> té, agua.<br />

DATOS AUXILWES: pitido <strong>de</strong> la tetera, aspecto<br />

<strong>de</strong> la infusión.<br />

Después <strong>de</strong> echar agua <strong>en</strong> la tetera, se pone ai fuego<br />

y se espera a que el agua hierva (hasta que su<strong>en</strong>a el<br />

pitido <strong>de</strong> la tetera). Introducimos el té y se <strong>de</strong>ja un<br />

tiempo hasta que esté hecho.<br />

Diseño <strong>de</strong>l dgorzbno<br />

inicio<br />

tomar la tetera<br />

ll<strong>en</strong>arla <strong>de</strong> agua<br />

<strong>en</strong>c<strong>en</strong><strong>de</strong>r el fuego<br />

poner la tetera <strong>en</strong> el fuego<br />

mi<strong>en</strong>tras no hierva el agua<br />

esperar<br />

tomar la bolsa <strong>de</strong> té<br />

introducirla <strong>en</strong> la tetera<br />

mi<strong>en</strong>tras no está hecho el té<br />

esperar<br />

echar el té <strong>en</strong> la taza<br />

fin.<br />

2.4. Hacer una llamada telefónica. Consi<strong>de</strong>rar los<br />

casos: a) llamada manual con operador; b) llamada<br />

automática; c) llamada a cobro revertido.<br />

Analisis <strong>de</strong>l problema<br />

Para <strong>de</strong>cidir el tipo <strong>de</strong> llamada que se efectuar&, primero<br />

se <strong>de</strong>be consi<strong>de</strong>rar si se dispone <strong>de</strong> efectivo o no<br />

para realizar la llamada a cobro revertido. Si hay efectivo<br />

se <strong>de</strong>be ver si el lugar a don<strong>de</strong> vamos a llamar<br />

está conectado a la red<br />

Para una llamada con<br />

la c<strong>en</strong>tralita y solicitar la llamada, esperando hasta que<br />

se establezca la comunicación. pata una llamada automática<br />

se le<strong>en</strong> los prefijos <strong>de</strong>l país y provincia si fuera<br />

necesario, y se realiza la liamada, espemdo hasta<br />

que cojan el teléfono. Para llamar a cobro revertido se<br />

<strong>de</strong>be 1- a c<strong>en</strong>traiita, solicitar la llamada y esperara<br />

que el abonado <strong>de</strong>l teléfono d que se llama dé su<br />

autorización, con lo que establecerá la comunicación.<br />

Como <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada t<strong>en</strong>drfmos las variables<br />

que nos van a condicionar el tipo <strong>de</strong> liamada, el núme<br />

ro <strong>de</strong> teléfono y, <strong>en</strong> caso <strong>de</strong> llamada automática, los<br />

prefijos si los hubiera. Como dato auxiliar se podría<br />

consi<strong>de</strong>rar <strong>en</strong> íos casos a) y c) el contacto con la c<strong>en</strong>tralita.<br />

Diseño <strong>de</strong>l ulgonhno<br />

inicio<br />

si t<strong>en</strong>emos dinero<br />

si po<strong>de</strong>mos hacer una llamada<br />

automática<br />

Leer el prefijo <strong>de</strong> país y local<br />

idad<br />

marcar el número<br />

si no<br />

c llamada manual ><br />

llamar a la c<strong>en</strong>tralita<br />

solicitar la comunicación<br />

mi<strong>en</strong>tras no contest<strong>en</strong><br />

esperar<br />

establecer comunicación<br />

si no<br />

c realizar una llamada a cobro<br />

revertido ><br />

llamar a la c<strong>en</strong>tralita<br />

solicitar la llamada<br />

esperar hasta t<strong>en</strong>er la autorización<br />

establecer comunicación<br />

fin.<br />

25. Averiguar si una palabra es un palíndromo. Un<br />

palíndmmo es una palabra que se be igual <strong>de</strong><br />

izquierda a <strong>de</strong>recha que <strong>de</strong> <strong>de</strong>recha a izquierda,<br />

como, por ejemplo, «radar*<br />

Análisis <strong>de</strong>l problem<br />

DATOS DE SALIDA: el m<strong>en</strong>saje que nos dice<br />

si es o no un palíndromo.<br />

DATOS DE ENTRADA: palabra.<br />

DATOS AUXILIARES: cada carácter <strong>de</strong> la palabra,<br />

palabra al revés.


68 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Para comprobar si una palabra es un paiíndromo, se<br />

pue<strong>de</strong> ir formando una palabra con los caracteres invertidos<br />

con respecto a la original y comprobar si la palabra<br />

al revés es iguai a la original. Para obt<strong>en</strong>er esa<br />

palabra al revés, se leerán <strong>en</strong> s<strong>en</strong>tido inverso los caracteres<br />

<strong>de</strong> la palabra inicial y se irán juntarido sucesivam<strong>en</strong>te<br />

hasta iiegar al primer cadcter.<br />

Diseño <strong>de</strong>l algoritmo<br />

Inicio<br />

palabra<br />

último<br />

carácter<br />

,I<br />

el carácter<br />

carácter<br />

2.6. Diseñar un algoritmo Pam calcular la velocidad<br />

(<strong>en</strong> metroslsegundo) <strong>de</strong> los corredores <strong>de</strong> una<br />

carrera <strong>de</strong> 1 .SO0 metros. La <strong>en</strong>trada serán parejm<br />

<strong>de</strong> números (minutos, segundos) que darán el<br />

tiempo <strong>de</strong> cada corredor. Por cada corredor se<br />

imprimirá el tiempo <strong>en</strong> minutos y segundos, así<br />

como la velocidad media. El bucle se ejecutará<br />

hasta que <strong>de</strong>mos una <strong>en</strong>traa'a <strong>de</strong> 0,O que será la<br />

marca <strong>de</strong> fin <strong>de</strong> <strong>en</strong>trada <strong>de</strong> <strong>datos</strong>.<br />

Andlisis <strong>de</strong>l problema<br />

DATOS DE SALIDA: v (velocidad media).<br />

DATOS DE ENTRADA mm , s s (minutos y segundos).<br />

DATOS AUXILIARES: di s t ane ia (distancia<br />

reconida, que <strong>en</strong> el ejemplo<br />

es <strong>de</strong> 1.500 metros)<br />

y tiempo (los ininutos y<br />

los segundos que ha tardado<br />

<strong>en</strong> recorrerla).<br />

Se <strong>de</strong>be efectuar un bucle hasta que rnm sea O y ss<br />

sea O. D<strong>en</strong>tro <strong>de</strong>l bucle se calcula el tiempo <strong>en</strong> segundos<br />

con la fórmula tiempo = ss + mm * 60. La velocidad<br />

se hallará con la fórmula<br />

velocidad = djstaucía I tiempo.<br />

Disefio <strong>de</strong>l a lgoh<br />

inicio<br />

distancia 4 ~ 1500 -<br />

leer (mm, ss)<br />

mi<strong>en</strong>tras mm = O y ss = O hacer<br />

tiempo 4- ss + mm * 60<br />

v +distancia / tiempo<br />

escribir (mm, ss, v)<br />

leer (m,ss)<br />

fin<br />

u<br />

un palíndrorno<br />

un<br />

2.7. Escribir un algoritmo que calcule la superficie<br />

<strong>de</strong> un triángulo <strong>en</strong>fuplción <strong>de</strong> la base y la altura.<br />

Análisis <strong>de</strong>l probiem<br />

DATOS DE SALDA: s (superficie).<br />

DATOS DE FNTRADA: b (base) a (altura).<br />

Para calcular la superficie se aplica la fórmula<br />

S = base * altura / 2.<br />

palíndromo Diseño <strong>de</strong>l algoritmo<br />

I inicio<br />

leer (b, a)<br />

s=b*a/2<br />

escribir (s)<br />

fin


Fundam<strong>en</strong>tos <strong>de</strong> programación 69<br />

2.8. Realizar un algoritmo que calcule la suma <strong>de</strong> los<br />

<strong>en</strong>teros <strong>en</strong>tre 1 y 10, es <strong>de</strong>cir, 1+2+3+ ...+ 10.<br />

Análisis <strong>de</strong>l problema<br />

DATOS DE SALIDA: suma (conti<strong>en</strong>e la suma<br />

requerida).<br />

DATOS AUXILIARES: num (será una variable<br />

que vaya tomando valores<br />

<strong>en</strong>tre 1 y 10 y se acumulará<br />

<strong>en</strong> suma).<br />

Hay que ejecutar un bucle que se realice 10 veces.<br />

En él se irá increm<strong>en</strong>tando <strong>en</strong> 1 la variable núm, y se<br />

acumulará su valor <strong>en</strong> la variable suma. Una vez salgamos<br />

<strong>de</strong>l bucle se visualizará el valor <strong>de</strong> la variable<br />

suma.<br />

Diseño <strong>de</strong>l dgorihno<br />

TABLA DE VARIABLES<br />

<strong>en</strong>tero: suma, num<br />

a<br />

Inicio<br />

I<br />

2.9. Realizar un algoritmo que calcule y visualice las<br />

pot<strong>en</strong>cias <strong>de</strong> 2 <strong>en</strong>tre O y 10.<br />

Análisis <strong>de</strong>l problema<br />

Hay que implem<strong>en</strong>tar un bucle que se ejecute once<br />

veces y d<strong>en</strong>tro <strong>de</strong> él ir increm<strong>en</strong>tando una variable que<br />

tome valores <strong>en</strong>tre O y 1 O y que se llamará num .<br />

También d<strong>en</strong>tro <strong>de</strong> él se visualizará el resultado <strong>de</strong> la<br />

operación 2 num.<br />

Diseño <strong>de</strong>l algoho<br />

TABLA DE VARIABLES:<br />

<strong>en</strong>tero: num<br />

Inicio<br />

l-----l num + O<br />

escribir<br />

num f- num+l<br />

r<br />

num + num+ 1<br />

suma + suma+num


CAPíTULO 3<br />

EL LENGUAJE C:<br />

ELEMENTOS BASICOS<br />

CONTENIDO<br />

3.1.<br />

3.2.<br />

3.3.<br />

3.4.<br />

3.b.<br />

3.6.<br />

Estructura g<strong>en</strong>eral <strong>de</strong> un<br />

programa <strong>en</strong> C.<br />

Creación <strong>de</strong> un programa.<br />

El proceso <strong>de</strong> ejecución <strong>de</strong> un<br />

programa <strong>en</strong> C.<br />

Depuración <strong>de</strong> un programa<br />

<strong>en</strong> C.<br />

Pruebas.<br />

Los elem<strong>en</strong>tos <strong>de</strong> un<br />

programa <strong>en</strong> C.<br />

3.7. Tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong> C.<br />

3.8. El tipo <strong>de</strong> dato lógico.<br />

3.9. Constantes.<br />

3.10. Variables.<br />

3.11. Duración <strong>de</strong> una variable.<br />

3.12. Entradas y salidas.<br />

3.13. Resum<strong>en</strong>.<br />

3.14. Ejercicios.<br />

, 72


INTRODUCCI~N<br />

Una vez gue se le ha <strong>en</strong>señado a crear sus propios programas, vamos a analizar<br />

los fundam<strong>en</strong>tos <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> programación C. Este capitulo comi<strong>en</strong>za con<br />

un repaso <strong>de</strong> los conceptos teóricos y pr&cticos relativos a la <strong>estructura</strong> <strong>de</strong> un<br />

programa <strong>en</strong>unciados <strong>en</strong> capítulos anteriores, dada su gran importancia <strong>en</strong> el<br />

<strong>de</strong>sarrollo <strong>de</strong> aplicaciones, incluy<strong>en</strong>do ad<strong>en</strong>iás los sigui<strong>en</strong>tes temaa:<br />

0 creación <strong>de</strong> un programa;<br />

0 elem<strong>en</strong>tos básicos gue compon<strong>en</strong> un programa;<br />

o tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong> C y cómo se <strong>de</strong>claran;<br />

0 concepto <strong>de</strong> constantes y su <strong>de</strong>claración;<br />

0 concepto y <strong>de</strong>claración <strong>de</strong> variables;<br />

0 tiempo <strong>de</strong> vida o duración <strong>de</strong> variables;<br />

0 operaciones básicas <strong>de</strong> <strong>en</strong>tradasalida.<br />

L<br />

CONCEPTOS CLAVE<br />

0 Archivo <strong>de</strong> cabecera.<br />

o Código ejecutable.<br />

o Códigofu<strong>en</strong>te.<br />

o Wgoobjeto.<br />

o Com<strong>en</strong>tarios.<br />

o Constantes.<br />

o char.<br />

o Directiva #inclu<strong>de</strong>.<br />

o Float/double.<br />

o Flujos.<br />

o Función main().<br />

o Id<strong>en</strong>tiOlcador.<br />

o int.<br />

0 Preprocesador.<br />

o grfntf O.<br />

O scanf 0.<br />

o Variables.<br />

73


74 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3.1. ESTRUCTURA GENERAL DE UN PROGRAMA EN C<br />

En esta sección repasamos los elem<strong>en</strong>tos constituy<strong>en</strong>tes <strong>de</strong> un programa escrito <strong>en</strong> C, fijando i<strong>de</strong>as y<br />

<strong>de</strong>scribi<strong>en</strong>do i<strong>de</strong>as nuevas relativas a la m<strong>en</strong>cionada <strong>estructura</strong> <strong>de</strong> un programa <strong>en</strong> C.<br />

Un programa <strong>en</strong> C se compone <strong>de</strong> una o más funciones. Una <strong>de</strong> las funciones <strong>de</strong>be ser obligatoriam<strong>en</strong>te<br />

main. Una función <strong>en</strong> C es un grupo <strong>de</strong> instrucciones que realizan una o más acciones. Asimismo,<br />

un programa cont<strong>en</strong>drá una serie <strong>de</strong> directivas #inclu<strong>de</strong> que permitirán incluir <strong>en</strong> el mismo archivos<br />

<strong>de</strong> cabecera que a su vez constarán <strong>de</strong> funciones y <strong>datos</strong> pre<strong>de</strong>finidos <strong>en</strong> ellos.<br />

#inclu<strong>de</strong> Cstdi0.b<br />

archivo <strong>de</strong> cabecera s tdi o. h<br />

int main() 4<br />

cabecera <strong>de</strong>función<br />

{ f<br />

nombre <strong>de</strong> la.función<br />

... 4 s<strong>en</strong>t<strong>en</strong>cias<br />

#inclu<strong>de</strong><br />

#<strong>de</strong>fine<br />

Directivas <strong>de</strong>l preprocesador<br />

Macros <strong>de</strong>l procesador<br />

I<br />

Declaraciones globales<br />

O prototipos <strong>de</strong> funciones<br />

O variables<br />

I<br />

Función principal ma in<br />

main ( )<br />

i<br />

1<br />

<strong>de</strong>claraciones locales<br />

s<strong>en</strong>t <strong>en</strong> ci as<br />

Dejkiciones <strong>de</strong> otras funciones<br />

tipo1 funcl( . . . )<br />

{<br />

}<br />

...<br />

Figura 3.1. Estructura típica <strong>de</strong> un programa C.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 75<br />

De un modo más explícito, un programa C pue<strong>de</strong> incluir:<br />

o directivas <strong>de</strong> preprocesador;<br />

0 <strong>de</strong>claraciones globales;<br />

o la función main ( ) ;<br />

funciones <strong>de</strong>finidas por el usuario;<br />

0 com<strong>en</strong>tarios <strong>de</strong>l programa (utilizados <strong>en</strong> su totalidad).<br />

La <strong>estructura</strong> típica completa <strong>de</strong> un programa C se muestra <strong>en</strong> la Figura 3.1. Un ejemplo <strong>de</strong> un<br />

programa s<strong>en</strong>cillo <strong>en</strong> C.<br />

/*Listado DEMO-UN0.C. Programa <strong>de</strong> saludo */<br />

#inclu<strong>de</strong> <br />

/* Este programa imprime: Bi<strong>en</strong>v<strong>en</strong>ido a la programación <strong>en</strong> C */<br />

int main0<br />

{<br />

printf("Bi<strong>en</strong>v<strong>en</strong>id0 a la programación <strong>en</strong> C\n");<br />

return O;<br />

1<br />

La directiva # inclu<strong>de</strong> <strong>de</strong> la primera línea es necesaria para que el programa t<strong>en</strong>ga salida. Se refiere<br />

a un archivo externo d<strong>en</strong>ominado s tdio . h <strong>en</strong> el que se proporciona la información relativa a la función<br />

printf ( ) . Obsérvese que los ángulos < y > no son parte <strong>de</strong>l nombre <strong>de</strong>l archivo; se utilizan para<br />

indicar que el archivo es un archivo <strong>de</strong> la biblioteca estándar C.<br />

La segunda línea es un com<strong>en</strong>tario, id<strong>en</strong>tificado por los caracteres /* y */. Los com<strong>en</strong>tarios se<br />

incluy<strong>en</strong> <strong>en</strong> programas que proporcionan explicaciones a los lectores <strong>de</strong> los mismos. Son ignorados por<br />

el compilador.<br />

La tercera línea conti<strong>en</strong>e la cabecera <strong>de</strong> la función main ( , obligatoria <strong>en</strong> cada programa C. Indica<br />

el comi<strong>en</strong>zo <strong>de</strong>l programa y requier<strong>en</strong> los paréntesis ( ) a continuación <strong>de</strong> main ( ) .<br />

La cuarta y séptima línea conti<strong>en</strong><strong>en</strong> sólo las llaves { y 1 que <strong>en</strong>cierran el cuerpo <strong>de</strong> la función<br />

main ( ) y son necesarias <strong>en</strong> todos los programas C.<br />

La quinta línea conti<strong>en</strong>e la s<strong>en</strong>t<strong>en</strong>cia<br />

printf("Bi<strong>en</strong>v<strong>en</strong>id0 a la programación <strong>en</strong> C\n");<br />

que indica al sistema que escriba el m<strong>en</strong>saje "Bi<strong>en</strong>v<strong>en</strong>ido a la programación <strong>en</strong> C\n" .<br />

print f I ) es la función más utilizada para dar salida <strong>de</strong> <strong>datos</strong> por el dispositivo estándar, la pantalla.<br />

La salida será<br />

Bi<strong>en</strong>v<strong>en</strong>ido a la programación <strong>en</strong> C<br />

El símbolo '\n' es el símbolo <strong>de</strong> nueva línea. Poni<strong>en</strong>do este símbolo al final <strong>de</strong> la cad<strong>en</strong>a <strong>en</strong>tre<br />

comillas, indica al sistema que comi<strong>en</strong>ce una nueva línea <strong>de</strong>spués <strong>de</strong> imprimir los caracteres<br />

preced<strong>en</strong>tes, terminando, por consigui<strong>en</strong>te, la línea actual.<br />

La sexta línea conti<strong>en</strong>e la s<strong>en</strong>t<strong>en</strong>cia return o. Esta s<strong>en</strong>t<strong>en</strong>cia termina la ejecución <strong>de</strong>l programa y<br />

<strong>de</strong>vuelve el control al sistema operativo <strong>de</strong> la computadora. El número O se utiliza para señalar que el<br />

programa ha terminado correctam<strong>en</strong>te (con éxito).<br />

Obsérvese el punto y coma (;) al final <strong>de</strong> la quinta y sexta línea. C requiere que cada s<strong>en</strong>t<strong>en</strong>cia<br />

termine con un punto y coma. No es necesario que esté al final <strong>de</strong> una línea. Se pued<strong>en</strong> poner varias<br />

s<strong>en</strong>t<strong>en</strong>cias <strong>en</strong> la misma línea y se pue<strong>de</strong> hacer que una s<strong>en</strong>t<strong>en</strong>cia se exti<strong>en</strong>da sobre varias líneas.<br />

i<br />

Advertancia<br />

O El programa más corto <strong>de</strong> C es el «programa vacio» que no hace nada.<br />

n O; no es obligatoria <strong>en</strong> la mayoría <strong>de</strong> los compiladores, aunque algunos<br />

advert<strong>en</strong>cia si se omite.


76 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3.1 .I. Directivas <strong>de</strong>l preprocesador<br />

El preprocesador <strong>en</strong> un programa C se pue<strong>de</strong> consi<strong>de</strong>rar como un editor <strong>de</strong> texto intelig<strong>en</strong>te que consta<br />

<strong>de</strong> directivas (instrucciones al compilador antes <strong>de</strong> que se compile el programa principal). Las dos<br />

directivas más usuales son #inclu<strong>de</strong> y #<strong>de</strong>fine.<br />

Todas las directivas <strong>de</strong>l preprocesador comi<strong>en</strong>zan con el signo <strong>de</strong> libro o «almohadilla>>(#), que<br />

indica al compilador que lea las directivas antes <strong>de</strong> compilar la parte (función) principal <strong>de</strong>l programa.<br />

Las directivas son instrucciones al compilador. Las directivas no son g<strong>en</strong>eralm<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cias<br />

-obsérvese que su línea no termina <strong>en</strong> punto y coma-, sino instrucciones que se dan al compilador<br />

antes <strong>de</strong> que el programa se compile. Aunque las directivas pued<strong>en</strong> <strong>de</strong>finir macros, nombres <strong>de</strong><br />

constantes, archivos fu<strong>en</strong>te adicionales, etc., su uso más frecu<strong>en</strong>te <strong>en</strong> C es la inclusión <strong>de</strong> archivos <strong>de</strong><br />

cabecera.<br />

Exist<strong>en</strong> archivos <strong>de</strong> cabecera estándar que se utilizan ampliam<strong>en</strong>te, tales como STDIO . H,<br />

STDLIB . H, MATH. H, STRING. H y se utilizarán otros archivos <strong>de</strong> cabecera <strong>de</strong>finidos por el usuario<br />

para diseño <strong>estructura</strong>do.<br />

La directiva #inclu<strong>de</strong> indica al compilador que lea el archivo fu<strong>en</strong>te que vi<strong>en</strong>e a continuación <strong>de</strong><br />

ella y su cont<strong>en</strong>ido lo inserte <strong>en</strong> la posición don<strong>de</strong> se <strong>en</strong>cu<strong>en</strong>tra dicha directiva. Estos archivos se<br />

d<strong>en</strong>ominan archivos <strong>de</strong> cubecera o archivos <strong>de</strong> inclusión.<br />

Los archivos <strong>de</strong> cabecera (archivos con ext<strong>en</strong>sión . h conti<strong>en</strong><strong>en</strong> código fu<strong>en</strong>te C) se sitúan <strong>en</strong> un<br />

programa C mediante la directiva <strong>de</strong>l preprocesador #inclu<strong>de</strong> con una instrucción que ti<strong>en</strong>e el<br />

sigui<strong>en</strong>te formato :<br />

#inclu<strong>de</strong> O bi<strong>en</strong><br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> "nombre <strong>de</strong>l archi vol1<br />

#inclu<strong>de</strong> "nombrearch.h"<br />

nombrearch <strong>de</strong>be ser un archivo <strong>de</strong> texto ASCII (su archivo fu<strong>en</strong>te) que resi<strong>de</strong> <strong>en</strong> su disco. En realidad,<br />

la directiva <strong>de</strong>l preprocesador mezcla un archivo <strong>de</strong> disco <strong>en</strong> su programa fu<strong>en</strong>te.<br />

La mayoría <strong>de</strong> los programadores C sitúan las directivas <strong>de</strong>l preprocesador al principio <strong>de</strong>l<br />

programa, aunque esta posición no es obligatoria.<br />

A<strong>de</strong>más <strong>de</strong> los archivos <strong>de</strong> código fu<strong>en</strong>te diseñados por el usuario, # inclu<strong>de</strong> se utiliza para incluir<br />

archivos <strong>de</strong> sistemas especiales (también d<strong>en</strong>ominados archivos <strong>de</strong> cabecera) que resid<strong>en</strong> <strong>en</strong> su<br />

compilador C. Cuando se instala el compilador, estos archivos <strong>de</strong> cabecera se almac<strong>en</strong>arán<br />

automáticam<strong>en</strong>te <strong>en</strong> su disco, <strong>en</strong> el directorio <strong>de</strong> inclusión (inclu<strong>de</strong>) <strong>de</strong>l sistema. Sus nombres <strong>de</strong><br />

archivo siempre ti<strong>en</strong><strong>en</strong> la ext<strong>en</strong>sión . h.<br />

El archivo <strong>de</strong> cabecera más frecu<strong>en</strong>te es STDIO . H. Este archivo proporciona al compilador C la<br />

información necesaria sobre las funciones <strong>de</strong> biblioteca que realizan operaciones <strong>de</strong> <strong>en</strong>trada y salida.<br />

Como casi todos los programas que escriba imprimirán información <strong>en</strong> pantalla y leerán <strong>datos</strong> <strong>de</strong><br />

teclado, necesitarán incluir scanf ( ) y print f ( ) <strong>en</strong> los mismos.<br />

Para ello será preciso que cada programa cont<strong>en</strong>ga la línea sigui<strong>en</strong>te:<br />

#inclu<strong>de</strong> <br />

De igual modo es muy frecu<strong>en</strong>te el uso <strong>de</strong> funciones <strong>de</strong> cad<strong>en</strong>a, especialm<strong>en</strong>te s t rcpy ( ) ; por esta<br />

razón, se requiere el uso <strong>de</strong>l archivo <strong>de</strong> cabecera d<strong>en</strong>ominado string . h. Por consigui<strong>en</strong>te, será muy<br />

usual que <strong>de</strong>ba incluir <strong>en</strong> sus programas las líneas:<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

El ord<strong>en</strong> <strong>de</strong> sus archivos <strong>de</strong> inclusión no importan con tal que se incluyan antes <strong>de</strong> que se utilic<strong>en</strong><br />

las funciones correspondi<strong>en</strong>tes. La mayoría <strong>de</strong> los programas C incluy<strong>en</strong> todos los archivos <strong>de</strong> cabecera<br />

necesarios antes <strong>de</strong> la primera función <strong>de</strong>l archivo.<br />

La directiva #inclu<strong>de</strong> pue<strong>de</strong> adoptar uno <strong>de</strong> los sigui<strong>en</strong>tes formatos:


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 77<br />

Dos ejemplos típicos son:<br />

(a) #inclu<strong>de</strong> <br />

(b) #inclu<strong>de</strong> "pruebas. h"<br />

El formato (a) (el nombre <strong>de</strong>l archivo <strong>en</strong>tre ángulos) significa que los archivos se <strong>en</strong>cu<strong>en</strong>tran <strong>en</strong> el<br />

directorio por <strong>de</strong>fecto inclu<strong>de</strong>. El formato (b) significa que el archivo está <strong>en</strong> el directorio actual. Los<br />

dos métodos no son excluy<strong>en</strong>tes y pued<strong>en</strong> existir <strong>en</strong> el mismo programa archivos <strong>de</strong> cabecera estándar<br />

utilizando ángulos y otros archivos <strong>de</strong> cabecera utilizando comillas. Si <strong>de</strong>sea utilizar un archivo <strong>de</strong><br />

cabecera que se creó y no está <strong>en</strong> el directorio por <strong>de</strong>fecto, se <strong>en</strong>cierra el archivo <strong>de</strong> cabecera y el camino<br />

<strong>en</strong>tre comillas, tal como<br />

#inclu<strong>de</strong> I'D: \MIPROG\CABEZA. H"<br />

#<strong>de</strong>fine. La directiva #<strong>de</strong>fine indica al preprocesador que <strong>de</strong>fina un item <strong>de</strong> <strong>datos</strong> u operación para<br />

el programa C. Por ejemplo, la directiva<br />

#<strong>de</strong>fine TAP-LINEA 65<br />

sustituirá TAM-LINEA por el valor 65 cada vez que aparezca <strong>en</strong> el programa.<br />

3.1.2. Declaraciones globales<br />

Las <strong>de</strong>claraciones globales indican ai compilador que las funciones <strong>de</strong>finidas por el usuario o variables<br />

así <strong>de</strong>claradas son comunes a todas las funciones <strong>de</strong> su programa. Las <strong>de</strong>claraciones globales se sitúan<br />

antes <strong>de</strong> la función main ( ) . Si se <strong>de</strong>clara global una variable Grado-clase <strong>de</strong>l tipo<br />

int Grado-clase;<br />

cualquier función <strong>de</strong> su programa, incluy<strong>en</strong>do main ( ) , pue<strong>de</strong> acce<strong>de</strong>r a la variable Grado-clase.<br />

La zona <strong>de</strong> <strong>de</strong>claraciones globales <strong>de</strong> un programa pue<strong>de</strong> incluir <strong>de</strong>claraciones <strong>de</strong> variables a<strong>de</strong>más<br />

<strong>de</strong> <strong>de</strong>claraciones <strong>de</strong> función. Las <strong>de</strong>claraciones <strong>de</strong> función se d<strong>en</strong>ominan prototipos<br />

int media(int a, int b) ;<br />

El sigui<strong>en</strong>te programa es una <strong>estructura</strong> mo<strong>de</strong>lo que incluye <strong>de</strong>claraciones globales.<br />

# /* Programa <strong>de</strong>m0.C */<br />

i #inclu<strong>de</strong>


78 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

main í )<br />

{<br />

I<br />

. . . 4- bloque <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias<br />

Las s<strong>en</strong>t<strong>en</strong>cias incluidas <strong>en</strong>tre las llaves { . .. } se d<strong>en</strong>ominan bloque. Un programa <strong>de</strong>be t<strong>en</strong>er sólo<br />

una función main ( . Si se int<strong>en</strong>ta hacer dos funciones main ( ) se produce un error. A<strong>de</strong>más <strong>de</strong> la<br />

función main ( ) , un programa C consta <strong>de</strong> una colección <strong>de</strong> funciones.<br />

UnafuncuSn C es un subpro<br />

<strong>de</strong>vuelve un único valor, un c<br />

En un programa corto, el programa completo pue<strong>de</strong> incluirse totalm<strong>en</strong>te <strong>en</strong> la función main ( ) . Un<br />

programa largo, sin embargo, ti<strong>en</strong>e <strong>de</strong>masiados códigos para incluirlo <strong>en</strong> esta función. La función<br />

main ( ) <strong>en</strong> un programa largo consta prácticam<strong>en</strong>te <strong>de</strong> llamadas a las funciones <strong>de</strong>finidas por el usuario.<br />

El programa sigui<strong>en</strong>te se compone <strong>de</strong> tres funciones: obt<strong>en</strong>er<strong>datos</strong> ( ) , alfabetizar ( ) y<br />

verpalabras ( ) que se invocan sucesivam<strong>en</strong>te.<br />

int main()<br />

obt<strong>en</strong>er<strong>datos</strong> ( ) ;<br />

alEabetizar ( ) ;<br />

verpalabras ( ) ;<br />

1<br />

return O;<br />

Las variables y constantes globules se <strong>de</strong>claran y <strong>de</strong>fin<strong>en</strong> fuera <strong>de</strong> A <strong>de</strong>finición <strong>de</strong> las funciones,<br />

g<strong>en</strong>eralm<strong>en</strong>te <strong>en</strong> la cabecera <strong>de</strong>l programa, antes <strong>de</strong> main ( ) , mi<strong>en</strong>tras que las variables y constantes<br />

locales se <strong>de</strong>claran y <strong>de</strong>fin<strong>en</strong> <strong>en</strong> la cabecera <strong>de</strong>l cuerpo o bloque <strong>de</strong> la función principal, o <strong>en</strong> la cabecera<br />

<strong>de</strong> cualquier bloque. Las s<strong>en</strong>t<strong>en</strong>cias situadas <strong>en</strong> el interior <strong>de</strong>l cuerpo <strong>de</strong> la función main ( ) , o cualquier<br />

otra función, <strong>de</strong>b<strong>en</strong> terminar <strong>en</strong> punto y coma.<br />

3.1.4. Funciones <strong>de</strong>finidas por el usuario<br />

Un programa C es una colección <strong>de</strong> funciones. Todos los programas se construy<strong>en</strong> a partir <strong>de</strong> una o más<br />

funciones que se integran para crear una aplicación. Todas las funciones conti<strong>en</strong><strong>en</strong> una o más s<strong>en</strong>t<strong>en</strong>cias<br />

C y se crean g<strong>en</strong>eralm<strong>en</strong>te para realizar una única tarea, tales como imprimir la pantalla, escribir un<br />

archivo o cambiar el color <strong>de</strong> la pantalla. Se pued<strong>en</strong> <strong>de</strong>clarar y ejecutar un número <strong>de</strong> funciones casi<br />

ilimitado <strong>en</strong> un programa C.<br />

Las funciones <strong>de</strong>finidas por el usuario se invocan por su nombre y los parámetros opcionales que<br />

puedan t<strong>en</strong>er. Después <strong>de</strong> que la función es llamada, el código asociado con la función se ejecuta y, a<br />

continuación, se retorna a la función llamadora.<br />

Todas las funciones ti<strong>en</strong><strong>en</strong> nombre y una lista <strong>de</strong> valores que recib<strong>en</strong>. Se pue<strong>de</strong> asignar cualquier<br />

nombre a su función, pero normalm<strong>en</strong>te se procura que dicho nombre <strong>de</strong>scriba el propósito <strong>de</strong> la<br />

función. En C, las funciones requier<strong>en</strong> una <strong>de</strong>clarucihn o prototipo <strong>en</strong> el programa:<br />

void trazarcurva();


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 79<br />

Una <strong>de</strong>claración <strong>de</strong> función indica al cornpilador el nombre <strong>de</strong> la función por el que ésta será<br />

invocada <strong>en</strong> el programa. Si la función no se <strong>de</strong>fine, el cornpilador informa <strong>de</strong> un error. La palabra<br />

reservada void significa que la función no <strong>de</strong>vuelve un valor.<br />

void contarvocales(char caracter);<br />

La <strong>de</strong>finición <strong>de</strong> una función es la <strong>estructura</strong> <strong>de</strong> la misma:<br />

t ipo-re t orn o nombre- f un ci ón (1 i s t a-<strong>de</strong>sa ráme t ros ) principio <strong>de</strong> la .función<br />

I<br />

s<strong>en</strong> t <strong>en</strong> ci as<br />

cuerpo <strong>de</strong> la función<br />

return;<br />

retorno <strong>de</strong> lafunción<br />

1 fin <strong>de</strong> lajünción<br />

t ipo-re t orno<br />

Es el tipo <strong>de</strong> valor, o void, <strong>de</strong>vuelto por la función<br />

nombre- f un ci Ón<br />

Nombre <strong>de</strong> la función<br />

1 ista-<strong>de</strong>parámetroc Lista <strong>de</strong> parámetros, o void, pasados a la función. Se conoce<br />

también como argum<strong>en</strong>ros <strong>de</strong> la función o argum<strong>en</strong>tos formales.<br />

C proporciona también funciones pre<strong>de</strong>finidas que se d<strong>en</strong>ominan funciones <strong>de</strong> biblioteca. Las<br />

funciones <strong>de</strong> biblioteca son funciones listas para ejecutar que vi<strong>en</strong><strong>en</strong> con el l<strong>en</strong>guaje C. Requier<strong>en</strong> la<br />

inclusión <strong>de</strong>l archivo <strong>de</strong> cabecera estándar, tal como STDIO . H I MATH. H, etc. Exist<strong>en</strong> c<strong>en</strong>t<strong>en</strong>ares <strong>de</strong><br />

funciones <strong>de</strong>finidas <strong>en</strong> diversos archivos <strong>de</strong> cabecera.<br />

/* ejemplo funciones <strong>de</strong>finidas por el usuario */<br />

#inclu<strong>de</strong> <br />

void visualizar();<br />

#int main ( )<br />

{<br />

J<br />

visualizar ( j ;<br />

return O;<br />

void visualizar()<br />

i<br />

printf ( "primeros pasos <strong>en</strong> C\n"j ;<br />

1<br />

Los programas C constan <strong>de</strong> un conjunto <strong>de</strong> funciones que normalm<strong>en</strong>te están controladas por la<br />

función main ( ) .<br />

main ( )<br />

i<br />

I<br />

...<br />

obt<strong>en</strong>er<strong>datos</strong> ( 1<br />

t<br />

...<br />

alfabetizar ( )<br />

{<br />

1<br />

...


80 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3.1.5. Com<strong>en</strong>tarios<br />

Un com<strong>en</strong>tario es cualquier información que se aña<strong>de</strong> a su archivo fu<strong>en</strong>te para proporcionar docum<strong>en</strong>tación<br />

<strong>de</strong> cualquier tipo. El compilador ignora los com<strong>en</strong>tarios, no realiza ninguna tarea concreta. El uso<br />

<strong>de</strong> com<strong>en</strong>tarios es totalm<strong>en</strong>te opcional, aunque dicho uso es muy recom<strong>en</strong>dable.<br />

G<strong>en</strong>eralm<strong>en</strong>te, se consi<strong>de</strong>ra bu<strong>en</strong>a práctica <strong>de</strong> programación com<strong>en</strong>tar su archivo fu<strong>en</strong>te tanto como<br />

sea posible, al objeto <strong>de</strong> que usted mismo y otros programadores puedan leer fácilm<strong>en</strong>te el programa con<br />

el paso <strong>de</strong> tiempo. Es bu<strong>en</strong>a práctica <strong>de</strong> programación com<strong>en</strong>tar su programa <strong>en</strong> la parte superior <strong>de</strong><br />

cada archivo fu<strong>en</strong>te. La información que se suele incluir es el nombre <strong>de</strong>l archivo, el nombre <strong>de</strong>l<br />

programador, una breve <strong>de</strong>scripción, la fecha <strong>en</strong> que se creó la versión y la información <strong>de</strong> la revisión.<br />

Los com<strong>en</strong>tarios <strong>en</strong> C estándar comi<strong>en</strong>zan con la secu<strong>en</strong>cia / * y terminan con la secu<strong>en</strong>cia * /.<br />

Todo el texto situado <strong>en</strong>tre las dos secu<strong>en</strong>cias es un com<strong>en</strong>tario ignorado por el compilador.<br />

/* PRUEBA1.C - Primer programa C */<br />

Si se necesitan varias líneas <strong>de</strong> programa se pue<strong>de</strong> hacer lo sigui<strong>en</strong>te:<br />

/*<br />

Programa : PRUEBA1.C<br />

Programador : Pepe Mortimer<br />

Descripción : Primer programa C<br />

Fecha creación : Septiembre 2000<br />

Revis iÓn<br />

: Ninguna<br />

*/<br />

También se pued<strong>en</strong> situar com<strong>en</strong>tarios <strong>de</strong> la forma sigui<strong>en</strong>te:<br />

scanf ("%d", &x); /* s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> <strong>en</strong>trdda <strong>de</strong> un valor <strong>en</strong>tero*/<br />

~~ ~<br />

Ejemplo 3.1<br />

Supongamos que se ha <strong>de</strong> imprimir su nombre y dirección muchas veces <strong>en</strong> su programa C. El sistema<br />

normal es teclear las líneas <strong>de</strong> texto cuantas veces sea necesario; sin embargo, el método más rápido<br />

y &ci<strong>en</strong>te sería escribir el códigofu<strong>en</strong>te correspondi<strong>en</strong>te una vez 4' a continuación grabar un archivo<br />

MIDIREC. c, <strong>de</strong> modo que para incluir el código sólo necesitará incluir <strong>en</strong> su programa la línea<br />

#inclu<strong>de</strong> "midirec. c"<br />

Es <strong>de</strong>cir, teclee las sigui<strong>en</strong>tes líneas y grábelas <strong>en</strong> un archivo d<strong>en</strong>ominado MIDIREC. C<br />

/* archivo mi di re c.^ */<br />

printf ( "Luis Joyanes Aguilar\n") ;<br />

printf ( "Avda <strong>de</strong> Andalucía, 48\n") :<br />

printf ( "Carchelejo, JAEN\n) 'I;<br />

printf ( "Andalucía, ESPAÑA\n") :<br />

El programa sigui<strong>en</strong>te:<br />

/* nombre <strong>de</strong>l archivo <strong>de</strong>moincl.~,<br />

ilustra el uso <strong>de</strong> #inclu<strong>de</strong><br />

*/<br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

#inclu<strong>de</strong> "midirec. c"


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 81<br />

1<br />

return O;<br />

equivale a<br />

/* nombre <strong>de</strong>l archivo <strong>de</strong>moinc1.c<br />

ilustra el uso <strong>de</strong> #inclu<strong>de</strong><br />

*/<br />

#inclu<strong>de</strong><br />

int main<br />

i<br />

1<br />

print f<br />

print f<br />

print f<br />

printf<br />

return<br />

"Luis Joyanes Aguilar\n") ;<br />

"Avda <strong>de</strong> Andalucía, 48\n") ;<br />

'Carchelejo, JAEN\n") ;<br />

"Andalucía, ESPAÑA\II") ;<br />

O;<br />

Ejemplo 3.2<br />

El sigui<strong>en</strong>te programa copia un m<strong>en</strong>saje <strong>en</strong> un array <strong>de</strong> caracteres y lo imprime <strong>en</strong> la pantalla. Ya que<br />

printfo y strcpy O (una función <strong>de</strong> cad<strong>en</strong>a) se utilizan, se necesitan sus archivos <strong>de</strong> cabecera<br />

especrjTcos.<br />

/* nombre <strong>de</strong>l archivo <strong>de</strong>moinc2.c<br />

utiliza dos archivos <strong>de</strong> cabecera<br />

*/<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

char m<strong>en</strong>saje [201;<br />

strcpy (m<strong>en</strong>saje, "Atapuerca\n");<br />

/* Las dos líneas anteriores también se pued<strong>en</strong> sustituir por<br />

char m<strong>en</strong>saje[20] = "Atapuerca\n";<br />

*/<br />

printf(m<strong>en</strong>saje1;<br />

return O;<br />

1<br />

Los archivos <strong>de</strong> cabecera <strong>en</strong> C ti<strong>en</strong><strong>en</strong> normalm<strong>en</strong>te una ext<strong>en</strong>sión . h y los archivos fu<strong>en</strong>te, la<br />

ext<strong>en</strong>sión . c.<br />

3.2. CREACI~N DE UN PROGRAMA<br />

Una vez creado un programa <strong>en</strong> C como el anterior, se <strong>de</strong>be ejecutar. ¿Cómo realizar esta tarea? Los<br />

pasos a dar <strong>de</strong>p<strong>en</strong><strong>de</strong>rán <strong>de</strong>l compilador C que utilice. Sin embargo, serán similares a los mostrados <strong>en</strong><br />

la Figura 3.2. En g<strong>en</strong>eral, los pasos serían:


82 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

O Utilizar un editor <strong>de</strong> texto para escribir el progratna y grabarlo <strong>en</strong> un archivo. Este archivo<br />

constituye el códigofu<strong>en</strong>te <strong>de</strong> un programa.<br />

Compilar el código fu<strong>en</strong>te. Se traduce el código fu<strong>en</strong>te <strong>en</strong> un codigo objeto (ext<strong>en</strong>sión . ob j )<br />

(l<strong>en</strong>guaje máquina <strong>en</strong>t<strong>en</strong>dible por la computadora). Un archivo objeto conti<strong>en</strong>e instrucciones <strong>en</strong><br />

l<strong>en</strong>guaje máquina que se pued<strong>en</strong> ejecutar por una computadora. Los archivos estándar C y los <strong>de</strong><br />

cabecera <strong>de</strong>finidos por el usuario son incluidos (#inclu<strong>de</strong>) <strong>en</strong> su código fu<strong>en</strong>te por el<br />

preprocesador. Los archivos <strong>de</strong> cabecera conti<strong>en</strong><strong>en</strong> información necesaria para la compilación,<br />

como es el caso <strong>de</strong> stdi0.h que conti<strong>en</strong>e información scanf() y <strong>de</strong> printf().<br />

Enlazar el código objeto con las bibliotecas correspondi<strong>en</strong>tes. Una biblioteca C conti<strong>en</strong>e código<br />

objeto <strong>de</strong> una colección <strong>de</strong> rutinas ofinciones que realizan tareas, como visualizar informaciones<br />

<strong>en</strong> la pantalla o calcular la raíz cuadrada <strong>de</strong> un número. El <strong>en</strong>lace <strong>de</strong>l código objeto <strong>de</strong>l programa<br />

con el objeto <strong>de</strong> las funciones utilizadas y cualquier otro código empleado <strong>en</strong> el <strong>en</strong>lace, producirá<br />

un código ejecutable. Un programa C consta <strong>de</strong> un número difer<strong>en</strong>te <strong>de</strong> archivos objeto y archivos<br />

biblioteca.<br />

fu<strong>en</strong>te<br />

Compilador<br />

1 Código objeto<br />

I<br />

I<br />

I<br />

Archivo<br />

cabecera<br />

I<br />

I<br />

I<br />

Bibliotecas<br />

I<br />

Enlazador<br />

I<br />

Código<br />

ejecutable<br />

I<br />

Figura 3.2. Etapas <strong>de</strong> creación <strong>de</strong> un programa<br />

Para crear un programa se utilizan las sigui<strong>en</strong>tes etapas:<br />

1. Definir su programa.<br />

2. Definir directivas <strong>de</strong>l preprocesador.<br />

3. Definición <strong>de</strong> <strong>de</strong>claraciones globales.<br />

4. Crear main ( ) .<br />

5. Crear el cuerpo <strong>de</strong>l programa.<br />

6. Crear sus propias funciones <strong>de</strong>finidas por el usuario.<br />

7. Compilar, <strong>en</strong>lazar, ejecutar y comprobar su programa.<br />

8. Utilizar com<strong>en</strong>tarios.<br />

3.3. EL PROCESO DE EJECUCIÓN DE UN PROGRAMA EN C<br />

Un programa <strong>de</strong> computadora escrito <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación (por ejemplo, C) ti<strong>en</strong>e forma <strong>de</strong><br />

un texto ordinario. Se escribe el programa <strong>en</strong> una hoja <strong>de</strong> papel y a este programa se le d<strong>en</strong>omina<br />

progmmu texto o ccídig:o.fu<strong>en</strong>te. Considérese el ejemplo s<strong>en</strong>cillo:


#inclu<strong>de</strong> <br />

int main()<br />

{<br />

1<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 83<br />

printf('Longitud <strong>de</strong> circunfer<strong>en</strong>cia <strong>de</strong> radio 5: %€",2*3.1416*5);<br />

return O;<br />

La primera operación <strong>en</strong> el proceso <strong>de</strong> ejecución <strong>de</strong> un programa es introducir las s<strong>en</strong>t<strong>en</strong>cias<br />

(instrucciones) <strong>de</strong>l programa con un editor <strong>de</strong> texto. El editor almac<strong>en</strong>a el texto y <strong>de</strong>be proporcionarle<br />

un nombre tal como area. c. Si la v<strong>en</strong>tana <strong>de</strong>l editor le muestra un nombre tal como noname. c, es<br />

conv<strong>en</strong>i<strong>en</strong>te cambiar dicho nombre (por ejemplo, por area. c). A continuación se <strong>de</strong>be guardar el texto<br />

<strong>en</strong> disco para su conservación y uso posterior, ya que <strong>en</strong> caso contrario el editor sólo almac<strong>en</strong>a el<br />

texto <strong>en</strong> memoria c<strong>en</strong>tral (RAM) y cuando se apague la computadora, o bi<strong>en</strong> ocurra alguna anomalía,<br />

se per<strong>de</strong>rá el texto <strong>de</strong> su programa. Sin embargo, si el texto <strong>de</strong>l programa se almac<strong>en</strong>a <strong>en</strong> un disquete,<br />

<strong>en</strong> un disco duro, o bi<strong>en</strong> <strong>en</strong> un CD-ROM, el programa se guardará <strong>de</strong> modo perman<strong>en</strong>te, incluso <strong>de</strong>spués<br />

<strong>de</strong> apagar la computadora y siempre que ésta se vuelva a arrancar.<br />

La Figura 3.3 muestra el método <strong>de</strong> edición <strong>de</strong> un programa y la creación <strong>de</strong>l programa <strong>en</strong> un disco,<br />

<strong>en</strong> un archivo que se d<strong>en</strong>omina archivo <strong>de</strong> texto (archivo fu<strong>en</strong>te). Con la ayuda <strong>de</strong> un editor <strong>de</strong> texto se<br />

pue<strong>de</strong> editar el texto fácilm<strong>en</strong>te, es <strong>de</strong>cir, cambiar, mover, cortar, pegar, borrar texto. Se pue<strong>de</strong> ver,<br />

normalm<strong>en</strong>te, una parte <strong>de</strong>l texto <strong>en</strong> la pantalla y se pue<strong>de</strong> marcar partes <strong>de</strong>l texto a editar con ayuda <strong>de</strong><br />

un ratón o el teclado. El modo <strong>de</strong> funcionami<strong>en</strong>to <strong>de</strong> un editor <strong>de</strong> texto y las órd<strong>en</strong>es <strong>de</strong> edición<br />

asociadas varían <strong>de</strong> un sistema a otro.<br />

4<br />

Editor<br />

<strong>de</strong><br />

texto<br />

Texto <strong>de</strong>l<br />

programa<br />

archivo<br />

fu<strong>en</strong>te<br />

1 I<br />

Figura 3.3. Proceso <strong>de</strong> edición <strong>de</strong> un archivo fu<strong>en</strong>te.<br />

Una vez editado un programa, se le proporciona un nombre. Se suele dar una ext<strong>en</strong>sión al nombre<br />

(normalm<strong>en</strong>te . c, aunque <strong>en</strong> algunos sistemas pue<strong>de</strong> t<strong>en</strong>er otros sufijos) .<br />

La sigui<strong>en</strong>te etapa es la <strong>de</strong> compilación. En ella se traduce el código fu<strong>en</strong>te escrito <strong>en</strong> l<strong>en</strong>guaje C a<br />

código máquina (<strong>en</strong>t<strong>en</strong>dible por la computadora). El programa que realiza esta traducción se llama<br />

cornpilador. Cada compilador se construye para un <strong>de</strong>terminado l<strong>en</strong>guaje <strong>de</strong> programación (por ejemplo<br />

C); un compilador pue<strong>de</strong> ser un programa in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te (como suele ser el caso <strong>de</strong> sistemas operativos<br />

como VMS, UNIX, etc.) o bi<strong>en</strong> formar parte <strong>de</strong> un programa <strong>en</strong>torno integrado <strong>de</strong> <strong>de</strong>sarrollo (EID).<br />

Los programas EID conti<strong>en</strong><strong>en</strong> todos los recursos que se necesitan para <strong>de</strong>sarrollar y ejecutar un<br />

programa, por ejemplo, editores <strong>de</strong> texto, compiladores, <strong>en</strong>lazadores, navegadores y <strong>de</strong>puradores.<br />

Cada l<strong>en</strong>guaje <strong>de</strong> programación ti<strong>en</strong>e unas reglas especiales para la construcción <strong>de</strong> programas que<br />

se d<strong>en</strong>omina sintaxis. El compilador lee el programa <strong>de</strong>l archivo <strong>de</strong> texto creado anteriorm<strong>en</strong>te y<br />

comprueba que el programa sigue las reglas <strong>de</strong> sintaxis <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> programación. Cuando se


84 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

compila su programa, el compilador traduce el código fu<strong>en</strong>te C (las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l programa) <strong>en</strong> un<br />

código máquina (código objeto). El código objeto consta <strong>de</strong> instrucciones máquina e información <strong>de</strong><br />

cómo cargar el programa <strong>en</strong> memoria antes <strong>de</strong> su ejecución. Si el compilador <strong>en</strong>cu<strong>en</strong>tra errores, los<br />

pres<strong>en</strong>tará <strong>en</strong> la pantalla. Una vez corregidos los errores con ayuda <strong>de</strong>l editor se vuelve a compilar<br />

sucesivam<strong>en</strong>te hasta que no se produzcan errores.<br />

El código objeto así obt<strong>en</strong>ido se almac<strong>en</strong>a <strong>en</strong> un archivo in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te, normalm<strong>en</strong>te con ext<strong>en</strong>sión<br />

. obj o bi<strong>en</strong> . o. Por ejemplo, el programa area anterior, se pue<strong>de</strong> almac<strong>en</strong>ar con el nombre area. obj .<br />

r-<br />

Compiiador<br />

v<br />

Archivo<br />

fu<strong>en</strong>te<br />

Figura 3.4. Proceso <strong>de</strong> edición <strong>de</strong> un archivo fu<strong>en</strong>te.<br />

El archivo objeto conti<strong>en</strong>e sólo la traducción <strong>de</strong>l código fu<strong>en</strong>te. Esto no es sufici<strong>en</strong>te para ejecutar<br />

realm<strong>en</strong>te el programa. Es necesario incluir los archivos <strong>de</strong> biblioteca (por ejemplo, <strong>en</strong> el programa<br />

area. c , stdio . h). Una biblioteca es una colección <strong>de</strong> código que ha sido programada y traducida<br />

y lista para utilizar <strong>en</strong> su programa.<br />

Normalm<strong>en</strong>te un programa consta <strong>de</strong> difer<strong>en</strong>tes unida<strong>de</strong>s o partes <strong>de</strong> programa que se han<br />

compilado in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tem<strong>en</strong>te. Por consigui<strong>en</strong>te, pue<strong>de</strong> haber varios archivos objetos. Un programa<br />

especial llamado <strong>en</strong>lazador toma el archivo objeto y las partes necesarias <strong>de</strong> la biblioteca <strong>de</strong>l sistema y<br />

construye un archivo ejecutable. Los archivos ejecutables ti<strong>en</strong><strong>en</strong> un nombre con la ext<strong>en</strong>sión . exe (<strong>en</strong><br />

el ejemplo, area. exe o simplem<strong>en</strong>te area según sea su computadora). Este archivo ejecutable conti<strong>en</strong>e<br />

todo el código máquinas necesario para ejecutar el programa. Se pue<strong>de</strong> ejecutar el programa escribi<strong>en</strong>do<br />

area <strong>en</strong> el indicador <strong>de</strong> órd<strong>en</strong>es o haci<strong>en</strong>do clic <strong>en</strong> el icono <strong>de</strong>l archivo.<br />

Figura 3.5.<br />

Proceso <strong>de</strong> conversión <strong>de</strong> código fu<strong>en</strong>te a código ejecutable.<br />

Se pue<strong>de</strong> poner ese archivo <strong>en</strong> un disquete o <strong>en</strong> un CD-ROM, <strong>de</strong> modo que esté disponible <strong>de</strong>spués<br />

<strong>de</strong> salir <strong>de</strong>l <strong>en</strong>torno <strong>de</strong>l compilador a cualquier usuario que no t<strong>en</strong>ga un compilador C o que pue<strong>de</strong> no<br />

conocer lo que hace.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 85<br />

El proceso <strong>de</strong> ejecución <strong>de</strong> un programa no suele funcionar a la primera vez; es <strong>de</strong>cir, casi siempre<br />

hay errores <strong>de</strong> sintaxis o errores <strong>en</strong> tiempo <strong>de</strong> ejecución. El proceso <strong>de</strong> <strong>de</strong>tectar y corregir errores se<br />

d<strong>en</strong>omina <strong>de</strong>puración o puesta a punto <strong>de</strong> un programa.<br />

La Figura 3.6 muestra el proceso completo <strong>de</strong> puesta a punto <strong>de</strong> un programa.<br />

( Inicio 1<br />

L<br />

editar<br />

programa<br />

programa<br />

errores<br />

<strong>en</strong> tiempo <strong>de</strong><br />

Fin ><br />

Figura 3.6. Proceso completo <strong>de</strong> <strong>de</strong>puración <strong>de</strong> un programa.<br />

Se comi<strong>en</strong>za escribi<strong>en</strong>do el archivo fu<strong>en</strong>te con el editor. Se compila el archivo fu<strong>en</strong>te y se<br />

comprueban m<strong>en</strong>sajes <strong>de</strong> errores. Se retorna al editor y se fijan los errores <strong>de</strong> sintaxis. Cuando el<br />

compilador ti<strong>en</strong>e éxito, el <strong>en</strong>lazador construye el archivo ejecutable. Se ejecuta el archivo ejecutable. Si<br />

se <strong>en</strong>cu<strong>en</strong>tra un error, se pue<strong>de</strong> activar el <strong>de</strong>purador para ejecutar s<strong>en</strong>t<strong>en</strong>cia a s<strong>en</strong>t<strong>en</strong>cia. Una vez que se<br />

<strong>en</strong>cu<strong>en</strong>tra la causa <strong>de</strong>l error, se vuelve al editor y se repite la compilación. El proceso <strong>de</strong> compilar,<br />

<strong>en</strong>lazar y ejecutar el programa se repetirá hasta que no se produzcan errores.<br />

Etapas <strong>de</strong>l proceso<br />

O El código fu<strong>en</strong>te (archivo <strong>de</strong>l programa) se crea con la ayuda <strong>de</strong>l editor <strong>de</strong> texto.<br />

O El compilador traduce el archivo texto <strong>en</strong> un archivo objeto.<br />

El <strong>en</strong>lazador pone juntos a difer<strong>en</strong>tes archivos objetos para poner un archivo ejecutable.<br />

O El sistema operativo pone el archivo ejecutable <strong>en</strong> la memoria c<strong>en</strong>tral y se ejecuta el<br />

programa.


86 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3.4. DEPURACIÓN DE UN PROGRAMA EN C<br />

Rara vez los programas funcionan bi<strong>en</strong> la primera vez que se ejecutan. Los errores que se produc<strong>en</strong> <strong>en</strong><br />

los programas han <strong>de</strong> ser <strong>de</strong>tectados, aislados (fijados) y corregidos. El proceso <strong>de</strong> <strong>en</strong>contrar errores se<br />

d<strong>en</strong>omina <strong>de</strong>puración <strong>de</strong>l programa. La corrección <strong>de</strong>l error es probablem<strong>en</strong>te la etapa más fácil, si<strong>en</strong>do<br />

la <strong>de</strong>tección y aislami<strong>en</strong>to <strong>de</strong>l error las tareas más difíciles.<br />

Exist<strong>en</strong> difer<strong>en</strong>tes situaciones <strong>en</strong> las cuales se suel<strong>en</strong> introducir errores <strong>en</strong> un programa. Dos <strong>de</strong> las<br />

más frecu<strong>en</strong>tes son:<br />

1. Violación (no cumplimi<strong>en</strong>to) <strong>de</strong> las reglas gramaticales <strong>de</strong>l l<strong>en</strong>guaje <strong>de</strong> alto nivel <strong>en</strong> el que se<br />

escribe el programa.<br />

2. Los errores <strong>en</strong> el diseño <strong>de</strong>l algoritmo <strong>en</strong> el que está basado el programa.<br />

Cuando el compilador <strong>de</strong>tecta un error, visualiza un m<strong>en</strong>saje <strong>de</strong> error indicando que se ha cometido<br />

un error y posible causa <strong>de</strong>l error. Desgraciadam<strong>en</strong>te los m<strong>en</strong>sajes <strong>de</strong> error son difíciles <strong>de</strong> interpretar<br />

y a veces se llegan a conclusiones erróneas. También varían <strong>de</strong> un compilador a otro compilador. A<br />

medida que se gana <strong>en</strong> experi<strong>en</strong>cia, el proceso <strong>de</strong> puesta a punto <strong>de</strong> un programa se mejora<br />

consi<strong>de</strong>rablem<strong>en</strong>te. Nuestro objetivo <strong>en</strong> cada capítulo es <strong>de</strong>scribir los errores que ocurr<strong>en</strong> más<br />

frecu<strong>en</strong>tem<strong>en</strong>te y sugerir posibles causas <strong>de</strong> error, junto con reglas <strong>de</strong> estilo <strong>de</strong> escritura <strong>de</strong> programas.<br />

Des<strong>de</strong> el punto <strong>de</strong> vista conceptual exist<strong>en</strong> tres tipos <strong>de</strong> errores: sintaxis, lógicos y <strong>de</strong> regresión.<br />

3.4.1. Errores <strong>de</strong> sintaxis<br />

Los errores <strong>de</strong> sintaxis son aquellos que se produc<strong>en</strong> cuando el programa viola la sintaxis, es <strong>de</strong>cir,<br />

las reglas <strong>de</strong> gramática <strong>de</strong>l l<strong>en</strong>guaje. Errores <strong>de</strong> sintaxis típicos son: escritura incorrecta <strong>de</strong> palabras<br />

reservadas, omisión <strong>de</strong> signos <strong>de</strong> puntuación (comillas, punto y coma.. .). Los errores <strong>de</strong> sintaxis son los<br />

más fáciles <strong>de</strong> fijar, ya que ellos son <strong>de</strong>tectados y aislados por el compilador.<br />

Estos errores se suel<strong>en</strong> <strong>de</strong>tectar por el compilador durante el proceso <strong>de</strong> compilación. A medida que<br />

se produce el proceso <strong>de</strong> traducción <strong>de</strong>l código fu<strong>en</strong>te (por ejemplo, programa escrito <strong>en</strong> C) a l<strong>en</strong>guaje<br />

máquina <strong>de</strong> la computadora, el compilador verifica si el programa que se está traduci<strong>en</strong>do cumple las<br />

reglas <strong>de</strong> sintaxis <strong>de</strong>l l<strong>en</strong>guaje. Si el programa viola alguna <strong>de</strong> estas reglas, el compilador g<strong>en</strong>era un<br />

m<strong>en</strong>suje <strong>de</strong> error (o diagnóstico) que explica el problema (apar<strong>en</strong>te). Algunos errores típicos (ya citados<br />

anteriorm<strong>en</strong>te):<br />

o Punto y coma <strong>de</strong>spués <strong>de</strong> la cabecera ma in ( ) .<br />

O Omisión <strong>de</strong> punto y coma al final <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia.<br />

0 Olvido <strong>de</strong> la secu<strong>en</strong>cia */ para finalizar un com<strong>en</strong>tario.<br />

0 Olvido <strong>de</strong> las dobles comillas al cerrar una cad<strong>en</strong>a.<br />

o Etc.<br />

Si una s<strong>en</strong>t<strong>en</strong>cia ti<strong>en</strong>e un error <strong>de</strong> sintaxis no se traducirá completam<strong>en</strong>te y el programa no se<br />

ejecutará. Así, por ejemplo, si una línea <strong>de</strong> programa es<br />

double radio<br />

se producirá un error ya que falta el punto y coma (;) <strong>de</strong>spués <strong>de</strong> la letra última "o". Posteriorm<strong>en</strong>te se<br />

explicará el proceso <strong>de</strong> corrección por parte <strong>de</strong>l programador.<br />

I<br />

I<br />

3.4.2. Errores lógicos<br />

Un segundo tipo <strong>de</strong> error importante es el error lógico, ya que tal error repres<strong>en</strong>ta errores <strong>de</strong>l<br />

programador <strong>en</strong> el diseño <strong>de</strong>l algoritmo y posterior programa. Los errores lógicos son más difíciles <strong>de</strong><br />

<strong>en</strong>contrar y aislar ya que no suel<strong>en</strong> ser <strong>de</strong>tectados por el compilador.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 87<br />

Suponga, por ejemplo, que una línea <strong>de</strong> un programa conti<strong>en</strong>e la s<strong>en</strong>t<strong>en</strong>cia<br />

double peso = d<strong>en</strong>sidad * 5.25 * PI * pow(longitud,5)/4.0<br />

pero resulta que el tercer asterisco (operador <strong>de</strong> multiplicación) es <strong>en</strong> realidad un signo + (operador<br />

suma). El compilador no produce ningún m<strong>en</strong>saje <strong>de</strong> error <strong>de</strong> sintaxis ya que no se ha violado ninguna<br />

regla <strong>de</strong> sintaxis y, por tanto, el cornpilador no <strong>de</strong>tecta error y el programa se compilará y ejecutará<br />

bi<strong>en</strong>, aunque producirá resultados <strong>de</strong> valores incorrectos ya que la fórmula utilizada para calcular el<br />

peso conti<strong>en</strong>e un error lógico.<br />

Una vez que se ha <strong>de</strong>terminado que un programa conti<strong>en</strong>e un error lógico -si es que se <strong>en</strong>cu<strong>en</strong>tra<br />

<strong>en</strong> la primera ejecución y no pasa <strong>de</strong>sapercibida al programador- <strong>en</strong>contrar el error es una <strong>de</strong> las tareas<br />

más difíciles <strong>de</strong> la programación. El <strong>de</strong>purador (<strong>de</strong>bugger) un programa <strong>de</strong> software diseñado<br />

específicam<strong>en</strong>te para la <strong>de</strong>tección, verificación y corrección <strong>de</strong> errores, ayudará <strong>en</strong> las tareas <strong>de</strong><br />

<strong>de</strong>puración.<br />

Los errores lógicos ocurr<strong>en</strong> cuando un programa es la implem<strong>en</strong>tación <strong>de</strong> un algoritmo <strong>de</strong>fectuoso.<br />

Dado que los errores lógicos normalm<strong>en</strong>te no produc<strong>en</strong> errores <strong>en</strong> tiempo <strong>de</strong> ejecución y no visualizan<br />

m<strong>en</strong>sajes <strong>de</strong> error; son más difíciles <strong>de</strong> <strong>de</strong>tectar porque el programa parece ejecutarse sin contratiempos.<br />

El único signo <strong>de</strong> un error lógico pue<strong>de</strong> ser la salida incorrecta <strong>de</strong> un programa. La s<strong>en</strong>t<strong>en</strong>cia<br />

total-grados-c<strong>en</strong>tigrados = fahr<strong>en</strong>heit-a-c<strong>en</strong>tigrddos<br />

* temperatura-c<strong>en</strong>;<br />

es una s<strong>en</strong>t<strong>en</strong>cia perfectam<strong>en</strong>te legal <strong>en</strong> C, pero la ecuación no respon<strong>de</strong> a ningún cálculo válido para<br />

obt<strong>en</strong>er el total <strong>de</strong> grados c<strong>en</strong>tígrados <strong>en</strong> una sala.<br />

Se pued<strong>en</strong> <strong>de</strong>tectar errores lógicos comprobando el programa <strong>en</strong> su totalidad, comprobando su salida<br />

con los resultados previstos. Se pued<strong>en</strong> prev<strong>en</strong>ir errores lógicos con un estudio minucioso y <strong>de</strong>tallado<br />

<strong>de</strong>l algoritmo antes <strong>de</strong> que el programa se ejecute, pero resultará fácil cometer errores lógicos y es el<br />

conocimi<strong>en</strong>to <strong>de</strong> C, <strong>de</strong> las técnicas algorítmicas y la experi<strong>en</strong>cia lo que permitirá la <strong>de</strong>tección <strong>de</strong> los<br />

errores lógicos.<br />

3.4.3. Errores <strong>de</strong> regresión<br />

Los errores <strong>de</strong> regresión son aquellos que se crean accid<strong>en</strong>talm<strong>en</strong>te cuando se int<strong>en</strong>ta corregir un error<br />

lógico. Siempre que se corrige un error se <strong>de</strong>be comprobar totalm<strong>en</strong>te la exactitud (corrección) para<br />

asegurarse que se fija el error que se está tratando y no produce otro error. Los errores <strong>de</strong> regresión son<br />

comunes, pero son fáciles <strong>de</strong> leer y corregir. Una ley no escrita es que: «un error se ha producido,<br />

probablem<strong>en</strong>te, por el último código modificadon.<br />

3.4.4. M<strong>en</strong>sajes <strong>de</strong> error<br />

Los compiladores emit<strong>en</strong> m<strong>en</strong>sajes <strong>de</strong> error o <strong>de</strong> advert<strong>en</strong>cia durante las fases <strong>de</strong> compilación, <strong>de</strong> <strong>en</strong>lace<br />

o <strong>de</strong> ejecución <strong>de</strong> un programa.<br />

Los m<strong>en</strong>sajes <strong>de</strong> error producidos durante la compilación se suel<strong>en</strong> producir, normalm<strong>en</strong>te, por<br />

errores <strong>de</strong> sintaxis y suele variar según los compiladores; pero, <strong>en</strong> g<strong>en</strong>eral, se agrupan <strong>en</strong> tres gran<strong>de</strong>s<br />

bloques:<br />

Errores fatales. Son raros. Algunos <strong>de</strong> ellos indican un error interno <strong>de</strong>l compilador. Cuando<br />

ocurre un error fatal, la compilación se <strong>de</strong>ti<strong>en</strong>e inmediatam<strong>en</strong>te, se <strong>de</strong>be tomar la acción apropiada<br />

y a continuación se vuelve a iniciar la compilación.<br />

O Errores <strong>de</strong> sintaxis. Son los errores típicos <strong>de</strong> sintaxis, errores <strong>de</strong> línea <strong>de</strong> órd<strong>en</strong>es y errores <strong>de</strong><br />

acceso a memoria o disco. El compilador terminará la fase actual <strong>de</strong> compilación y se <strong>de</strong>ti<strong>en</strong>e.<br />

Advert<strong>en</strong>cias (warning). No impid<strong>en</strong> la compilación. Indican condiciones que son sospechosas,<br />

pero son legítimas como parte <strong>de</strong>l l<strong>en</strong>guaje.


88 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3.4.5. Errores <strong>en</strong> tiempo <strong>de</strong> ejecución<br />

Exist<strong>en</strong> dos tipos <strong>de</strong> errores <strong>en</strong> tiempo <strong>de</strong> ejecución: aquellos que son <strong>de</strong>tectados por el sistema <strong>en</strong><br />

tiempo <strong>de</strong> ejecución <strong>de</strong> C y aquellos que permit<strong>en</strong> la terminación <strong>de</strong>l programa pero produc<strong>en</strong> resultados<br />

incorrectos.<br />

Un error <strong>en</strong> tiempo <strong>de</strong> ejecución pue<strong>de</strong> ocurrir como resultado <strong>de</strong> que el programa obliga a la<br />

computadora a realizar una operación ilegal, tal como dividir un número por cero, raíz cuadrada <strong>de</strong> un<br />

número negativo o manipular <strong>datos</strong> no válidos o no <strong>de</strong>finidos. Cuando ocurre este tipo <strong>de</strong> error, la<br />

computadora <strong>de</strong>t<strong>en</strong>drá la ejecución <strong>de</strong> su programa y emitirá (visualizará) un m<strong>en</strong>saje <strong>de</strong> diagnóstico tal<br />

como:<br />

Divi<strong>de</strong> error, line number ***<br />

Si se int<strong>en</strong>ta manipular <strong>datos</strong> no válidos o in<strong>de</strong>finidos su salida pue<strong>de</strong> cont<strong>en</strong>er resultados extraños.<br />

Por ejemplo, se pue<strong>de</strong> producir un <strong>de</strong>sbordumi<strong>en</strong>to aritmético cuando un programa int<strong>en</strong>ta almac<strong>en</strong>ar<br />

un número que es mayor que el tamaño máximo que pue<strong>de</strong> manipular su computadora.<br />

El programa <strong>de</strong>purar. c se compila con éxito; pero no conti<strong>en</strong>e ninguna s<strong>en</strong>t<strong>en</strong>cia que asigne un<br />

valor a la variable x que pueda sumarse a y para producir un valor z, por lo tanto al ejecutarse la<br />

s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> asignación<br />

z=x+y;<br />

se produce un error <strong>en</strong> tiempo <strong>de</strong> ejecución, un error <strong>de</strong> lógica.<br />

1: /* archivo <strong>de</strong>purar<br />

2: prueba <strong>de</strong> errores <strong>en</strong> tiempo <strong>de</strong> ejecución<br />

3: */<br />

4: #inclu<strong>de</strong> <br />

5:<br />

6: void main0<br />

I: {<br />

8: /* Variables locales */<br />

9: float x, y, z;<br />

10:<br />

11: y = 10.0<br />

12: z = x + y; /* valor inesperado: error <strong>de</strong> ejecución */<br />

13: printf("E1 valor <strong>de</strong> z es = %f\n",z);<br />

14: 1<br />

El programa anterior, sin embargo, podría terminar su ejecución, aunque produciría resultados<br />

incorrectos. Dado que no se asigna ningún valor a x, cont<strong>en</strong>drá un valor impre<strong>de</strong>cible y el resultado <strong>de</strong><br />

la suma será también impre<strong>de</strong>cible. Muchos compiladores inicializan las variables automáticam<strong>en</strong>te a<br />

cero, haci<strong>en</strong>do <strong>en</strong> este caso más difícil <strong>de</strong> <strong>de</strong>tectar la omisión, sobre todo cuando el programa se<br />

transfiere a otro compilador que no asigna ningún valor <strong>de</strong>finido.<br />

Otra fu<strong>en</strong>te <strong>de</strong> errores <strong>en</strong> tiempo <strong>de</strong> ejecución se suele producir por errores <strong>en</strong> la <strong>en</strong>trada <strong>de</strong> <strong>datos</strong><br />

producidos por la lectura <strong>de</strong>l dato incorrecto <strong>en</strong> una variable <strong>de</strong> <strong>en</strong>trada.<br />

3.5. PRUEBAS<br />

Los errores <strong>de</strong> ejecución ocurr<strong>en</strong> <strong>de</strong>spués que el programa se ha compilado con éxito y aún se está<br />

ejecutando. Exist<strong>en</strong> ciertos errores que la computadora sólo pue<strong>de</strong> <strong>de</strong>tectar cuando se ejecuta el<br />

programa. La mayoría <strong>de</strong> los sistemas informáticos <strong>de</strong>tectarán ciertos errores <strong>en</strong> tiempo <strong>de</strong> ejecución y<br />

pres<strong>en</strong>tarán un m<strong>en</strong>saje <strong>de</strong> error apropiado. Muchos errores <strong>en</strong> tiempo <strong>de</strong> ejecución ti<strong>en</strong><strong>en</strong> que ver con<br />

los cálculos numéricos. Por ejemplo, si la computadora int<strong>en</strong>ta dividir un número por cero o leer un<br />

archivo no creado, se produce un error <strong>en</strong> tiempo <strong>de</strong> ejecución.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 89<br />

Es preciso t<strong>en</strong>er pres<strong>en</strong>te que el compilador pue<strong>de</strong> no emitir ningún m<strong>en</strong>saje <strong>de</strong> error durante la<br />

ejecución y eso no garantiza que el programa sea correcto. Recuer<strong>de</strong> que el compilador sólo le indica<br />

si se escribió bi<strong>en</strong> sintácticam<strong>en</strong>te un programa <strong>en</strong> C. No indica si el programa hace lo que realm<strong>en</strong>te<br />

<strong>de</strong>sea que haga. Los errores lógicos pued<strong>en</strong> aparecer -y <strong>de</strong> hecho aparecerán- por un mal diseño <strong>de</strong>l<br />

algoritmo y posterior programa.<br />

Para <strong>de</strong>terminar si un programa conti<strong>en</strong>e un error lógico, se <strong>de</strong>be ejecutar utilizando <strong>datos</strong> <strong>de</strong><br />

muestra y comprobar la salida verificando su exactitud. Esta prueba (testing) se <strong>de</strong>be hacer varias veces<br />

utilizando difer<strong>en</strong>tes <strong>en</strong>tradas, preparadas -<strong>en</strong> el caso i<strong>de</strong>al-, por personas difer<strong>en</strong>tes al programador,<br />

que puedan indicar suposiciones no evid<strong>en</strong>tes <strong>en</strong> la elección <strong>de</strong> los <strong>datos</strong> <strong>de</strong> prueba. Si cualquier<br />

combinación <strong>de</strong> <strong>en</strong>tradas produce salida incorrecta, <strong>en</strong>tonces el programa conti<strong>en</strong>e un error lógico.<br />

Una vez que se ha <strong>de</strong>terminado que un programa conti<strong>en</strong>e un error lógico, la localización <strong>de</strong>l error<br />

es una <strong>de</strong> las partes más difíciles <strong>de</strong> la programación. La ejecución se <strong>de</strong>be realizar paso a paso (seguir<br />

la traza) hasta el punto <strong>en</strong> que se observe que un valor calculado difiere <strong>de</strong>l valor esperado. Para<br />

simplificar este seguimi<strong>en</strong>to o traza, la mayoría <strong>de</strong> los compiladores <strong>de</strong> C proporcionan un <strong>de</strong>purador<br />

integrado' incorporado con el editor, y todos ellos <strong>en</strong> un mismo paquete <strong>de</strong> software, que permit<strong>en</strong> al<br />

programador ejecutar realm<strong>en</strong>te un programa, línea a línea, observando los efectos <strong>de</strong> la ejecución <strong>de</strong><br />

cada línea <strong>en</strong> los valores <strong>de</strong> los objetos <strong>de</strong>l programa. Una vez que se ha localizado el error, se utilizará<br />

el editor <strong>de</strong> texto para corregir dicho error.<br />

Es preciso hacer constar que casi nunca será posible comprobar un programa para todos los posibles<br />

conjuntos <strong>de</strong> <strong>datos</strong> <strong>de</strong> prueba. Exist<strong>en</strong> casos <strong>en</strong> <strong>de</strong>sarrollos profesionales <strong>en</strong> los que, apar<strong>en</strong>tem<strong>en</strong>te, los<br />

programas han estado si<strong>en</strong>do utilizados sin problemas durante años, hasta que se utilizó una<br />

combinación específica <strong>de</strong> <strong>en</strong>tradas y ésta produjo una salida incorrecta <strong>de</strong>bida a un error lógico. El<br />

conjunto <strong>de</strong> <strong>datos</strong> específicos que produjo el error nunca se había introducido.<br />

A medida que los programas crec<strong>en</strong> <strong>en</strong> tamaño y complejidad, el problema <strong>de</strong> las pruebas se<br />

convierte <strong>en</strong> un problema <strong>de</strong> dificultad cada vez más creci<strong>en</strong>te. No importa cuantas pruebas se hagan:<br />


90 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3.6.2. Id<strong>en</strong>tificadores<br />

Un id<strong>en</strong>t$cador es una secu<strong>en</strong>cia <strong>de</strong> caracteres, letras, dígitos y subrayados (J. El primer carácter <strong>de</strong>be<br />

ser una letra (algún compilador admite carácter <strong>de</strong> subrayado). Las letras mayúsculas y minúsculas son<br />

difer<strong>en</strong>tes.<br />

nombre-clase<br />

elem<strong>en</strong>tomayor<br />

a<br />

Indice<br />

Cantidad-Total<br />

Habitacionl20<br />

DiaMesAnyo<br />

Fecha-Compra-Casa<br />

En Borland C/C++ el id<strong>en</strong>tificador pue<strong>de</strong> ser <strong>de</strong> cualquier longitud; sin embargo, el compilador<br />

ignora cualquier carácter fuera <strong>de</strong> los 32 primeros.<br />

C es s<strong>en</strong>sible a las mayúsculas. Por consigui<strong>en</strong>te, C reconoce como distintos los id<strong>en</strong>tificadores ALFA,<br />

al f a y ALFa. (Le recom<strong>en</strong>damos que utilice siempre el mismo estilo d escribir sus id<strong>en</strong>tificadores.) Un<br />

consejo que pue<strong>de</strong> servir <strong>de</strong> posible regla pue<strong>de</strong> ser:<br />

1. Escribir id<strong>en</strong>tificadores <strong>de</strong> variables <strong>en</strong> letras minúsculas.<br />

2. Constantes <strong>en</strong> mayúsculas.<br />

3. Funciones con tipo <strong>de</strong> letra mixto: mayúsculdminúscula.<br />

Reglas básicas <strong>de</strong> formación <strong>de</strong> id<strong>en</strong>tificadores<br />

1. Secu<strong>en</strong>cia <strong>de</strong> letras o dígitos; el primer carácter pue<strong>de</strong> ser una letra o un subrayado<br />

(compiladores <strong>de</strong> Borland, <strong>en</strong>tre otros).<br />

2. Los id<strong>en</strong>tificadores son s<strong>en</strong>sibles a las mayúsculas:<br />

*<br />

minun es distinto <strong>de</strong><br />

MiNum<br />

3. Los id<strong>en</strong>tificadores pued<strong>en</strong> t<strong>en</strong>er cualquier longitud, pero sólo son significativos los 32<br />

primeros (ése es el caso <strong>de</strong> Borland y Microsoft).<br />

4. Los id<strong>en</strong>tificadores no pued<strong>en</strong> ser palabras reservadas, tales como if, switch o else.<br />

1<br />

3.6.3. Palabras reservadas<br />

Una palabra reservada (keyword o resewed word), tal como void es una característica <strong>de</strong>l l<strong>en</strong>guaje C<br />

asociada con algún significado especial. Una palabra reservada no se pue<strong>de</strong> utilizar como nombre <strong>de</strong><br />

id<strong>en</strong>tificador o función<br />

void void( ) /* error */<br />

i<br />

...<br />

int char; /* error */<br />

...<br />

Los sigui<strong>en</strong>tes id<strong>en</strong>tificadores están reservados para utilizarlos como palabras reservadas, y no se<br />

<strong>de</strong>b<strong>en</strong> emplear para otros propósitos.<br />

a sm <strong>en</strong>um signed<br />

auto extern sizeof<br />

break float static<br />

case for struct<br />

char goto switch<br />

const if type<strong>de</strong>f


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos<br />

continue<br />

<strong>de</strong>fault<br />

do<br />

double<br />

else<br />

int<br />

long<br />

register<br />

return<br />

short<br />

union<br />

un8 igned<br />

void<br />

vol at i 1 e<br />

while<br />

3.6.4. Com<strong>en</strong>tarios<br />

Ya se ha expuesto antes que los com<strong>en</strong>tarios <strong>en</strong> C ti<strong>en</strong><strong>en</strong> el formato:<br />

/*. . .*/<br />

Los com<strong>en</strong>tarios se <strong>en</strong>cierran <strong>en</strong>tre / * y * / pued<strong>en</strong> ext<strong>en</strong><strong>de</strong>rse a lo largo <strong>de</strong> varias líneas.<br />

/* Titulo: Demo-uno por Mr. Martinez */<br />

Otra forma, el com<strong>en</strong>tario <strong>en</strong> dos líneas:<br />

/* Cabecera <strong>de</strong>l programa text-uno<br />

Autor: J.R. Mazinger */<br />

3.6.5. Signos <strong>de</strong> puntuación y separadores<br />

Todas las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>b<strong>en</strong> terminar con un punto y coma. Otros signos <strong>de</strong> puntuación son:<br />

i % A & * o<br />

-+={ } -<br />

[ I \ ; ' : < > ? , . / I '<br />

Los separadores son espacios <strong>en</strong> blanco, tabulaciones, retornos <strong>de</strong> carro y avances <strong>de</strong> línea.<br />

3.6.6. Archivos <strong>de</strong> cabecera<br />

Un archivo <strong>de</strong> cabecera es un archivo especial que conti<strong>en</strong>e <strong>de</strong>claraciones <strong>de</strong> elem<strong>en</strong>tos y funciones <strong>de</strong><br />

la biblioteca. Para utilizar macros, constantes, tipos y funciones almac<strong>en</strong>adas <strong>en</strong> una biblioteca, un<br />

programa <strong>de</strong>be utilizar la directiva #inclu<strong>de</strong> para insertar el archivo <strong>de</strong> cabecera correspondi<strong>en</strong>te. Por<br />

ejemplo, si un programa utiliza la función pow que se almac<strong>en</strong>a <strong>en</strong> la biblioteca matemática math.h,<br />

<strong>de</strong>be cont<strong>en</strong>er la directiva<br />

#inclu<strong>de</strong> <br />

para hacer que el cont<strong>en</strong>ido <strong>de</strong> la biblioteca matemática esté disponible a un programa. La mayoría <strong>de</strong><br />

los programas conti<strong>en</strong><strong>en</strong> líneas como ésta al principio, que se incluy<strong>en</strong> <strong>en</strong> el mom<strong>en</strong>to <strong>de</strong> compilación.<br />

#inclu<strong>de</strong> <br />

/* o bi<strong>en</strong> */<br />

#inclu<strong>de</strong> "stdio. h"<br />

3.7. TIPOS DE DATOS EN C<br />

C no soporta un gran número <strong>de</strong> tipos <strong>de</strong> <strong>datos</strong> pre<strong>de</strong>finidos, pero ti<strong>en</strong>e la capacidad para crear sus<br />

propios tipos <strong>de</strong> <strong>datos</strong>. Todos los tipos <strong>de</strong> <strong>datos</strong> simples o básicos <strong>de</strong> C son, es<strong>en</strong>cialm<strong>en</strong>te, números. Los<br />

tres tipos <strong>de</strong> <strong>datos</strong> básicos son:


92 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

<strong>en</strong>teros;<br />

números <strong>de</strong> coma flotante (reales);<br />

0 caracteres.<br />

La Tabla 3.1 recoge los principales tipos <strong>de</strong> <strong>datos</strong> básicos, sus tamaños <strong>en</strong> bytes y el rango <strong>de</strong> valores<br />

que pue<strong>de</strong> almac<strong>en</strong>ar.<br />

Tabla 3.1. Tipos <strong>de</strong> <strong>datos</strong> simples <strong>de</strong> C.<br />

Tipo Ejemplo Tamaño Rango<br />

<strong>en</strong> bytes<br />

Mínimo..Máximo<br />

char 'C' I 0. .255<br />

short -15 2 -128.. 127<br />

int 1024 2 -32768..32767<br />

unsigned int 42325 2 0.. 65535<br />

long 262144 4 -2147483648..2147483637<br />

float 10.5 4 3.4*(10 )..3.4*(10 )<br />

double O. 00045 8 1.7*(10 )..1.7*(10 )<br />

long double le-8 8 igual que double<br />

Los tipos <strong>de</strong> <strong>datos</strong> fundam<strong>en</strong>tales <strong>en</strong> C son:<br />

o <strong>en</strong>teros: (números completos y sus negativos), <strong>de</strong> tipo int.<br />

variantes <strong>de</strong> <strong>en</strong>teros: tipos short, long y unsigned.<br />

o reales: números <strong>de</strong>cimales, tipos float, double o long double.<br />

o caracteres: letras, dígitos, símbolos y signos <strong>de</strong> puntuación, tipo char.<br />

char, int , float y double son palabras reservadas, o más específicam<strong>en</strong>te, especificadores <strong>de</strong><br />

tipos. Cada tipo <strong>de</strong> dato ti<strong>en</strong>e su propia lista <strong>de</strong> atributos que <strong>de</strong>fin<strong>en</strong> las características <strong>de</strong>l tipo y pued<strong>en</strong><br />

variar <strong>de</strong> una máquina a otra. Los tipos char, i nt y doubl e ti<strong>en</strong><strong>en</strong> variaciones o modijcadores <strong>de</strong><br />

tipos <strong>de</strong> <strong>datos</strong>, tales como short, long, signed y unsigned, para permitir un uso más efici<strong>en</strong>te <strong>de</strong><br />

los tipos <strong>de</strong> <strong>datos</strong>.<br />

Existe el tipo adicional <strong>en</strong>um (constante <strong>de</strong> <strong>en</strong>umeración (Capítulo 9).<br />

3.7.1. Enteros (int)<br />

Probablem<strong>en</strong>te el tipo <strong>de</strong> dato más familiar es el <strong>en</strong>tero, o tipo int. Los <strong>en</strong>teros son a<strong>de</strong>cuados para<br />

aplicaciones que trabaj<strong>en</strong> con <strong>datos</strong> numéricos. Los tipos <strong>en</strong>teros se almac<strong>en</strong>an internam<strong>en</strong>te <strong>en</strong> 2 bytes<br />

(o 16 bits) <strong>de</strong> memoria. La Tabla 3.2 resume los tres tipos <strong>en</strong>teros básicos, junto con el rango <strong>de</strong> valores<br />

y el tamaño <strong>en</strong> bytes usual, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> cada máquina.<br />

Tabla 3.2. Tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong>teros.<br />

Tipo C Rango <strong>de</strong> valores Uso recom<strong>en</strong>dado<br />

int -32.768 .. +32.767 Aritmética <strong>de</strong> <strong>en</strong>teros, bucles for, conteo.<br />

unsigned int 0 . . 65.535 Conteo, bucles for, índices.<br />

short int -128 .. +127 Aritmética <strong>de</strong> <strong>en</strong>teros, bucles for, conteo.


Declaración <strong>de</strong> variables<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 93<br />

La forma más simple <strong>de</strong> una <strong>de</strong>claración <strong>de</strong> variable <strong>en</strong> C es poner primero el tipo <strong>de</strong> dato y a continuación<br />

el nombre <strong>de</strong> la variable. Si se <strong>de</strong>sea dar un valor inicial a la variable, éste se pone a continuación.<br />

El formato <strong>de</strong> la <strong>de</strong>claración es:<br />

= <br />

Se pued<strong>en</strong> también <strong>de</strong>clarar múltiples variables <strong>en</strong> la misma línea:<br />

. . . <br />

Así, por ejemplo,<br />

int longitud; int valor = 99;<br />

int valorl, valor2;<br />

int num-parte = 1141, num-items = 45;<br />

Los tres modificadores (unsigned, short, int) que funcionan con int (Tabla 3.3) varían el<br />

rango <strong>de</strong> los <strong>en</strong>teros.<br />

En aplicaciones g<strong>en</strong>erales, las constantes <strong>en</strong>teras se escrib<strong>en</strong> <strong>en</strong> <strong>de</strong>cimal o base IO; por ejemplo,<br />

1 0 0, 2 0 0 o 4 5 O. Para escribir una constante sin signo, se aña<strong>de</strong> la letra u (o bi<strong>en</strong> u). Por ejemplo, para<br />

escribir 4 0 . O 0 0, escriba 4 0 O 0 Ou.<br />

Si se utiliza C para <strong>de</strong>sarrollar software para sistemas operativos o para hardware <strong>de</strong> computadora,<br />

será Útil escribir constantes <strong>en</strong>teras <strong>en</strong> octal (base 8) o hexa<strong>de</strong>cimal (base 16). Una constante octal es<br />

cualquier número que comi<strong>en</strong>za con un O y conti<strong>en</strong>e dígitos <strong>en</strong> el rango <strong>de</strong> 1 a 7. Por ejemplo, 0377 es<br />

un número octal. Una constante hexa<strong>de</strong>cimal comi<strong>en</strong>za con Ox y va seguida <strong>de</strong> los dígitos O a 9 o las<br />

letras A a F (o bi<strong>en</strong> a a f). Por ejemplo, OxFF16 es una constante hexa<strong>de</strong>cimal.<br />

La Tabla 3.3 muestra ejemplos <strong>de</strong> constantes <strong>en</strong>teras repres<strong>en</strong>tadas <strong>en</strong> sus notaciones (bases)<br />

<strong>de</strong>cimal, hexa<strong>de</strong>cimal y octal.<br />

Cuando el rango <strong>de</strong> los tipos <strong>en</strong>teros básicos no es sufici<strong>en</strong>tem<strong>en</strong>te gran<strong>de</strong> para sus necesida<strong>de</strong>s, se<br />

consi<strong>de</strong>ran tipos <strong>en</strong>teros largos. La Tabla 3.4 muestra los dos tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong>teros largos. Ambos tipos<br />

requier<strong>en</strong> 4 bytes <strong>de</strong> memoria (32 bits) <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to. Un ejemplo <strong>de</strong> uso <strong>de</strong> <strong>en</strong>teros largos es:<br />

long medidamilimetros;<br />

unsigned long distanciamedia;<br />

Tabla 3.3. Constantes <strong>en</strong>teras <strong>en</strong> tres bases difer<strong>en</strong>tes.<br />

Base 10<br />

De c i m a 1<br />

8<br />

10<br />

16<br />

65536<br />

24<br />

17<br />

Base 16<br />

Hexa<strong>de</strong>cimal (Hex)<br />

0x08<br />

OxOA<br />

ox10<br />

Ox100 00<br />

0x18<br />

0x11<br />

Base 8<br />

Octal<br />

O10<br />

012<br />

020<br />

0200000<br />

030<br />

021<br />

Si se <strong>de</strong>sea forzar al compilador para tratar sus constantes como iong, añada la letra L, (o bi<strong>en</strong> 1) a<br />

su constante. Por ejemplo,<br />

long numeros-gran<strong>de</strong>s = 40000L;


94 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Tabla 3.4. Tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong>teros largos.<br />

Tipo C<br />

long<br />

unsigned long<br />

Rango <strong>de</strong> valores<br />

-2147483648 .. 2147483647<br />

O .. +4294967295<br />

3.7.2. Tipos <strong>de</strong> coma flotante (float/double)<br />

Los tipos <strong>de</strong> <strong>datos</strong> <strong>de</strong> coma (punto) flotante repres<strong>en</strong>tan números reales que conti<strong>en</strong><strong>en</strong> una coma (un<br />

punto) <strong>de</strong>cimal, tal como 3.14159, o números muy gran<strong>de</strong>s, tales como 1.85* IO”.<br />

La <strong>de</strong>claración <strong>de</strong> las variables <strong>de</strong> coma flotante es igual que la <strong>de</strong> variables <strong>en</strong>teras. Así, un ejemplo<br />

es el sigui<strong>en</strong>te:<br />

float valor; /* <strong>de</strong>clara und. variable real */<br />

float valorl, valor2; /* <strong>de</strong>clara varias variables <strong>de</strong> coma flotante */<br />

float valor = 99.99; /* asigna el valor 99.99 a la variable valor */<br />

C soporta tres formatos <strong>de</strong> coma flotante (Tabla 3.5). El tipo float requiere 4 bytes <strong>de</strong> memoria,<br />

double requiere 8 bytes y long double requiere 10 bytes (Borland C).<br />

Tabla 3.5. Tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong> coma flotante (Borland C).<br />

Tipo C Rango <strong>de</strong> valores Precisión<br />

float 3.4 X 10 ”’ ... 3.4 x 10“ 7 dígitos<br />

double 1.7 x 10 ... 1.7 x 10 15 dígitos<br />

long double 3.4 x 10 ’ ... 1.1 X 10”’ 19 dígitos<br />

Ejemplos<br />

float f; /* <strong>de</strong>finición <strong>de</strong> la variable f */<br />

f = 1.65; /* asignación a f */<br />

printf (“f:%f\n“,<br />

f) ; /* visualización <strong>de</strong> f:5.65 */<br />

double h; /* <strong>de</strong>finición <strong>de</strong> la variable <strong>de</strong> tipo double h */<br />

h = 0.0; /* asignación <strong>de</strong> 0.0 a h */<br />

3.7.3. Caracteres (char)<br />

Un carácter es cualquier elem<strong>en</strong>to <strong>de</strong> un conjunto <strong>de</strong> caracteres pre<strong>de</strong>finidos o alfabeto. La mayoría <strong>de</strong><br />

las computadoras utilizan el conjunto <strong>de</strong> caracteres ASCII.<br />

C procesa <strong>datos</strong> carácter (tales como texto) utilizando el tipo <strong>de</strong> dato char. En unión con la<br />

<strong>estructura</strong> array, que se verá posteriorm<strong>en</strong>te, se pue<strong>de</strong> utilizar para almac<strong>en</strong>ar cad<strong>en</strong>as <strong>de</strong> caracteres<br />

(grupos <strong>de</strong> caracteres). Se pue<strong>de</strong> <strong>de</strong>finir una variable carácter escribi<strong>en</strong>do:<br />

char dato-car;<br />

char letra = ‘A‘;<br />

char respuesta = ‘SI;<br />

Internam<strong>en</strong>te, los caracteres se almac<strong>en</strong>an como números. La letra A, por ejemplo, se almac<strong>en</strong>a<br />

internam<strong>en</strong>te como el número 65, la letra B es 66, la letra c es 67, etc. El tipo char repres<strong>en</strong>ta valores<br />

<strong>en</strong> el rango -128 a +I27 y se asocian con el código ASCII.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 95<br />

1<br />

I<br />

I<br />

Dado que el tipo char almac<strong>en</strong>a valores <strong>en</strong> el rango <strong>de</strong> -128 a +127, C proporciona el tipo<br />

unsigned char para repres<strong>en</strong>tar valores <strong>de</strong> O a 255 y así repres<strong>en</strong>tar todos los caracteres ASCII.<br />

Puesto que los caracteres se almac<strong>en</strong>an internam<strong>en</strong>te como números, se pued<strong>en</strong> realizar operaciones<br />

aritméticas con <strong>datos</strong> tipo char. Por ejemplo, se pue<strong>de</strong> convertir una letra minúscula a a una letra<br />

mayúscula A, restando 32 <strong>de</strong>l código ASCII. Las s<strong>en</strong>t<strong>en</strong>cias para realizar la conversión:<br />

char car-uno = 'a';<br />

car-uno = car-uno - 32;<br />

Esto convierte a (código ASCII 97) a A (código ASCII 65). De modo similar, añadi<strong>en</strong>do 32 convierte<br />

el carácter <strong>de</strong> letra mayúscula a minúscula:<br />

car-uno = car-uno + 32;<br />

Como los tipos char son subconjuntos <strong>de</strong> los tipos <strong>en</strong>teros, se pue<strong>de</strong> asignar un tipo char a un <strong>en</strong>tero.<br />

Por ejemplo,<br />

int suma = O;<br />

char valor;<br />

...<br />

scanf ("%c",&valor); /* €unción estándar <strong>de</strong> <strong>en</strong>trada */<br />

suma = suma + valor; /* operador ... */<br />

Exist<strong>en</strong> caracteres que ti<strong>en</strong><strong>en</strong> un propósito especial y no se pued<strong>en</strong> escribir utilizando el método<br />

normal. C proporciona secu<strong>en</strong>cias <strong>de</strong> escape. Por ejemplo, el literal carácter <strong>de</strong> un apóstrofe se pue<strong>de</strong><br />

escribir como<br />

'\"<br />

y el carácter nueva línea<br />

\n<br />

La Tabla 3.7 <strong>en</strong>umera las difer<strong>en</strong>tes secu<strong>en</strong>cias <strong>de</strong> escape <strong>de</strong> C.<br />

3.8. EL TIPO DE DATO LÓGICO<br />

Los compiladores <strong>de</strong> C que sigu<strong>en</strong> la norma ANSI no incorporan el tipo <strong>de</strong> dato lógico cuyos valores<br />

son «verda<strong>de</strong>ro» (true) y «falso» (fialse). El l<strong>en</strong>guaje C simula este tipo <strong>de</strong> dato tan importante <strong>en</strong> la<br />

<strong>estructura</strong>s <strong>de</strong> control ( if , while. .). Para ello utiliza el tipo <strong>de</strong> dato int . C interpreta todo valor<br />

distinto <strong>de</strong> O como «verda<strong>de</strong>ro» y el valor O como «falso». De esta forma se pued<strong>en</strong> escribir expresiones<br />

lógicas <strong>de</strong> igual forma que <strong>en</strong> otros l<strong>en</strong>guajes <strong>de</strong> programación se utiliza true y false. Una expresión<br />

lógica que se evalúa a «O» se consi<strong>de</strong>ra falsa; una expresión lógica que se evalúa a 1 (o valor <strong>en</strong>tero<br />

distinto <strong>de</strong> O) se consi<strong>de</strong>ra verda<strong>de</strong>ra.<br />

Ejemplo<br />

int bisiesto;<br />

bisiesto = 1;<br />

int <strong>en</strong>contrado, ban<strong>de</strong>ra;<br />

Dadas estas <strong>de</strong>claraciones, las sigui<strong>en</strong>tes s<strong>en</strong>t<strong>en</strong>cias son todas válidas<br />

if (<strong>en</strong>contrado) . .. (* s<strong>en</strong>tcncia <strong>de</strong> selección */


96 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

indicador = O; /* indicador toma el valor falso */<br />

indicador = suma > 10; /* indicador toma el valor l(true) si suma es<br />

mayor que 10, <strong>en</strong> caso contrario, O */<br />

Valor distinto <strong>de</strong> cero repres<strong>en</strong>tatrue (verda<strong>de</strong>ro)<br />

O<br />

repres<strong>en</strong>ta false (falso)<br />

En C, se pue<strong>de</strong> <strong>de</strong>finir un tipo que asocia valores <strong>en</strong>teros constantes con id<strong>en</strong>tificadores, es el tipo<br />

<strong>en</strong>umerado. Para repres<strong>en</strong>tar los <strong>datos</strong> lógicos <strong>en</strong> C, el sistema usual es <strong>de</strong>finir un tipo <strong>en</strong>umerado<br />

Boolean con dos id<strong>en</strong>tificadores false (valor O) y true (valor I) <strong>de</strong> la forma sigui<strong>en</strong>te:<br />

<strong>en</strong>um Boolean { FALSE, TRUE };<br />

Esta <strong>de</strong>claración hace a Boolean un tipo <strong>de</strong>finido por el usuario con literales o id<strong>en</strong>tificadores<br />

(valores constantes) TRUE y FALSE.<br />

*<br />

Ejercicio 3.1<br />

Si <strong>de</strong>sea simular el tipo lógico pero al estilo <strong>de</strong> tipo incorporado propio, se podría conseguir<br />

construy<strong>en</strong>do un archí vo. h (boo1 ean) con constantes con nombre TRUE y FALSE, tal como<br />

/* archivo: boo1ean.h */<br />

#ifn<strong>de</strong>f BOOLEAN-H<br />

#<strong>de</strong>fine BOOLEAN-H<br />

type<strong>de</strong>f int Boolean;<br />

const int TRUE = 1;<br />

const int FALSE = O;<br />

#<strong>en</strong>dif /* BOOLEAN-H */<br />

Entonces, basta con incluir el archivo "boolean. h" y utilizar Boolean como si fuera un tipo <strong>de</strong><br />

dato incorporado con los literales TRUE y FALSE como literales lógicos o booleanos.<br />

Si <strong>de</strong>sea utilizar las letras minúsculas para <strong>de</strong>finir boolean, true y false , se pue<strong>de</strong> utilizar esta<br />

versión <strong>de</strong>l archivo <strong>de</strong> cabecera boolean. h.<br />

/* archivo: boo1ean.h */<br />

#ifn<strong>de</strong>f BOOLEAN-H<br />

#<strong>de</strong>fine BOOLEAN-H<br />

type<strong>de</strong>f int boolean;<br />

const int true = 1;<br />

const int false = O;<br />

#<strong>en</strong>dif /* BOLEAN-H */<br />

3.8.1. Escritura <strong>de</strong> valores lógicos<br />

La mayoría <strong>de</strong> las expresiones lógicas aparec<strong>en</strong> <strong>en</strong> <strong>estructura</strong>s <strong>de</strong> control que sirv<strong>en</strong> para <strong>de</strong>terminar la<br />

secu<strong>en</strong>cia <strong>en</strong> que se ejecutan las s<strong>en</strong>t<strong>en</strong>cias C. Raram<strong>en</strong>te se ti<strong>en</strong>e la necesidad <strong>de</strong> leer valores lógicos<br />

como dato <strong>de</strong> <strong>en</strong>trada o <strong>de</strong> visualizar valores lógicos como resultados <strong>de</strong> programa. Si es necesario,<br />

se pue<strong>de</strong> visualizar el valor <strong>de</strong> la variable lógica utilizando la función para salida print f ( ) . Así, si<br />

<strong>en</strong>contrado es false, la s<strong>en</strong>t<strong>en</strong>cia<br />

visualizará<br />

printf("E1 valor <strong>de</strong> <strong>en</strong>contrado es %d\n",<strong>en</strong>contrado);<br />

El valor <strong>de</strong> <strong>en</strong>contrado es O


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 97<br />

3.9. CONSTANTES<br />

I<br />

En C exist<strong>en</strong> cuatro tipos <strong>de</strong> constantes:<br />

o constantes literales,<br />

o constantes <strong>de</strong>finidas,<br />

o constantes <strong>en</strong>umeradas,<br />

o constantes <strong>de</strong>claradas.<br />

\<br />

.<br />

Las constantes literales son las más usuales; toman valores tales como 4 5 .3 2 5 6 4, 2 2 2 o bi<strong>en</strong><br />

"Introduzca sus <strong>datos</strong>" que se escrib<strong>en</strong> directam<strong>en</strong>te <strong>en</strong> el texto <strong>de</strong>l programa. Las constantes<br />

<strong>de</strong>finidas son id<strong>en</strong>tificadores que se asocian con valores literales constantes y que toman <strong>de</strong>terminados<br />

nombres. Las constantes <strong>de</strong>claradas son como variables: sus valores se almac<strong>en</strong>an <strong>en</strong> memoria, pero no<br />

se pued<strong>en</strong> modificar. Las constantes <strong>en</strong>umeradas permit<strong>en</strong> asociar un id<strong>en</strong>tificador, tal como Color,<br />

con una secu<strong>en</strong>cia <strong>de</strong> otros nombres, tales como Azul, Ver<strong>de</strong>, R ojo y Amari 1 lo.<br />

3.9.1. Constantes literales<br />

Las constantes literales o constantes, <strong>en</strong> g<strong>en</strong>eral, se clasifican también <strong>en</strong> cuatro grupos, cada uno <strong>de</strong> los<br />

cuales pue<strong>de</strong> ser <strong>de</strong> cualquiera <strong>de</strong> los tipos:<br />

o constantes <strong>en</strong>teras,<br />

0 constantes caracteres,<br />

0 constantes <strong>de</strong> coma flotante,<br />

o constantes <strong>de</strong> cad<strong>en</strong>a.<br />

Constantes <strong>en</strong>teras<br />

La escritura <strong>de</strong> constantes <strong>en</strong>teras requiere seguir unas <strong>de</strong>terminadas reglas:<br />

0 No utilizar nunca comas ni otros signos <strong>de</strong> puntuación <strong>en</strong> números <strong>en</strong>teros o completos.<br />

123456 <strong>en</strong> lugar <strong>de</strong> 123.456<br />

0 Para forzar un valor al tipo long, terminar con una letra L o 1. Por ejemplo,<br />

1 O 2 4 es un tipo <strong>en</strong>tero 1 O 2 4 I, es un tipo largo ( long )<br />

o Para forzar un valor al tipo unsigned, terminarlo con una letra mayúscula u. Por ejemplo, 4 3 5 2U.<br />

0 Para repres<strong>en</strong>tar un <strong>en</strong>tero <strong>en</strong> octal (base 8), éste <strong>de</strong>be <strong>de</strong> estar precedido <strong>de</strong> O.<br />

Formato <strong>de</strong>cimal 123<br />

Formato octal O 77'7 (están precedidas <strong>de</strong> la cifra O)<br />

o Para repres<strong>en</strong>tar un <strong>en</strong>tero <strong>en</strong> hexa<strong>de</strong>cimal (base 16), este <strong>de</strong>be <strong>de</strong> estar precedido <strong>de</strong> Ox.<br />

Formato hexa<strong>de</strong>cimal OXFF3A (están precedidas <strong>de</strong> "ox" o bi<strong>en</strong> "ox")<br />

Se pued<strong>en</strong> combinar sufijos L ( 1 ) , que significa long (largo), o bi<strong>en</strong> u (u), que significa<br />

unsigned (sin signo).<br />

3456UL<br />

Constantes reales<br />

Una constante flotante repres<strong>en</strong>ta un número real; siempre ti<strong>en</strong><strong>en</strong> signo y repres<strong>en</strong>tan aproximaciones<br />

<strong>en</strong> lugar <strong>de</strong> valores exactos.


98 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

82.347 .63 83. 47e-4 1.25E7 6l.e+4<br />

La notación ci<strong>en</strong>tífica se repres<strong>en</strong>ta con un expon<strong>en</strong>te positivo o negativo.<br />

2.5E4 equivale u<br />

5.435E-3<br />

equivale a<br />

Exist<strong>en</strong> tres tipos <strong>de</strong> constantes:<br />

float<br />

double<br />

long double<br />

4 bytes<br />

8 bytes<br />

10 bytes<br />

25000<br />

O. 005435<br />

Constantes carácter<br />

Una constante carácter (char) es un carácter <strong>de</strong>l código ASCII <strong>en</strong>cerrado <strong>en</strong>tre apóstrofes.<br />

'A' 'b' 'C'<br />

A<strong>de</strong>más <strong>de</strong> los caracteres ASCII estándar, una constante carácter soporta caracteres especiales que<br />

no se pued<strong>en</strong> repres<strong>en</strong>tar utilizando su teclado, como, por ejemplo, los códigos ASCII altos y las<br />

secu<strong>en</strong>cias <strong>de</strong> escape. (El Apéndice B recoge un listado <strong>de</strong> todos los caracteres ASCII.)<br />

Así, por ejemplo, el carácter sigma (C) -código ASCII 228, hex E4- se repres<strong>en</strong>ta mediante el<br />

prefijo \x y el número hexa<strong>de</strong>cimal <strong>de</strong>l código ASCII. Por ejemplo,<br />

char sigma = '\xE4';<br />

Este método se utiliza para almac<strong>en</strong>ar o imprimir cualquier carácter <strong>de</strong> la tabla ASCII por su número<br />

hexa<strong>de</strong>cimal. En el ejemplo anterior, la variable sigma no conti<strong>en</strong>e cuatro caracteres sino únicam<strong>en</strong>te el<br />

símbolo sigma.<br />

Tabla 3.6. Caracteres secu<strong>en</strong>cias (códigos) <strong>de</strong> escape.<br />

Código <strong>de</strong> escape Significado Códigos ASCII<br />

Dec<br />

Hex<br />

'\n'<br />

'\r'<br />

'\t'<br />

' \VI<br />

'\a'<br />

nueva línea<br />

retorno <strong>de</strong> carro<br />

tabulación<br />

tabulación vertical<br />

alerta (pitido sonoro)<br />

13 10<br />

13<br />

9<br />

I 1<br />

7<br />

OD OA<br />

OD<br />

09<br />

OB<br />

07<br />

'\b'<br />

retroceso <strong>de</strong> espacio<br />

8<br />

O8<br />

'\f$<br />

avance <strong>de</strong> página<br />

12 oc<br />

barra inclinada inversa<br />

92<br />

5c<br />

' \ \ I<br />

'\"<br />

I \I1 I<br />

I\?'<br />

comilla simple<br />

doble comilla<br />

signo <strong>de</strong> interrogación<br />

39<br />

34<br />

63<br />

27<br />

22<br />

3F<br />

'\000'<br />

número octal<br />

Todos<br />

Todos<br />

I \xhh' número hexa<strong>de</strong>cimal Todos Todos<br />

Un carácter que se lee utilizando una barra oblicua (\) se llama secu<strong>en</strong>cia o código <strong>de</strong> escape. La<br />

Tabla 3.6 muestra difer<strong>en</strong>tes secu<strong>en</strong>cias <strong>de</strong> escape y su significado.


I<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 99 l<br />

/* Programa: Pruebas códigos <strong>de</strong> escape *I<br />

#inclu<strong>de</strong> <br />

int main0<br />

1<br />

char alarma = '\a'; /* alarma */<br />

char bs = '\b'; /* retroceso <strong>de</strong> espacio */<br />

printf ( "%c %c" , alarma, bs) ;<br />

return O;<br />

Aritmética con caracteres C<br />

Dada la correspond<strong>en</strong>cia <strong>en</strong>tre un carácter y su código ASCII, es posible realizar operaciones aritméticas<br />

sobre <strong>datos</strong> <strong>de</strong> caracteres. Observe el sigui<strong>en</strong>te segm<strong>en</strong>to <strong>de</strong> código:<br />

char c;<br />

c = 'T' t 5; /* suma 5 al carácter ASCII */<br />

Realm<strong>en</strong>te lo que suce<strong>de</strong> es almac<strong>en</strong>ar Y <strong>en</strong> c. El valor ASCII <strong>de</strong> la letra T es 84, y al sumarle 5<br />

produce 89, que es el código <strong>de</strong> la letra Y. A la inversa, se pued<strong>en</strong> almac<strong>en</strong>ar constantes <strong>de</strong> carácter <strong>en</strong><br />

variables <strong>en</strong>teras. Así,<br />

int j = 'p'<br />

No pone una letra p <strong>en</strong> j , sino que asigna el valor 80 -ódigo ASCII <strong>de</strong> p- a la variable j .<br />

Observar este pequeño segm<strong>en</strong>to <strong>de</strong> código:<br />

int m;<br />

m = m + 'aI-'A';<br />

Está convirti<strong>en</strong>do una letra mayúscula <strong>en</strong> su correspondi<strong>en</strong>te minúscula. Para lo cual suma el<br />

<strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> las letras mayúsculas a las minúsculas ( ' a ' - ' A ' ) .<br />

I<br />

Constantes cad<strong>en</strong>a<br />

Una constante cad<strong>en</strong>a (también llamada literal cad<strong>en</strong>a o simplem<strong>en</strong>te cad<strong>en</strong>a) es una secu<strong>en</strong>cia <strong>de</strong><br />

caracteres <strong>en</strong>cerrados <strong>en</strong>tre dobles comillas. Algunos ejemplos <strong>de</strong> constantes <strong>de</strong> cad<strong>en</strong>a son:<br />

"123"<br />

"12 <strong>de</strong> octubre 1492"<br />

"esto es una cad<strong>en</strong>a"<br />

Se pue<strong>de</strong> escribir una cad<strong>en</strong>a <strong>en</strong> varias líneas, terminando cada línea con ''Y'<br />

"esto es una cad<strong>en</strong>a\<br />

que ti<strong>en</strong>e dos lineas"<br />

Se pue<strong>de</strong> concat<strong>en</strong>ar cad<strong>en</strong>as, escribi<strong>en</strong>do<br />

que equivale a<br />

' ABCDEFGHI JKL"


100 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En memoria, las cad<strong>en</strong>as se repres<strong>en</strong>tan por una serie <strong>de</strong> caracteres ASCII más un O o nulo. El<br />

carácter nulo marca el final <strong>de</strong> la cad<strong>en</strong>a y se inserta automáticam<strong>en</strong>te por el compilador C al final <strong>de</strong><br />

las constantes <strong>de</strong> cad<strong>en</strong>as. Para repres<strong>en</strong>tar valores nulos, C <strong>de</strong>fine el símbolo NULL como una constante<br />

<strong>en</strong> diversos archivos <strong>de</strong> cabecera (normalm<strong>en</strong>te STDEF . H, STDIO . H, STDLIB . H y STRING. H). Para<br />

utilizar NULL <strong>en</strong> un programa, incluya uno o más <strong>de</strong> estos archivos <strong>en</strong> lugar <strong>de</strong> <strong>de</strong>finir NULL con una<br />

línea tal como<br />

#<strong>de</strong>fine NULL O<br />

Recuer<strong>de</strong> que una constante <strong>de</strong> caracteres se <strong>en</strong>cierra <strong>en</strong>tre comillas simples (apóstrofe), y las<br />

constantes <strong>de</strong> cad<strong>en</strong>a <strong>en</strong>cierran caracteres <strong>en</strong>tre dobles comillas. Por ejemplo,<br />

'Z' "z"<br />

El primer z es una constante carácter simple con una longitud <strong>de</strong> I, y el segundo "z" es una<br />

constante <strong>de</strong> cad<strong>en</strong>a <strong>de</strong> caracteres también con la longitud 1. La difer<strong>en</strong>cia es que la constante <strong>de</strong> cad<strong>en</strong>a<br />

incluye un cero (nulo) al final <strong>de</strong> la cad<strong>en</strong>a, ya que C necesita conocer dón<strong>de</strong> termina la cad<strong>en</strong>a, y la<br />

constante carácter no incluye el nulo ya que se almac<strong>en</strong>a como un <strong>en</strong>tero. Por consigui<strong>en</strong>te, no pue<strong>de</strong><br />

mezclar constantes caracteres y cad<strong>en</strong>as <strong>de</strong> caracteres <strong>en</strong> su programa.<br />

!<br />

I<br />

3.9.2. Constantes <strong>de</strong>finidas (simbólicas)<br />

Las constantes pued<strong>en</strong> recibir nombres simbólicos mediante la directiva #<strong>de</strong>fine.<br />

#<strong>de</strong>fine NUEVALINEA \n<br />

#<strong>de</strong>fine PI 3.141592<br />

#<strong>de</strong>fine VALOR 54<br />

C sustituye los valores \n, 3.141592 y 54 cuando se <strong>en</strong>cu<strong>en</strong>tra las constantes simbólicas<br />

NUEVALINEA, PI y VALOR. Las líneas anteriores no son s<strong>en</strong>t<strong>en</strong>cias y, por ello, no terminan <strong>en</strong> punto<br />

y coma.<br />

printf ("El valor es %dNUEVALINEA", VALOR) ;<br />

Escribe <strong>en</strong> pantalla la constante VALOR. Realm<strong>en</strong>te, el compilador lo que hace es sustituir <strong>en</strong> el<br />

progama todas las ocurr<strong>en</strong>cias <strong>de</strong> VALOR por 54, antes <strong>de</strong> analizar sintácticam<strong>en</strong>te el programa fu<strong>en</strong>te.<br />

3.9.3. Constantes <strong>en</strong>umeradas<br />

Las constantes <strong>en</strong>umeradas permit<strong>en</strong> crear listas <strong>de</strong> elem<strong>en</strong>tos afines. Un ejemplo típico es una constante<br />

<strong>en</strong>umerada <strong>de</strong> lista <strong>de</strong> colores, que se pue<strong>de</strong> <strong>de</strong>clarar como:<br />

<strong>en</strong>um Colores {Rojo, Naranja, Amarillo, Ver<strong>de</strong>, Azul, Violeta};<br />

Cuando se procesa esta s<strong>en</strong>t<strong>en</strong>cia, el compilador asigna un valor que comi<strong>en</strong>za <strong>en</strong> O a cada elem<strong>en</strong>to<br />

<strong>en</strong>umerado; así, Ro j o equivale a O, Naranja es 1, etc. El compilador <strong>en</strong>umera los id<strong>en</strong>tificadores por<br />

usted. Después <strong>de</strong> <strong>de</strong>clarar un tipo <strong>de</strong> dato <strong>en</strong>umerado, se pued<strong>en</strong> crear variables <strong>de</strong> ese tipo, como con<br />

cualquier otro tipo <strong>de</strong> <strong>datos</strong>. Así, por ejemplo, se pue<strong>de</strong> <strong>de</strong>finir una variable <strong>de</strong> tipo <strong>en</strong>um colores.<br />

<strong>en</strong>um Colores Colorfavorito = Ver<strong>de</strong>;<br />

Otro ejemplo pue<strong>de</strong> ser:<br />

<strong>en</strong>um Boolean { False, True 1;<br />

que asignará al elem<strong>en</strong>to False el valor O y a True el valor 1.


- -<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 101<br />

Para crear una variable <strong>de</strong> tipo lógico <strong>de</strong>clarar:<br />

<strong>en</strong>um Boolean Interruptor = True;<br />

Es posible asignar valores distintos <strong>de</strong> los que les correspon<strong>de</strong> <strong>en</strong> su secu<strong>en</strong>cia natural<br />

<strong>en</strong>um LucesTrafico {Ver<strong>de</strong>, Amarillo = 10, Rojo};<br />

Al procesar esta s<strong>en</strong>t<strong>en</strong>cia, el compilador asigna el valor O al id<strong>en</strong>tificador ver<strong>de</strong>, 1 O al<br />

id<strong>en</strong>tificadorharillo y 11 aRojo.<br />

3.9.4. Constantes <strong>de</strong>claradas const y volatile<br />

El cualificador const permite dar nombres simbólicos a constantes a modo <strong>de</strong> otros l<strong>en</strong>guajes, como<br />

Pascal. El formato g<strong>en</strong>eral para crear una constante es:<br />

CQXIS~ tipo nombre = valor;<br />

Si se omite tipo, C utiliza int (<strong>en</strong>tero por <strong>de</strong>fecto)<br />

const int Meses=12;<br />

/* Meses es constante simbólica <strong>de</strong><br />

const char CARACTER='@';<br />

const int OCTALz0233;<br />

const char CADENA [ ] ="Curso <strong>de</strong> C" ;<br />

C soporta el calificador <strong>de</strong> tipo variable const. Especifica que el valor <strong>de</strong> una variable no se pue<strong>de</strong><br />

modificar durante el programa. Cualquier int<strong>en</strong>to <strong>de</strong> modificar el valor <strong>de</strong> la variable <strong>de</strong>finida con<br />

const producirá un m<strong>en</strong>saje <strong>de</strong> error.<br />

const int semana = 7;<br />

const char CADENA []= "Borland C 3.013.1 Guía <strong>de</strong> refer<strong>en</strong>cia";<br />

La palabra reservada voiat iie actúa como const, pero su valor pue<strong>de</strong> ser modificado no sólo por<br />

el propio programa, sino también por el hardware o por el software <strong>de</strong>l sistema. Las variables volátiles,<br />

sin embargo, no se pued<strong>en</strong> guardar <strong>en</strong> registros, como es el caso <strong>de</strong> las variables normales.<br />

Difer<strong>en</strong>cias <strong>en</strong>tre const y #<strong>de</strong>fine<br />

Las <strong>de</strong>finiciones const especifican tipos <strong>de</strong> <strong>datos</strong>, terminan con puntos y coma y se inicializan como<br />

las variables. La directiva #<strong>de</strong>fine no especifica tipos <strong>de</strong> <strong>datos</strong>, no utilizan el operador <strong>de</strong> asignación<br />

(=) y no termina con punto y coma.<br />

V<strong>en</strong>tajas <strong>de</strong> const sobre #<strong>de</strong>fine<br />

En C casi siempre es recom<strong>en</strong>dable el uso <strong>de</strong> const <strong>en</strong> lugar <strong>de</strong> #<strong>de</strong>fine. A<strong>de</strong>más <strong>de</strong> las v<strong>en</strong>tajas ya<br />

<strong>en</strong>unciadas se pued<strong>en</strong> consi<strong>de</strong>rar otras:<br />

0 El compilador, normalm<strong>en</strong>te, g<strong>en</strong>era código más efici<strong>en</strong>te con constantes const .<br />

0 Como las <strong>de</strong>finiciones especifican tipos <strong>de</strong> <strong>datos</strong>, el compilador pue<strong>de</strong> comprobar inmediatam<strong>en</strong>te<br />

si las constantes literales <strong>en</strong> las <strong>de</strong>finiciones <strong>de</strong> const están <strong>en</strong> forma correcta. Con #<strong>de</strong>fine el<br />

compilador no pue<strong>de</strong> realizar pruebas similares hasta que una s<strong>en</strong>t<strong>en</strong>cia utiliza el id<strong>en</strong>tificador<br />

constante, por lo que se hace más difícil la <strong>de</strong>tección <strong>de</strong> errores.


102 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Desv<strong>en</strong>taja <strong>de</strong> const sobre #<strong>de</strong>fine<br />

Los valores <strong>de</strong> los símbolos <strong>de</strong> const ocupan espacio <strong>de</strong> <strong>datos</strong> <strong>en</strong> tiempo <strong>de</strong> ejecución, mi<strong>en</strong>tras que<br />

#<strong>de</strong>fine sólo existe <strong>en</strong> el texto <strong>de</strong>l programa y su valor se inserta directam<strong>en</strong>te <strong>en</strong> el código compilado.<br />

Los valores const no se pued<strong>en</strong> utilizar don<strong>de</strong> el compilador espera un valor constante, por ejemplo<br />

<strong>en</strong> la <strong>de</strong>finición <strong>de</strong> un array. Por esta razón, algunos programadores <strong>de</strong> C sigu<strong>en</strong> utilizando #<strong>de</strong>fine <strong>en</strong><br />

lugar <strong>de</strong> const.<br />

Sintaxis <strong>de</strong> const<br />

cons t tipoDato nombreconstan t e = val orCons tan t e;<br />

const unsigned DiasDeSemana = 7;<br />

const HorasDelDia = 24;<br />

3.10. VARIABLES<br />

En C una variable es una posición con nombre <strong>en</strong> memoria don<strong>de</strong> se almac<strong>en</strong>a un valor <strong>de</strong> un cierto tipo<br />

<strong>de</strong> dato. Las variables pued<strong>en</strong> almac<strong>en</strong>ar todo tipo <strong>de</strong> <strong>datos</strong>: cad<strong>en</strong>as, números y <strong>estructura</strong>s. Una<br />

constante, por el contrario, es una variable cuyo valor no pue<strong>de</strong> ser modificado.<br />

Una variable típicam<strong>en</strong>te ti<strong>en</strong>e un nombre (un id<strong>en</strong>tificador) que <strong>de</strong>scribe su propósito. Toda variable<br />

utilizada <strong>en</strong> un programa <strong>de</strong>be ser <strong>de</strong>clarada previam<strong>en</strong>te. La <strong>de</strong>finición <strong>en</strong> C <strong>de</strong>be situarse al principio<br />

<strong>de</strong>l bloque, antes <strong>de</strong> toda s<strong>en</strong>t<strong>en</strong>cia ejecutable. Una <strong>de</strong>finición reserva un espacio <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong><br />

memoria. El procedimi<strong>en</strong>to para <strong>de</strong>finir (crear) una variable es escribir el tipo <strong>de</strong> dato, el id<strong>en</strong>tificador<br />

o nombre <strong>de</strong> la variable y, <strong>en</strong> ocasiones, el valor inicial que tomará. Por ejemplo,<br />

char Respuesta;<br />

significa que se reserva espacio <strong>en</strong> memoria para Respuesta, <strong>en</strong> este caso, un carácter ocupa un solo<br />

byte.<br />

El nombre <strong>de</strong> una variable ha <strong>de</strong> ser un id<strong>en</strong>tificador válido. Es frecu<strong>en</strong>te, <strong>en</strong> la actualidad, utilizar<br />

subrayados <strong>en</strong> los nombres, bi<strong>en</strong> al principio o <strong>en</strong> su interior, con objeto <strong>de</strong> obt<strong>en</strong>er mayor legibilidad<br />

y una correspond<strong>en</strong>cia mayor con el elem<strong>en</strong>to <strong>de</strong>l mundo real que repres<strong>en</strong>ta.<br />

salario dias-<strong>de</strong>-semana edad-alumno - fax<br />

3.10.1. Declaración<br />

Una <strong>de</strong>claración <strong>de</strong> una variable es una s<strong>en</strong>t<strong>en</strong>cia que proporciona información <strong>de</strong> la variable al<br />

cornpilador C. Su sintaxis es:<br />

tipo variable<br />

Ejemplo<br />

tipo es el nombre <strong>de</strong> un tipo <strong>de</strong> dato conocido por el C<br />

variable es un id<strong>en</strong>tificador (nombre) válido <strong>en</strong> C.<br />

long dNumero;<br />

double HorasAcumuladas;<br />

float HorasPorSemana;<br />

float NotaMedia;<br />

short Diasemana;


~~ ~<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 103<br />

Es preciso <strong>de</strong>clarar las variables antes <strong>de</strong> utilizarlas. Se pue<strong>de</strong> <strong>de</strong>clarar una variable al principio <strong>de</strong><br />

un archivo o bloque <strong>de</strong> código al principio <strong>de</strong> una función.<br />

#inclu<strong>de</strong> /* variable al principio <strong>de</strong>l archivo */<br />

int MiNumero;<br />

int main()<br />

i<br />

printf (''¿Cuál es su número favorito?') ;<br />

scanf ("%d", &MiNumero);<br />

return O;<br />

}<br />

/*Variable al principio <strong>de</strong> una función .<br />

Al principio <strong>de</strong> la función main()*/<br />

...<br />

int main()<br />

{<br />

int i;<br />

int j;<br />

/*Variable al principio <strong>de</strong> un bloque.<br />

Al principio <strong>de</strong> un bloque for*/<br />

...<br />

int main()<br />

{<br />

int i;<br />

}<br />

t<br />

double suma;<br />

...<br />

1<br />

...<br />

En C las <strong>de</strong>claraciones se han <strong>de</strong> situar siempre al principio <strong>de</strong>l bloque. Su ámbito es el bloque <strong>en</strong><br />

el que están <strong>de</strong>claradas.<br />

Ejemplo<br />

/* Distancia a la luna <strong>en</strong> kilometros */<br />

#inclu<strong>de</strong> <br />

int main ( )


104 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

{<br />

const int luna=238857; /* Distancia <strong>en</strong> millas */<br />

float luna-kilo;<br />

printf("Distancia a la Luna %d millac\n",luna);<br />

luna-kilo = luna"1.609; /* una m illa = 1.609 kilómetros */<br />

printf("En kilómetros es %fKm.\n",luna-kilo);<br />

return O;<br />

Ejemplo 3.3<br />

Este ejemplo muestra cómo una variable pue<strong>de</strong> ser <strong>de</strong>clarada al inicio <strong>de</strong> cualquier bloque <strong>de</strong> un<br />

programa C.<br />

#inclu<strong>de</strong> <br />

/* Difer<strong>en</strong>tes <strong>de</strong>claraciones */<br />

int main( )<br />

t<br />

int x, yl; /* <strong>de</strong>clarar a las variables x e y1 <strong>en</strong> la función main0 */<br />

x = 75;<br />

y1 = 89;<br />

if (x > 10)<br />

i<br />

int y2 = 50; /* <strong>de</strong>clarar e inicializa a la variable y2 <strong>en</strong> el<br />

bloque if */<br />

y1 = yl+y2;<br />

1<br />

printf("x = %d, y1 = %d\n",x,yl);<br />

return O;<br />

3.10.2. Inicialización <strong>de</strong> variables<br />

En algunos programas anteriores, se ha proporcionado un valor d<strong>en</strong>ominado valor inicial, a una variable<br />

cuando se <strong>de</strong>clara. El formato g<strong>en</strong>eral <strong>de</strong> una <strong>de</strong>claración <strong>de</strong> inicialización es:<br />

tipo nombre-varíabl e = expresión<br />

expresión es cualquier expresión válida cuyo valor es <strong>de</strong>l mismo tipo que tipo.<br />

ia <strong>de</strong>clara y proporciona un valor inicial a una variable.<br />

I<br />

Las variables se pued<strong>en</strong> inicializar a la vez que se <strong>de</strong>claran, o bi<strong>en</strong>, inicializarse <strong>de</strong>spués <strong>de</strong> la<br />

<strong>de</strong>claración. El primer método es probablem<strong>en</strong>te el mejor <strong>en</strong> la mayoría <strong>de</strong> los casos, ya que combina<br />

la <strong>de</strong>finición <strong>de</strong> la variable con la asignación <strong>de</strong> su valor inicial.<br />

char respuesta = 'S';<br />

int contador = 1;<br />

float peso = 156.45;<br />

int anyo = 1993;<br />

Estas acciones crean variables respuesta, contador, peso y anyo, que almac<strong>en</strong>an <strong>en</strong><br />

memoria los valores respectivos situados a sy <strong>de</strong>recha.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 105<br />

El segundo método consiste <strong>en</strong> utilizar s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> asignación difer<strong>en</strong>tes <strong>de</strong>spués <strong>de</strong> <strong>de</strong>finir la<br />

variable, como <strong>en</strong> el sigui<strong>en</strong>te caso:<br />

char barra;<br />

barra = ' / I ;<br />

3.10.3. Declaración o <strong>de</strong>finición<br />

La difer<strong>en</strong>cia <strong>en</strong>tre <strong>de</strong>claración y <strong>de</strong>finición es sutil. Una <strong>de</strong>claración introduce un ombre <strong>de</strong> una<br />

variable y asocia un tipo con la variable. Una <strong>de</strong>finición es una <strong>de</strong>claración que asigna simultáneam<strong>en</strong>te<br />

memoria a la variable.<br />

double x; /* <strong>de</strong>clara el nombre <strong>de</strong> la variable x <strong>de</strong> tipo double */<br />

char c-var; /* <strong>de</strong>clara c-var <strong>de</strong> tipo char */<br />

int i; /* <strong>de</strong>finido pero no inicializado */<br />

int i = O; /* <strong>de</strong>finido e inicializado a cero.*/<br />

3.1 1. DURACIÓN DE UNA VARIABLE<br />

Dep<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l lugar don<strong>de</strong> se <strong>de</strong>finan las variables <strong>de</strong> C, éstas se pued<strong>en</strong> utilizar <strong>en</strong> la totalidad <strong>de</strong>l<br />

programa, d<strong>en</strong>tro <strong>de</strong> una función o pued<strong>en</strong> existir sólo temporalm<strong>en</strong>te d<strong>en</strong>tro <strong>de</strong> un bloque <strong>de</strong> una<br />

función. La zona <strong>de</strong> un programa <strong>en</strong> la que una variable está activa se d<strong>en</strong>omina, normalm<strong>en</strong>te, ámbito<br />

o alcance («scope»).<br />

El ámbito (alcance) <strong>de</strong> una variable se exti<strong>en</strong><strong>de</strong> hasta los límites <strong>de</strong> la <strong>de</strong>finición <strong>de</strong> su bloque. Los<br />

tipos básicos <strong>de</strong> variables <strong>en</strong> C son:<br />

variables locales;<br />

variables globules;<br />

variables dinámicas.<br />

3.11.1. Variables locales<br />

Las variables locales son aquéllas <strong>de</strong>finidas <strong>en</strong> el interior <strong>de</strong> una función y son visibles sólo <strong>en</strong> esa<br />

función específica. Las reglas por las que se rig<strong>en</strong> las variables locales son:<br />

I. En el interior <strong>de</strong> una función, una variable local no pue<strong>de</strong> ser modificada por ninguna s<strong>en</strong>t<strong>en</strong>cia<br />

externa a la función.<br />

2. Los nombres <strong>de</strong> las variables locales no han <strong>de</strong> ser Únicos. Dos, tres o más funciones pued<strong>en</strong><br />

<strong>de</strong>finir variables <strong>de</strong> nombre Interruptor: Cada variable es distinta y pert<strong>en</strong>ece a la función <strong>en</strong> que<br />

está <strong>de</strong>clarada.<br />

3. Las variables locales <strong>de</strong> las funciones no exist<strong>en</strong> <strong>en</strong> memoria hasta que se ejecuta la función.<br />

Esta propiedad permite ahorrar memoria, ya que permite que varias funciones compartan la<br />

misma memoria para sus variables locales (pero no a la vez).<br />

Por la razón dada <strong>en</strong> el punto 3, las variables locales se llaman también automáticas o auto, ya que<br />

dichas variables se crean automáticam<strong>en</strong>te <strong>en</strong> la <strong>en</strong>trada a la función y se liberan también automáticam<strong>en</strong>te<br />

cuando se termina la e.jecución <strong>de</strong> la función.<br />

#inclu<strong>de</strong> <br />

int main()


106 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

i<br />

int a, b, c, suma, numero; /*vdriables locales */<br />

printf ( "Cuantos números a sumar: ") ;<br />

scanf ("%d", &numero);<br />

I<br />

suma = a + b + c;<br />

...<br />

return O;<br />

3.1 1.2. Variables globales<br />

Las variables globales son variables que se <strong>de</strong>claran fuera <strong>de</strong> la función y por <strong>de</strong>fecto (omisión) son<br />

visibles a cualquier función, incluy<strong>en</strong>do main ( ) .<br />

#inclu<strong>de</strong> <br />

int a, b, c; /* <strong>de</strong>claración <strong>de</strong> variables globales */<br />

int main()<br />

i<br />

int valor; /* <strong>de</strong>claración <strong>de</strong> variable local */<br />

print f ("Tres valores : 'I ) ;<br />

scanf("%d %d %d",&a,&b,&c); /* d,b,c variables globales */<br />

valor = a+b+c;<br />

Todas las variables locales <strong>de</strong>saparec<strong>en</strong> cuando termina su bloque. Una variable global es visible<br />

<strong>de</strong>s<strong>de</strong> el punto <strong>en</strong> que se <strong>de</strong>fine hasta el final <strong>de</strong>l programa (archivo fu<strong>en</strong>te).<br />

La memoria asignada a una variable global permanece asignada a través <strong>de</strong> la ejecución <strong>de</strong>l<br />

programa, tomando espacio válido según se utilice. Por esta razón, se <strong>de</strong>be evitar utilizar muchas<br />

variables globales d<strong>en</strong>tro <strong>de</strong> un programa. Otro problema que surge con variables globales es que una<br />

función pue<strong>de</strong> asignar un valor específico a una variable global. Posteriorm<strong>en</strong>te, <strong>en</strong> otra función, y por<br />

olvido, se pued<strong>en</strong> hacer cambios <strong>en</strong> la misma variable. Estos cambios dificultarán la localización <strong>de</strong><br />

errores.<br />

3.1 1.3. Variables dinámicas<br />

Las variables dinbmicns ti<strong>en</strong><strong>en</strong> características que <strong>en</strong> algunos casos son similares tanto a variables<br />

locales como a globales. AI igual que una variable local, una variable dinámica se crea y libera durante<br />

la ejecución <strong>de</strong>l programa. La dfer<strong>en</strong>ciu <strong>en</strong>tre unu vuriable local y una variable dinámica es que la<br />

variable dinámica se crea tras su petición (<strong>en</strong> vez <strong>de</strong> automáticam<strong>en</strong>te, como las variables locales), es<br />

<strong>de</strong>cir, a su voluntad, y se libera cuando ya no se necesita. AI igual que una variable global, se pued<strong>en</strong><br />

crear variables dinámicas que son accesibles <strong>de</strong>s<strong>de</strong> múltiples funciones. Las variables dinámicas se<br />

examinan <strong>en</strong> <strong>de</strong>talle <strong>en</strong> el capítulo <strong>de</strong> punteros (Capítulo 1 O).


Y<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 107<br />

En el segm<strong>en</strong>to <strong>de</strong> código C sigui<strong>en</strong>te, Q es una variable global por estar <strong>de</strong>finida fuera <strong>de</strong> las<br />

funciones y es accesible <strong>de</strong>s<strong>de</strong> todas las s<strong>en</strong>t<strong>en</strong>cias. Sin embargo, las <strong>de</strong>finiciones d<strong>en</strong>tro <strong>de</strong> main, como<br />

A, son locules a main. Por consigui<strong>en</strong>te, sólo las s<strong>en</strong>t<strong>en</strong>cias interiores a main pued<strong>en</strong> utilizar A.<br />

#inclu<strong>de</strong>


108 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

visualiza<br />

Suma = 10<br />

El número <strong>de</strong> argum<strong>en</strong>tos <strong>de</strong> print f ( ) es in<strong>de</strong>finido, por lo que se pued<strong>en</strong> trasmitir cuantos <strong>datos</strong><br />

se <strong>de</strong>see. Así, suponi<strong>en</strong>do que<br />

1=5 j = 12 c = 'A' n = 40.791512<br />

la s<strong>en</strong>t<strong>en</strong>cia<br />

printf ("%d%d %c %f", I, J ,c,n);<br />

visualizará <strong>en</strong> pantalla<br />

5 12 A 40.791512<br />

La forma g<strong>en</strong>eral que ti<strong>en</strong>e la función printf ( )<br />

printf (cad<strong>en</strong>a-<strong>de</strong>-control, datol, dato2, ... )<br />

cad<strong>en</strong>a-<strong>de</strong>-control<br />

da to1 , da t o2 . . .<br />

conti<strong>en</strong>e los tipos <strong>de</strong> los <strong>datos</strong> y forma <strong>de</strong> mostrarlos.<br />

variables, constantes, <strong>datos</strong> <strong>de</strong> salida.<br />

print f ( ) convierte, da forma <strong>de</strong> salida a los <strong>datos</strong> y los escribe <strong>en</strong> pantalla. La cad<strong>en</strong>a <strong>de</strong> control<br />

conti<strong>en</strong>e códigos <strong>de</strong> formato que se asocian uno a uno con los <strong>datos</strong>. Cada código comi<strong>en</strong>za con el<br />

carácter %, a continuación pue<strong>de</strong> especificarse el ancho mínimo <strong>de</strong>l dato y termina con el carácter <strong>de</strong><br />

conversión. Así, suponi<strong>en</strong>do que<br />

1 = 11 J = 12 c = 'A' n = 40.791512<br />

printf ("%x%3d %c %.3f",i, j,c,n);<br />

visualizará <strong>en</strong> pantalla<br />

B 12 A 40.792<br />

El primer dato es 11 <strong>en</strong> hexa<strong>de</strong>cimal ( %x ) , el segundo es el número <strong>en</strong>tero 12 <strong>en</strong> un ancho <strong>de</strong> 3,<br />

le sigue el carácter A y, por Último, el número real n redon<strong>de</strong>ado a 3 cifras <strong>de</strong>cimales ( % . 3 f ) . Un<br />

signo m<strong>en</strong>os a continuación <strong>de</strong> % indica que el dato se ajuste a la izquierda <strong>en</strong> vez <strong>de</strong>l ajuste a la <strong>de</strong>recha<br />

por <strong>de</strong>fecto.<br />

printf ("%15s","HOLA LUCAS") ;<br />

printf ("%-15s","HOLA LUCAS") ;<br />

visualizará <strong>en</strong> pantalla<br />

HOLA LUCAS<br />

HOLA LUCAS<br />

Los códigos <strong>de</strong> formato más utilizados y su significado:<br />

%d<br />

%O<br />

%X<br />

%U<br />

%C<br />

%e<br />

%f<br />

%g<br />

%S<br />

%If<br />

El dato se convierte a <strong>en</strong>tero <strong>de</strong>cimal.<br />

El dato <strong>en</strong>tero se convierte a octal.<br />

El dato <strong>en</strong>tero se convierte a hexa<strong>de</strong>cimal.<br />

El dato <strong>en</strong>tero se convierte a <strong>en</strong>tero sin signo.<br />

El dato se consi<strong>de</strong>ra <strong>de</strong> tipo carácter.<br />

El dato se consi<strong>de</strong>ra <strong>de</strong> tipo f 1 oat. Se convierte a notación ci<strong>en</strong>tífica, <strong>de</strong> la forma<br />

(-}n.mmmmmmE(+I-}dd.<br />

El dato se consi<strong>de</strong>ra <strong>de</strong> tipo float. Se convierte a notación <strong>de</strong>cimal, con parte<br />

<strong>en</strong>tera y los dígitos <strong>de</strong> precisión.<br />

El dato se consi<strong>de</strong>ra <strong>de</strong> tipo float. Se convierte según el código %e o %f<br />

<strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> cual sea la repres<strong>en</strong>tación más corta.<br />

El dato ha <strong>de</strong> ser una cad<strong>en</strong>a <strong>de</strong> caracteres.<br />

El dato se consi<strong>de</strong>ra <strong>de</strong> tipo double.


.A<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 109<br />

C utiliza secu<strong>en</strong>cias <strong>de</strong> escape para visualizar caracteres que no están repres<strong>en</strong>tados por símbolos<br />

tradicionales, tales como \a, \b, etc. Las secu<strong>en</strong>cias <strong>de</strong> escape clásicas se muestran <strong>en</strong> la Tabla 3.7.<br />

Las secu<strong>en</strong>cias <strong>de</strong> escape proporcionan flexibilidad <strong>en</strong> las aplicaciones mediante efectos especiales.<br />

printf("\n Error ~<br />

Pulsar una tecla para continuar \n");<br />

printf ("\n") ; /* salta a una nueva línea */<br />

printf('Yo estoy preocupado\n no por el \n sino por ti.\n");<br />

la última s<strong>en</strong>t<strong>en</strong>cia visualiza<br />

Yo estoy preocupado<br />

no por el<br />

sino por ti.<br />

<strong>de</strong>bido a que la secu<strong>en</strong>cia <strong>de</strong> escape '\n' significa nueva línea o salto <strong>de</strong> línea. Otros ejemplos:<br />

printf("\n Tabla <strong>de</strong> números \n"); /* uso <strong>de</strong> \n para nueva línea */<br />

printf("\nNuml\t Num2\t Num3\n"); /* uso <strong>de</strong> \t para tabulaciones */<br />

printf ("%c",'\a'); /* uso <strong>de</strong> \a para alarma sonora */<br />

<strong>en</strong> los que se utilizan los caracteres <strong>de</strong> secu<strong>en</strong>cias <strong>de</strong> escape <strong>de</strong> nueva línea ( \n ) , tabulación ( \ t ) y<br />

alarma (\a).<br />

Tabla 3.7. Caracteres secu<strong>en</strong>cias <strong>de</strong> escape.<br />

Secu<strong>en</strong>cia <strong>de</strong> escape<br />

Significado<br />

\a Alarma<br />

\b Retroceso <strong>de</strong> espacio<br />

\f Avance <strong>de</strong> página<br />

\n<br />

\r<br />

Retorno <strong>de</strong> carro y avance <strong>de</strong> línea<br />

Retorno <strong>de</strong> carro<br />

\t Tabulación<br />

\V Tabulación vertical<br />

\\ Barra inclinada<br />

\? Signo <strong>de</strong> interrogación<br />

\ " Dobles comillas<br />

\o00 Número octal<br />

\ xhh Número hexa<strong>de</strong>cimal<br />

\O Cero, nulo (ASCII O)<br />

b<br />

Ejemplo 3.4<br />

El listado SECESC. c utiliza secu<strong>en</strong>cias <strong>de</strong> escape, tales como emitir sonidos (pitidos) <strong>en</strong> el terminal<br />

dos veces y a continuación pres<strong>en</strong>tar dos retrocesos <strong>de</strong> espacios <strong>en</strong> blanco.<br />

/* Programa:SECESC.C<br />

Propósito: Mostrar funcionami<strong>en</strong>to <strong>de</strong> secu<strong>en</strong>cias <strong>de</strong> escape<br />

*/


1 10 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

char sonidos='\a'; /* secu<strong>en</strong>cia <strong>de</strong> escape alarma <strong>en</strong> sonidos */<br />

char bs='\b'; /* almac<strong>en</strong>a secu<strong>en</strong>cia escape retroceso <strong>en</strong> bs */<br />

printf("%c%c",sonidos,sonidos);/* emite el sonido dos veces */<br />

printf ("zz") ; /* imprime dos caracteres */<br />

printf ("%c%c",bs,bs) ; /* mueve el cursor al primer carácter 'Z' */<br />

i<br />

return O;<br />

3.12.2. Entrada<br />

La <strong>en</strong>trada <strong>de</strong> <strong>datos</strong> a un programa pue<strong>de</strong> t<strong>en</strong>er diversas fu<strong>en</strong>tes, teclado, archivos <strong>en</strong> disco. La <strong>en</strong>trada<br />

que consi<strong>de</strong>ramos ahora es a través <strong>de</strong>l teclado, asociado al archivo estándar <strong>de</strong> <strong>en</strong>trada stdin. La función<br />

mas utilizada, por su versatilidad, para <strong>en</strong>trada formateada es scanf ( ) .<br />

El archivo <strong>de</strong> cabecera stdio . h <strong>de</strong> la biblioteca C proporciona la <strong>de</strong>finición (el prototipo) <strong>de</strong><br />

scanf ( ) , así como <strong>de</strong> otras funciones <strong>de</strong> <strong>en</strong>trada o <strong>de</strong> salida. La forma g<strong>en</strong>eral que ti<strong>en</strong>e la función<br />

scanf ( )<br />

scanf(cad<strong>en</strong>a-<strong>de</strong>-control, varl, var2, var3, ... )<br />

cad<strong>en</strong>a-<strong>de</strong>-con trol<br />

varl, var2 .. .<br />

conti<strong>en</strong>e los tipos <strong>de</strong> los <strong>datos</strong> y si se <strong>de</strong>sea su anchura.<br />

variables <strong>de</strong>l tipo <strong>de</strong> los códigos <strong>de</strong> control.<br />

Los códigos <strong>de</strong> formato más comunes son los ya indicados <strong>en</strong> la salida. Se pued<strong>en</strong> añadir, como<br />

sufijo <strong>de</strong>l código, ciertos modificadores como I o L. El significado es «largo», aplicado a float<br />

(%If) indica tipo double, aplicado a int (%Id) indica<strong>en</strong>tero largo.<br />

int n; double x;<br />

scanf ("%d %lf",&n,&x);<br />

La <strong>en</strong>trada ti<strong>en</strong>e que ser <strong>de</strong> la forma<br />

134 -1.4E-4<br />

En este caso la función scanf ( ) <strong>de</strong>vuelve n= 13 4 x= - 1 . 4E - 4 (<strong>en</strong> doble precisión). Los<br />

argum<strong>en</strong>tos varl , var2 . . . <strong>de</strong> la función scanf ( ) se pasan por dirección o refer<strong>en</strong>cia pues van<br />

a ser modificados por la función para <strong>de</strong>volver los <strong>datos</strong>. Por ello necesitan el operador <strong>de</strong> dirección, el<br />

prefijo &. Un error frecu<strong>en</strong>te se produce al escribir, por ejemplo,<br />

double x;<br />

scanf ("%lf",x) ;<br />

<strong>en</strong> vez <strong>de</strong><br />

scanf ("%lf",&x) ;


-<br />

El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 111<br />

~~<br />

Las variables que se pasan a scanf ( 1 se transmit<strong>en</strong> por refer<strong>en</strong>cia para po<strong>de</strong>r ser modificadas<br />

y transmitir los <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada, para ello se hac<strong>en</strong> prece<strong>de</strong>r <strong>de</strong> & .<br />

Un ejemplo típico es el sigui<strong>en</strong>te:<br />

printf ("introduzca vl y v2: 'I) ;<br />

scanf("%d %f",&vl,&v2); /*lectura valores vl y v2 */<br />

printf ("Precio <strong>de</strong> v<strong>en</strong>ta al público") ;<br />

scanf ("%f",&Precio-v<strong>en</strong>ta);<br />

printf ("Base y altura: " ) ;<br />

scanf ("%f %f",&b,&h);<br />

La función scanf ( ) termina cuando ha captado tantos <strong>datos</strong> como códigos <strong>de</strong> control se han<br />

especificado, o cuando un dato no coinci<strong>de</strong> con el código <strong>de</strong> control especificado.<br />

Ejemplo 3.5<br />

2 Cuál es la salida <strong>de</strong>l sigui<strong>en</strong>te programa, si se introduc<strong>en</strong> por teclado las letras LJ?<br />

#inclu<strong>de</strong> <br />

int main()<br />

{<br />

I<br />

char primero, ultimo;<br />

printf("1ntroduzca su primera y Última inicial:");<br />

scanf ("%c%c",&primero,&ultimo);<br />

printf ("Hola,%c . %c .\n",primero,ultimo);<br />

return O<br />

3.12.3. Salida <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres<br />

Con la función printf ( ) se pue<strong>de</strong> dar salida a cualquier dato, asociándolo el código que le<br />

correspon<strong>de</strong>. En particular, para dar salida a una cad<strong>en</strong>a <strong>de</strong> caracteres se utiliza el código % s. Así,<br />

char arbol [ I = "Acebo";<br />

printf ("%s\n", arbol) ;<br />

Para salida <strong>de</strong> cad<strong>en</strong>as, la biblioteca C proporciona la función específica puts ( ) . Ti<strong>en</strong>e un solo<br />

argum<strong>en</strong>to, que es una cad<strong>en</strong>a <strong>de</strong> caracteres. Escribe la cad<strong>en</strong>a <strong>en</strong> la salida estándar (pantalla) y aña<strong>de</strong><br />

el fin <strong>de</strong> línea. Así,<br />

puts (arbol) ;<br />

muestra<strong>en</strong> pantalla lo mismo que printf ("%s\n", arbol) ;<br />

Ejemplo 3.6<br />

Cuál es la salida <strong>de</strong>l sigui<strong>en</strong>te programa .?<br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine T "Tambor <strong>de</strong> hojalata. I'<br />

int main()


112 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

i<br />

1<br />

char st[21<br />

puts (T) ;<br />

puts ("Perm<br />

puts(st) ;<br />

puts (&st [8<br />

return O;<br />

="Todo pue<strong>de</strong> hacerse. 'I<br />

so para salir <strong>en</strong> la toto.") ;<br />

);<br />

3.12.4. Entrada <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres<br />

La <strong>en</strong>trada <strong>de</strong> una cad<strong>en</strong>a <strong>de</strong> caracteres se hace con la función más g<strong>en</strong>eral scanf ( ) y el código %s.<br />

Así, por ejemplo,<br />

char nombre [ 51 I ;<br />

printf ("Nombre <strong>de</strong>l atleta: 'I) ;<br />

scanf ("%s", nombre) ;<br />

printf("Nombre introducido: %s",nombre);<br />

La <strong>en</strong>trada <strong>de</strong>l nombre podría ser<br />

Junipero Serra<br />

La salida<br />

Nombre introducido: Junipero<br />

scanf ( ) con el código %s capta palabras, el criterio <strong>de</strong> terminación es el <strong>en</strong>contrarse un blanco, o<br />

bi<strong>en</strong> fin <strong>de</strong> línea.<br />

También com<strong>en</strong>tar que nombre no ti<strong>en</strong>e que ir precedido <strong>de</strong>l operador <strong>de</strong> dirección &. En C el<br />

id<strong>en</strong>tificador <strong>de</strong> un array, nombre lo es, ti<strong>en</strong>e la dirección <strong>de</strong>l array, por lo que <strong>en</strong> scanf ( ) se transmite<br />

la dirección <strong>de</strong>l array nombre.<br />

La biblioteca <strong>de</strong> C ti<strong>en</strong>e una función específica para captar una cad<strong>en</strong>a <strong>de</strong> caracteres, la función<br />

gets ( ) . Capta <strong>de</strong>l dispositivo estándar <strong>de</strong> <strong>en</strong>trada una cad<strong>en</strong>a <strong>de</strong> caracteres, termina la captación con<br />

un retorno <strong>de</strong> carro. El sigui<strong>en</strong>te ejemplo muestra cómo captar una línea <strong>de</strong> como máximo 80 caracteres.<br />

char linea [ 81 I ;<br />

puts ("Nombre y dirección") ;<br />

gets (linea) ;<br />

La función gets ( ) ti<strong>en</strong>e un solo argum<strong>en</strong>to, una variable tipo cad<strong>en</strong>a. Capta la cad<strong>en</strong>a <strong>de</strong> <strong>en</strong>trada<br />

y la <strong>de</strong>vuelve <strong>en</strong> la variable pasada como argum<strong>en</strong>to.<br />

gets(variab1e-cad<strong>en</strong>a);<br />

Tanto con scanf ( ) como con gets ( ) , el programa inserta al final <strong>de</strong> la cad<strong>en</strong>a el carácter que<br />

indica fin <strong>de</strong> cad<strong>en</strong>a, el carácter nulo, \ O. Siempre hay que <strong>de</strong>finir las cad<strong>en</strong>as con un espacio más <strong>de</strong>l<br />

previsto como máxima longitud para el carácter fin <strong>de</strong> cad<strong>en</strong>a.


El l<strong>en</strong>guaje C: elem<strong>en</strong>tos básicos 113<br />

3.13. RESUMEN<br />

do a los compon<strong>en</strong>tes<br />

tbne un calificador<br />

te, utiliza #inclu<strong>de</strong> para<br />

<strong>de</strong>finidas <strong>en</strong> archivos <strong>de</strong><br />

ml<strong>de</strong>s/ahomados <strong>de</strong> tipos (cast) para convertir<br />

un tipo a otro. El compilador realiza automáti-<br />

3.14. EJERCICIOS<br />

3.1. ¿Cuál es la salida <strong>de</strong>l sigui<strong>en</strong>te programa'?<br />

#inclu<strong>de</strong> <br />

int main ( )<br />

{<br />

char pax[] = "Juan Sin Miedo";<br />

%s\n",pax,&pax[41) ;<br />

1<br />

41);<br />

3.2. Escribir y ejecutar un programa que imprima su<br />

nombre y direccibn.<br />

33. Escribir y ejecutar un<br />

página <strong>de</strong> texto con n<br />

linea.<br />

3.4. Depurar el programa sigui<strong>en</strong>te:<br />

#inclu<strong>de</strong> <br />

void main ( )<br />

{<br />

print f ('El l<strong>en</strong>guaje <strong>de</strong> programa-<br />

ción C")<br />

3.5. Escribir un programa que imprima la letra B<br />

con asteriscos,<br />

*****<br />

* *<br />

* *<br />

* *<br />

*****<br />

* *<br />

* *<br />

* *<br />

*****


CAPíTULO 4<br />

OPERADORES Y EXPRESIONES<br />

CONTENIDO<br />

4.1. Operadores y expresiones.<br />

4.2. Operador <strong>de</strong> asignación.<br />

4.3. Operadores aritméticos.<br />

4.4. Operadores <strong>de</strong> increm<strong>en</strong>tación<br />

y <strong>de</strong>crem<strong>en</strong>tación.<br />

4.5. Operadores relacionales.<br />

4.6. Operadores lógicos.<br />

4.7. Operadores <strong>de</strong> manipulación<br />

<strong>de</strong> bits.<br />

4.8. Operador condicional.<br />

4.9. Operador corixi..<br />

4.10. Operadores especiales,: ( 1 ,<br />

El.<br />

4.11. El operador sizeof.<br />

4.12. Conversiones <strong>de</strong> tipos.<br />

4.13. Prioridad y asociatividad.<br />

4.14. Resum<strong>en</strong>.<br />

4.15. Ejercicios.<br />

4.16. Problemas.<br />

114


INTRODUCCI~N<br />

Los programas <strong>de</strong> computadoras se apoyan es<strong>en</strong>cialm<strong>en</strong>te <strong>en</strong> la realización <strong>de</strong><br />

numerosas operaciones aritméticas y matemáticas <strong>de</strong> difer<strong>en</strong>te complejidad.<br />

Este capítulo muestra como C hace uno <strong>de</strong> los operadores y expresiones para la<br />

resolución <strong>de</strong> operaciones. Los operadores fundam<strong>en</strong>tales que se analizan <strong>en</strong> el<br />

capítulo son:<br />

0 aritméticos, lógicos y relacionales;<br />

o <strong>de</strong> manipulación <strong>de</strong> bits;<br />

o condicionales;<br />

o especiales.<br />

A<strong>de</strong>más se analizarán las conversiones <strong>de</strong> tipos <strong>de</strong> <strong>datos</strong> y las reglas que<br />

seguirá el compilador cuando concurran <strong>en</strong> una misma expresión difer<strong>en</strong>tes<br />

tipos <strong>de</strong> operadores. Estas reglas se conoc<strong>en</strong> como prioridad y asociatividad.<br />

I<br />

CONCEPTOS CLAVE<br />

Asignación.<br />

Asociatividad.<br />

Conversión explícita.<br />

Conversiones <strong>de</strong> tipos.<br />

Evaluación <strong>en</strong> cortocircuito.<br />

Expresión.<br />

Increm<strong>en</strong>taciÓn/<strong>de</strong>crem<strong>en</strong>taciÓn.<br />

Manipulación <strong>de</strong> bits.<br />

Operador.<br />

Operador sizeof.<br />

Prioridadpreced<strong>en</strong>cia.<br />

115<br />

ad


1 16 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

4.1. OPERADORES Y EXPRESIONES<br />

Los programas C constan <strong>de</strong> <strong>datos</strong>, s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> programas y expresiones. Una expresión es,<br />

normalm<strong>en</strong>te, una ecuación matemática, tal como 3 + 5. En esta expresión, el símbolo más (+) es el<br />

operador <strong>de</strong> suma, y los números 3 y 5 se llaman operandos. En síntesis, una expresión es una secu<strong>en</strong>cia<br />

<strong>de</strong> operaciones y operandos que especifica un cálculo.<br />

Cuando se utiliza el + <strong>en</strong>tre números (o variables) se d<strong>en</strong>omina operador binario, <strong>de</strong>bido a que el<br />

operador + suma dos números. Otro tipo <strong>de</strong> operador <strong>de</strong> C es el operador unitario («unario»), que actúa<br />

sobre un Único valor. Si la variable x conti<strong>en</strong>e el valor 5, -x es el valor -5. El signo m<strong>en</strong>os (-) es el<br />

operador unitario m<strong>en</strong>os.<br />

C soporta un conjunto pot<strong>en</strong>te <strong>de</strong> operadores unarios, binarios y <strong>de</strong> otros tipos.<br />

Sintaxis<br />

Variable = expresión<br />

variable<br />

ador válido C <strong>de</strong>clarado como variable.<br />

expresión una constante, otra variable a la que se ha asignado p<br />

o una fórmula que se ha evaluado y cuyo tipo es el<br />

I<br />

I<br />

l<br />

I<br />

rograma que toma un valor. En al<br />

s o variables simples,<br />

as con operadores (a++, m==n,<br />

s como toupper ('b') .<br />

4.2. OPERADOR DE ASIGNACIÓN<br />

El operador = asigna el valor <strong>de</strong> la expresión <strong>de</strong>recha a la variable situada a su izquierda.<br />

codigo = 3467;<br />

fahr<strong>en</strong>heit = 123.456;<br />

coordX = 525;<br />

coordY = 725;<br />

Este operador es asociativo por la <strong>de</strong>recha, eso permite realizar asignaciones múltiples. Así,<br />

a = b = c = 45;<br />

equivale a<br />

a = (b = (c = 45));<br />

o dicho <strong>de</strong> otro modo, a las variables a, b y c se asigna el valor 4 5.<br />

Esta propiedad permite inicializar varias variables con una sola s<strong>en</strong>t<strong>en</strong>cia<br />

int a, b, c;<br />

a = b = c = 5; /* se asigna 5 a las variables a, b y c */<br />

A<strong>de</strong>más <strong>de</strong>l operador <strong>de</strong> asignación =, C proporciona cinco operadores <strong>de</strong> asignación adicionales.<br />

En la Tabla 4.1 aparec<strong>en</strong> los seis operadores <strong>de</strong> asignación.<br />

Estos operadores <strong>de</strong> asignación actúan como una notación abreviada para expresiones utilizadas<br />

con frecu<strong>en</strong>cia. Así, por ejemplo, si se <strong>de</strong>sea multiplicar 1 O por i, se pue<strong>de</strong> escribir<br />

i = i * 10;


Operadores y expresiones 1 17<br />

-<br />

Símbolo uso Descripción<br />

Tabla 4.1. Operadores <strong>de</strong> asignación <strong>de</strong> C.<br />

a=b Asigna el valor <strong>de</strong> b a a.<br />

a *= b Multiplica a por by asigna el resultado a la variable d.<br />

a / = b<br />

o- b<br />

Divi<strong>de</strong> a <strong>en</strong>tre b y asigna el resultado a la variable a.<br />

Fija a al resto <strong>de</strong> a/b.<br />

a 9-<br />

a += b Suma by a y io asigna a la variable d.<br />

a -= b Resta b <strong>de</strong> a y asigna el resultado a la variable d.<br />

C proporciona un operador abreviado <strong>de</strong> asignación ( * = ) , que realiza una asignación equival<strong>en</strong>te<br />

i *= 10; equivnlea i = i * 10;<br />

Operador S<strong>en</strong>t<strong>en</strong>cia S<strong>en</strong>t<strong>en</strong>cia<br />

abreviada no abreviada<br />

m t= n<br />

m -= n<br />

m *= n<br />

m /= n<br />

m 3.-<br />

o- n<br />

Tabla 4.2. Equival<strong>en</strong>cia <strong>de</strong> operadores <strong>de</strong> asignación.<br />

m=m+n;<br />

m=m-n;<br />

m=m*n;<br />

m=m/n;<br />

m=m%n;<br />

Estos operadores <strong>de</strong> asignación no siempre se utilizan, aunque algunos programadores C se<br />

acostumbran a su empleo por el ahorro <strong>de</strong> escritura que supon<strong>en</strong>.<br />

4.3. OPERADORES ARITMÉTICOS<br />

Los operadores aritméticos sirv<strong>en</strong> para realizar operaciones aritméticas básicas. Los operadores<br />

aritméticos C sigu<strong>en</strong> las reglas algebraicas típicas <strong>de</strong> jerarquía o prioridad. Estas reglas especifican la<br />

preced<strong>en</strong>cia <strong>de</strong> las operaciones aritméticas.<br />

Consi<strong>de</strong>re la expresión<br />

3+5*2<br />

¿Cual es el valor correcto, 1 6 ( 8 * 2 ) o 13 ( 3 + 1 O ) ? De acuerdo a las citadas reglas, la multiplicación<br />

se realiza antes que la suma. Por consigui<strong>en</strong>te, la expresión anterior equivale a:<br />

3 t (5 * 2)<br />

En C las expresiones interiores a paréntesis se evalúan primero; a continuación, se realizan los<br />

operadores unitarios, seguidos por los operadores <strong>de</strong> multiplicación, división, resto, suma y resta.<br />

Tabla 4.3. Operadores aritméticos.<br />

Operador Tipos <strong>en</strong>teros Tipos reales Ejemplo<br />

t Suma Suma X+Y<br />

- Resta Resta b-c<br />

*<br />

Producto Producto X*Y<br />

/ División <strong>en</strong>tera: coci<strong>en</strong>te División <strong>en</strong> coma flotante b / 5<br />

, División <strong>en</strong>tera: resto b%5


I_<br />

I_<br />

118 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Tabla 4.4. Preced<strong>en</strong>cia <strong>de</strong> operadores matemáticos básicos.<br />

Operador Operación Nivel <strong>de</strong> preced<strong>en</strong>cia<br />

+, - +25, -6.745 1<br />

*, /, % 5*5es 25 2<br />

25 / 5 es 5<br />

25 % 6 es 1<br />

+, - 2 +3 es 5 3<br />

2 - 3 es -1<br />

Obsérvese que los operadores + y -, cuando se utilizan <strong>de</strong>lante <strong>de</strong> un operador, actúan como<br />

operadores unitarios más y m<strong>en</strong>os.<br />

+75 /* 75 significa que es positivo */<br />

-154 /* 154 significa que es negativo */<br />

Ejemplo 4.1<br />

I. iCuÚl es el resultado <strong>de</strong> la expresión: 6 + 2 * 3 - 4 /2?<br />

6+2*3-4/2<br />

6 + 6 - 4/2<br />

-<br />

6+6 - 2<br />

12 - 2<br />

2. i Cuál es el resultado <strong>de</strong> la expresión: 5 * 5 ( 5 + ( 6 - 2 ) + 1 ) ?<br />

5 * (5 + (6-2) + 1)<br />

5* ( 5 + 4 +1)<br />

5 * 10<br />

50<br />

3. Cuál es el resultado <strong>de</strong> la e.xpresión: 7 - 6 / 3 + 2 * 3 / 2 - 4 / 2?<br />

7 - 6/3 i 2*3/2 - 4/2<br />

7 - 2 + 2*3/2 - 4/2<br />

7-2+ 3<br />

-<br />

- 4/2<br />

7- 2 +3 - 2<br />

5 + 3 -2<br />

8 -2


y<br />

2<br />

y)<br />

~- ~<br />

Operadores y expresiones 119<br />

4.3.1. Asociatividad<br />

En una expresión tal como<br />

3*4+5<br />

el compilador realiza primero la multiplicación -por t<strong>en</strong>er el operador * prioridad más alta- y luego<br />

la suma, por tanto, produce 17. Para forzar un ord<strong>en</strong> <strong>en</strong> las operaciones se <strong>de</strong>b<strong>en</strong> utilizar paréntesis<br />

3 * (4 + 5)<br />

produce 2 7, ya que 4 + 5 se realiza <strong>en</strong> primer lugar.<br />

La asociatividad <strong>de</strong>termina el ord<strong>en</strong> <strong>en</strong> que se agrupan los operadores <strong>de</strong> igual prioridad; es <strong>de</strong>cir,<br />

<strong>de</strong> izquierda a <strong>de</strong>recha o <strong>de</strong> <strong>de</strong>recha a izquierda. Por ejemplo,<br />

x ~<br />

+ z se agrupa como (x ~<br />

+ z<br />

ya que - y +, con igual prioridad, ti<strong>en</strong><strong>en</strong> asociatividad <strong>de</strong> izquierda a <strong>de</strong>recha. Sin embargo,<br />

x=y=z<br />

se agrupa como<br />

x = (y = z)<br />

dado que su asociatividad es <strong>de</strong> <strong>de</strong>recha a izquierda.<br />

Tabla 4.5. Prioridad y asociatividad.<br />

Prioridad (mayor a m<strong>en</strong>or)<br />

+ , - (unarios)<br />

*, /, %<br />

+,<br />

Asociatividad<br />

izquierda-<strong>de</strong>recha ( +)<br />

izquierda-<strong>de</strong>recha ( +)<br />

izquierda-<strong>de</strong>recha ( +)<br />

Ejemplo 4.2<br />

Cuál es el resultado <strong>de</strong> la expresicín: 7 * 1 O -5 % 3 * 4 + 9 .?<br />

Exist<strong>en</strong> tres operadores <strong>de</strong> prioridad más alta (* , % y * )<br />

70 - 5 io 3 * 4 + 9<br />

La asociatividad es <strong>de</strong> izquierda a <strong>de</strong>recha, por consigui<strong>en</strong>te se ejecuta a continuación 5%<br />

70 ~<br />

* 4 + 9<br />

y la segunda multiplicación se realiza a continuación, produci<strong>en</strong>do<br />

70-8t9<br />

Las dos operaciones restantes son <strong>de</strong> igual prioridad y como la asociatividad es a izquierda, se<br />

realizará la resta primero y se obti<strong>en</strong>e el resultado<br />

62 + 9<br />

y, por último, se realiza la suma y se obti<strong>en</strong>e el resultado final <strong>de</strong>


120 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

4.3.2. Uso <strong>de</strong> paréntesis<br />

Los paréntesis se pued<strong>en</strong> utilizar para cambiar el ord<strong>en</strong> usual <strong>de</strong> evaluación <strong>de</strong> una expresión<br />

<strong>de</strong>terminada por su prioridad y asociatividad. Las subexpresiones <strong>en</strong>tre paréntesis se evalúan <strong>en</strong> primer<br />

lugar según el modo estándar y los resultados se combinan para evaluar la expresión completa. Si los<br />

paréntesis están «anidados» -es <strong>de</strong>cir, un conjunto <strong>de</strong> paréntesis cont<strong>en</strong>ido <strong>en</strong> otro- se ejecutan <strong>en</strong><br />

primer lugar los paréntesis más internos. Por ejemplo, considérese la expresión<br />

(7 * (10 - 5) % 3)* 4 + 9<br />

La subexpresión (1 O - 5) se evalúa primero, produci<strong>en</strong>do<br />

( 7 * 5 % 3 ) * 4 + 9<br />

A continuación se evalúa <strong>de</strong> izquierda a <strong>de</strong>recha la subexpresión (7 * 5 % 3)<br />

seguida <strong>de</strong><br />

(35 % 3) * 4 + 9<br />

2 * 4 + 9<br />

Se realiza a continuación la multiplicación, obt<strong>en</strong>i<strong>en</strong>do<br />

8+9<br />

y la suma produce el resultado final<br />

17<br />

Precaución<br />

Se <strong>de</strong>be t<strong>en</strong>er cuidado <strong>en</strong> la escritura <strong>de</strong> expresiones que cont<strong>en</strong>gan dos o más operaciones para<br />

asegurarse que se evaliían <strong>en</strong> el ord<strong>en</strong> previsto. Incluso aunque no se requieran paréntesis, <strong>de</strong>b<strong>en</strong><br />

utilizarse para clarificar el ord<strong>en</strong> concebido <strong>de</strong> evaluación y escribir expresiones complicadas <strong>en</strong><br />

términos <strong>de</strong> expresiones más simples. Es importante, sin embargo, que los paréntesis estén equilibrados<br />

-cada paréntesis a la izquierda ti<strong>en</strong>e un correspondi<strong>en</strong>te paréntesis a la <strong>de</strong>recha que aparece<br />

posteriorm<strong>en</strong>te <strong>en</strong> la expresi6n- ya que exist<strong>en</strong> paréntesis <strong>de</strong>sequilibrados se producirá un<br />

error <strong>de</strong> compilación.<br />

( ( 8 - 5) + 4 - ( 3 + 7 )<br />

ermr <strong>de</strong> compilación,faLtaparéntesUfiwl a la <strong>de</strong>recha<br />

4.4. OPERADORES DE INCREMENTACIÓN Y DECREMENTACIÓN<br />

De las características que incorpora C, una <strong>de</strong> las más Útiles son los operadores <strong>de</strong> increm<strong>en</strong>to ++ y<br />

<strong>de</strong>crem<strong>en</strong>to --. Los operadores ++ y --, d<strong>en</strong>ominados <strong>de</strong> increm<strong>en</strong>tación y <strong>de</strong>crem<strong>en</strong>tación, suman o<br />

restan 1 a su argum<strong>en</strong>to, respectivam<strong>en</strong>te, cada vez que se aplican a una variable.<br />

Increm<strong>en</strong>tación<br />

Tabla 4.6. Operadores <strong>de</strong> incrern<strong>en</strong>tación (++) y <strong>de</strong>crern<strong>en</strong>tación (- -).<br />

Decrem<strong>en</strong>tación<br />

++n --n<br />

n += 1 n -= 1<br />

n=n+l n=n-1


Operadores y expresiones 121<br />

Por consigui<strong>en</strong>te,<br />

a+ t<br />

es igual que<br />

a = a+l<br />

Estos operadores ti<strong>en</strong><strong>en</strong> la propiedad <strong>de</strong> que pued<strong>en</strong> utilizarse como sufijo o prefijo, el resultado <strong>de</strong><br />

la expresón pue<strong>de</strong> ser distinto, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l contexto.<br />

Las s<strong>en</strong>t<strong>en</strong>cias<br />

++n;<br />

ni+ ;<br />

ti<strong>en</strong><strong>en</strong> el mismo efecto: así como<br />

--n;<br />

n-- ;<br />

Sin embargo, cuando se utilizan como expresiones tales como<br />

m = n++;<br />

printfi" n = %d",n--);<br />

el resultado es distinto si se utilizan como prefijo.<br />

m = ++n;<br />

printf ( " n = %d", --n) ;<br />

t+n produce un valor que es mayor <strong>en</strong> uno que el <strong>de</strong> n++ , y --n produce un valor que es m<strong>en</strong>or <strong>en</strong> uno<br />

que el valor <strong>de</strong> n--. Supongamos que<br />

n = 8;<br />

m = ++n; /* increm<strong>en</strong>ta n <strong>en</strong> 1, 9, y lo asigna a m */<br />

n = 9;<br />

printf(" n = %d",--n); /*<strong>de</strong>crem<strong>en</strong>ts n <strong>en</strong> 1, 8, y lo pasa a printf() */<br />

n = 8;<br />

m = n++; /* asigna n(8) a m, <strong>de</strong>spués increm<strong>en</strong>ta n <strong>en</strong> 1 (9) */<br />

n = 9;<br />

printf(" n = %d',n--); /* pasa n(9) a printfo, <strong>de</strong>spués <strong>de</strong>crem<strong>en</strong>ta n */<br />

En este otro ejemplo,<br />

int a = 1, b;<br />

b = a++; /* b vale 1 y a vale 2 */<br />

int a = 1, b;<br />

b = ++a; /* b vale 2 y a vale 2 */<br />

Si los operadores ++ y -- están <strong>de</strong> prefijos, la operación <strong>de</strong> increm<strong>en</strong>to o <strong>de</strong>crem<strong>en</strong>to se efectúa<br />

antes que la operación <strong>de</strong> asignación; si los operadores ++ y -- están <strong>de</strong> sufijos, la asignación<br />

se efectúa <strong>en</strong> primer lugar y la increm<strong>en</strong>tación o <strong>de</strong>crem<strong>en</strong>tación a continuación.


*/<br />

122 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo<br />

int i = 10:<br />

int j;<br />

...<br />

j = i++;<br />

Ejemplo 4.3<br />

Demostración <strong>de</strong>l funcionami<strong>en</strong>to <strong>de</strong> los operadores <strong>de</strong> increm<strong>en</strong>tc~/d~crem<strong>en</strong>t~~.<br />

#inclu<strong>de</strong> <br />

/* Test <strong>de</strong> operadores ++ y ~~<br />

void main( )<br />

i<br />

int m = 45, n = 75;<br />

printf( I' m = %d, n = %d\n",m,n);<br />

+ +m;<br />

--ni<br />

printf( 'I rn = %d, n = %d\n",m,n);<br />

m++;<br />

n--;<br />

printf( I' m = %d, n = %d\n",rn,n);<br />

1<br />

Ejecución<br />

m = 45, n = 75<br />

m = 46, n = 74<br />

m = 47, n = 73<br />

En este contexto, el ord<strong>en</strong> <strong>de</strong> los operadores es irrelevante.<br />

Ejemplo 4.4<br />

Difer<strong>en</strong>cias <strong>en</strong>tre operadores <strong>de</strong> preincrem<strong>en</strong>to y po.stiricrem<strong>en</strong>to.<br />

#inclu<strong>de</strong> <br />

/* Test <strong>de</strong> operadores ++ y -- */<br />

void main( )<br />

i<br />

int rn = 99, n;<br />

n = ++in;<br />

printf("m = %d, n = %d\n",m,n);<br />

n = mi+;<br />

printf("m = %d, n = %d\n",m,n);<br />

printf("m = %d \n",m++);<br />

printf ("m = %d \n",++m);<br />

}


Operadores y expresiones 123<br />

Ejecución<br />

m = 100, n = 100<br />

m = 101, n = 100<br />

m = 101<br />

m = 103<br />

Ejemplo 4.5<br />

Ord<strong>en</strong> <strong>de</strong> evaluación no pre<strong>de</strong>cihle <strong>en</strong> expresiones.<br />

#inclu<strong>de</strong> <br />

void main()<br />

l<br />

i<br />

int n = 5, t;<br />

t = ++n * --n;<br />

printf("n = %d, t = %d\n",n,t);<br />

printf ("%a%d %d\n", ++n, ++n, ++n) ;<br />

Ejecución<br />

n = 5, t = 25<br />

a 7 6<br />

Aunque parece que apar<strong>en</strong>tem<strong>en</strong>te el resultado <strong>de</strong> t será 3 o, <strong>en</strong> realidad es 2 5, <strong>de</strong>bido a que <strong>en</strong> la<br />

asignación <strong>de</strong> t, n se increm<strong>en</strong>ta a 6 y a continuación se <strong>de</strong>crem<strong>en</strong>ta a 5 antes <strong>de</strong> que se evalúe el<br />

operador producto, calculando 5 * 5. Por último, las tres subexpresiones se evalúan <strong>de</strong> <strong>de</strong>recha a<br />

izquierda sera 8 7 6 al contrario <strong>de</strong> 6 7 8 que parece que apar<strong>en</strong>tem<strong>en</strong>te se producirá.<br />

4.5. OPERADORES RELACIONALES<br />

C no ti<strong>en</strong>e tipos <strong>de</strong> <strong>datos</strong> lógicos o booleanos, como Pascal, para repres<strong>en</strong>tar los valores verda<strong>de</strong>ro (true)<br />

y falso (false). En su lugar se utiliza el tipo i nt para este propósito, con el valor <strong>en</strong>tero O que repres<strong>en</strong>ta<br />

a falso y distinto <strong>de</strong> cero a verda<strong>de</strong>ro.<br />

falso<br />

verda<strong>de</strong>ro<br />

cero<br />

distinto <strong>de</strong> cero<br />

Operadores tales como >= y == que comprueban una relación <strong>en</strong>tre dos operandos se llaman<br />

operadores relacionales y se utilizan <strong>en</strong> expresiones <strong>de</strong> la forma


124 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

expresión, operador-re1 aci onal expresión<br />

expresión, y expresión expresiones compatibles C<br />

operador-re1 acional un operador <strong>de</strong> la tabla 4.7<br />

Los operadores relacionales se usan normalm<strong>en</strong>te <strong>en</strong> s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> selección (if) o <strong>de</strong> iteración<br />

(while, for), que sirv<strong>en</strong> para comprobar una condición. Utilizando operadores relacionales se realizan<br />

operaciones <strong>de</strong> igualdad, <strong>de</strong>sigualdad y difer<strong>en</strong>cias relativas. La Tabla 4.7 muestra los operadores<br />

relacionales que se pued<strong>en</strong> aplicar a operandos <strong>de</strong> cualquier tipo <strong>de</strong> dato estándar: char, int ,<br />

float, double, etc.<br />

Cuando se utilizan los operadores <strong>en</strong> una expresión, el operador relaciona1 produce un O, o un 1,<br />

<strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l resultado <strong>de</strong> la condición. O se <strong>de</strong>vuelve para una condiciónfalsa, y 1 se <strong>de</strong>vuelve para<br />

una condición verda<strong>de</strong>ra. Por ejemplo, si se escribe<br />

c=3


Operadores y expresiones 125<br />

numero = 27<br />

la expresión<br />

numero == 100<br />

produce el valor O (false).<br />

Los caracteres se comparan utilizando los códigos numéricos (véase Apéndice B, código ASCII)<br />

'A ' c 'c ' es 1, verda<strong>de</strong>ra (true), ya que A es el código 65 y es m<strong>en</strong>or que el código 67 <strong>de</strong> c.<br />

'a' < 'c' es I , verda<strong>de</strong>ra (true): (código 97) y b (código 99).<br />

'b' c 'B ' es O, falsa valse) ya que b (código 98) no es m<strong>en</strong>or que B (código 66).<br />

Los operadores relacionales ti<strong>en</strong><strong>en</strong> m<strong>en</strong>or prioridad que los operadores aritméticos, y asociatividad<br />

<strong>de</strong> izquierda a <strong>de</strong>recha. Por ejemplo,<br />

m+5


126 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Tabla 4.8. Operadores lógicos.<br />

Operador Operación lógica Ejemplo<br />

Negación (!) No lógica ! (x >= y)<br />

Y lógica (&&) operando-l && operando-2 m < n && i > j<br />

O lógica II operando-1 II operando-2 m = 5 I I n != 10<br />

Tabla 4.9. Tabla <strong>de</strong> verdad <strong>de</strong>l operador lógico NOT í!).<br />

Operando (a)<br />

Verda<strong>de</strong>ro ( 1 )<br />

Falso (O)<br />

NOT a<br />

Falso (O)<br />

Verda<strong>de</strong>ro ( 1 )<br />

Tabla 4.10. Tabla <strong>de</strong> verdad <strong>de</strong>l operador lógico AND.<br />

Operandos<br />

a b a && b<br />

Verda<strong>de</strong>ro ( 1 ) Verda<strong>de</strong>ro (1)<br />

Verda<strong>de</strong>ro ( 1 ) Falso (O)<br />

Falso (O) Verda<strong>de</strong>ro ( 1 )<br />

Falso (O)<br />

Falso (O)<br />

Verda<strong>de</strong>ro ( 1 )<br />

Falso (O)<br />

Falso (O)<br />

Falso (O)<br />

Tabla 4.11. Tabla <strong>de</strong> verdad <strong>de</strong>l operador lógico OR (11).<br />

Operandos<br />

a b a II b<br />

Verda<strong>de</strong>ro ( 1 )<br />

Verda<strong>de</strong>ro (1)<br />

Falso (O)<br />

Falso (O)<br />

Verda<strong>de</strong>ro ( 1 )<br />

Falso (O)<br />

Verda<strong>de</strong>ro ( 1 )<br />

Falso (O)<br />

Verda<strong>de</strong>ro ( 1 )<br />

Verda<strong>de</strong>ro ( 1 )<br />

Verda<strong>de</strong>ro ( 1 )<br />

Falso (O)<br />

AI igual que los operadores matemáticos, el valor <strong>de</strong> una expresión formada con operadores lógicos<br />

<strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong>: (u) el operador y (h) sus argum<strong>en</strong>tos. Con operadores lógicos exist<strong>en</strong> sólo dos valores<br />

posibles para expresiones: verda<strong>de</strong>ro y ,falso. La forma más usual <strong>de</strong> mostrar los resultados <strong>de</strong><br />

operaciones lógicas es mediante las d<strong>en</strong>ominadas rubla? <strong>de</strong> verdud, que muestran como funcionan cada<br />

uno <strong>de</strong> los operadores lógicos.<br />

Ejemplo<br />

! (x+7 == 5)<br />

(anum > 5) && (Respuesta == 'S')<br />

(bnum > 3) I 1 (Respuesta == 'N')<br />

Los operadores lógicos se utilizan <strong>en</strong> expresiones condicionales y mediante s<strong>en</strong>t<strong>en</strong>cias 1 f , while<br />

o for, que se analizarán <strong>en</strong> capítulos posteriores. Así, por ejemplo, la s<strong>en</strong>t<strong>en</strong>cia ~f (si la condición es<br />

verdu<strong>de</strong>rdfulsu. .) se utiliza para evaluar operadores lógicos.<br />

1. if ((a < b) && (c > d))<br />

{<br />

puts ("Los resultados no son vc71idos'') ;<br />

1


Operadores y expresiones 127<br />

Si la variable a es m<strong>en</strong>or que h y, al mismo tiempo, c es mayor que d, <strong>en</strong>tonces visualizar el<br />

m<strong>en</strong>saje: Los resultados no son vál idos.<br />

2.if ((v<strong>en</strong>tas > 50000) I I (horas < 100))<br />

1<br />

prima = 100000;<br />

I<br />

Si la variable v<strong>en</strong>tas es mayor 5000 O o bi<strong>en</strong> la variable horas es m<strong>en</strong>or que 1 0 O, <strong>en</strong>tonces<br />

asignar a la variable prima el valor 1 0 O 0 0 O.<br />

3.if (!(v<strong>en</strong>tas i 2500))<br />

i<br />

prima = 12500;<br />

1<br />

En este ejemplo, si v<strong>en</strong>tas es mayor que o igual a 2500, se inicializará prima al valor<br />

12500.<br />

El operador ! ti<strong>en</strong>e prioridad más alta que &&, que a su vez ti<strong>en</strong>e mayor prioridad que t<br />

asociatividad es <strong>de</strong> izquierda a <strong>de</strong>recha.<br />

I . La<br />

La preced<strong>en</strong>cia <strong>de</strong> los operadores es: los operadores matemáticos ti<strong>en</strong><strong>en</strong> preced<strong>en</strong>cia sobre los<br />

operadores relacionales, y los operadores relacionales ti<strong>en</strong><strong>en</strong> preced<strong>en</strong>cia sobre los operadores lógicos.<br />

La sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia:<br />

if ((v<strong>en</strong>tas < salmin * 3 && ayos > 10 * iva) ...<br />

equivale a<br />

if ((v<strong>en</strong>tas < (salmin * 3)) && (ayos > (10 * iva))) ...<br />

4.6.1. Evaluación <strong>en</strong> cortocircuito<br />

En C los operandos <strong>de</strong> la izquierda <strong>de</strong> && y t I se evalúan siempre <strong>en</strong> primer lugar; si el valor <strong>de</strong>l<br />

operando <strong>de</strong> la izquierda <strong>de</strong>termina <strong>de</strong> forma inequívoca el valor <strong>de</strong> la expresión, el operando <strong>de</strong>recho<br />

no se evalúa. Esto significa que si el operando <strong>de</strong> la izquierda <strong>de</strong> && es falso o el <strong>de</strong> t 1 es verda<strong>de</strong>ro, el<br />

operando <strong>de</strong> la <strong>de</strong>recha no se evalúa. Esta propiedad se d<strong>en</strong>omina evaluacio'n <strong>en</strong> cortocircuito y se <strong>de</strong>be<br />

a que si p es falso, la condición p && q es falsa con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia <strong>de</strong>l valor <strong>de</strong> q y <strong>de</strong> este modo C no<br />

evalúa q. De modo similar si p es verda<strong>de</strong>ra la condición p I I q es verda<strong>de</strong>ra con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia <strong>de</strong>l<br />

valor <strong>de</strong> q y C no evalúa a q.<br />

Ejemplo 4.6<br />

Supongamos que se evalúa la expresión<br />

(x > 0.0) && (log(x) >= 0.5)<br />

Dado que <strong>en</strong> una operación lógica Y ( && ) si el operando <strong>de</strong> la izquierda ( x >O. O) es falso (x<br />

es negativo o cero), la expresión lógica se evalúa a falso, y <strong>en</strong> consecu<strong>en</strong>cia, no es necesario evaluar el<br />

segundo operando. En el ejemplo anterior la expresión evita calcular el logaritmo <strong>de</strong> números ( x )<br />

negativos o cero.<br />

La evaluación <strong>en</strong> cortocircuito ti<strong>en</strong>e dos b<strong>en</strong>eficios importantes:


128 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1. Una expresión booleana se pue<strong>de</strong> utilizar para guardar una operación pot<strong>en</strong>cialm<strong>en</strong>te insegura<br />

<strong>en</strong> una segunda expresión booleana.<br />

2. Se pue<strong>de</strong> ahorrar una consi<strong>de</strong>rable cantidad <strong>de</strong> tiempo <strong>en</strong> la evaluación <strong>de</strong> condiciones complejas.<br />

, Ejemplo 4.7<br />

Los b<strong>en</strong>e$cios anteriores se aprecian <strong>en</strong> la expresión booleana<br />

(n != O) && (x < l.O/n)<br />

ya que no se pue<strong>de</strong> producir un error <strong>de</strong> división por cero al evaluar esta expresión, pues si n es O,<br />

<strong>en</strong>tonces la primera expresión<br />

n != O<br />

es falsa y la segunda expresión<br />

x < 1.0in<br />

no se evalúa.<br />

De modo similar, tampoco se producirá un error <strong>de</strong> división por cero al evaluar la condición<br />

(n ==O) 1 1 (x >= 5.O/n)<br />

ya que si n es O, la primera expresión<br />

n == 0<br />

es verda<strong>de</strong>ra y <strong>en</strong>tonces no se evalúa la segunda expresión<br />

x >= 5.Oin<br />

Aplicación<br />

Dado el test condicional<br />

if ((7 > 5) / I (v<strong>en</strong>tas < 30) && (30 != 30)) ...<br />

C examina sólo la primera condición (7 > 5), ya que como es verda<strong>de</strong>ra, la operación lógica 1 1 í O)<br />

será verda<strong>de</strong>ra, sea cual sea el valor <strong>de</strong> la expresión que le sigue.<br />

Otro ejemplo es el sigui<strong>en</strong>te:<br />

if ((8 < 4) && (edad > 18) && (letra-inicial == ‘Z’)) ...<br />

En este caso, C examina la primera condición y su valor es falso; por consigui<strong>en</strong>te, sea cual sea el<br />

valor que sigue al operador &&, la expresión primitiva será falsa y toda la subexpresión a la <strong>de</strong>recha <strong>de</strong><br />

(8 < 4) no se evalúa por C.<br />

Por último, <strong>en</strong> la s<strong>en</strong>t<strong>en</strong>cia<br />

if ((10 > 4) I I (num == O)) ...<br />

la operación num == O nunca se evaluará.<br />

4.6.2. Asignaciones booleanas (lógicas)<br />

Las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> asignación booleanas se pued<strong>en</strong> escribir <strong>de</strong> modo que dan como resultado un valor <strong>de</strong><br />

tipo int que será cero o uno.


I<br />

Operadores y expresiones 129<br />

Ejemplo<br />

int edad, MayorDeEdad, juv<strong>en</strong>il;<br />

scanf ("%d",&edad);<br />

MayorDeEdad = (edad > 18); /* asigna el valor <strong>de</strong> edad > 18 MayorDeEdad.<br />

Cuando edad es mayor que 18, MayorDeEdad es 1 , sino O */<br />

juv<strong>en</strong>il = (edad >15) && (edad -100) && (n < 100);<br />

b. es-letra = ( ( 'A'


130 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

-==4<br />

AIB C<br />

Ejemplo<br />

1. Si se aplica el operador & <strong>de</strong> manipulación <strong>de</strong> bits a los números 9 y 14, se obti<strong>en</strong>e un resultado<br />

<strong>de</strong> 8. La Figura 4.1 muestra cómo se realiza la operación.<br />

2. (61) Ox3A6B<br />

OxOOFO<br />

= O011 1010 0110 1011<br />

= O000 O000 1111 O000<br />

Ox3A6B & OxOOFO = 0000 O000 O110 0000 = 0~0060<br />

1 3. ( ) 152 0x0098 = O000 O000 1001 1000<br />

5 0x0005 O000 O000 0000 O101<br />

152 I 5 = 0000 0000 1001 1101 = Ox009d<br />

4. (^) 83 0x53 = 0101 o01 1<br />

204 0xcc = 1100 1100<br />

83^204 = 1001 1111 = OX9f<br />

9 <strong>de</strong>cimal equivalea 1 O O 1 binario<br />

& & & &<br />

14<strong>de</strong>cimal equivale a 1 1 1 O binario<br />

= 1 0 O O binario<br />

- 8 <strong>de</strong>cimal<br />

Figura 4.1. Operador & <strong>de</strong> manipulación <strong>de</strong> bits.<br />

4.7.1. Operadores <strong>de</strong> asignación adicionales<br />

AI igual que los operadores aritméticos, los operadores <strong>de</strong> asignación abreviados están disponibles<br />

también para operadores <strong>de</strong> manipulación <strong>de</strong> bits. Estos operadores se muestran <strong>en</strong> la Tabla 4.13.<br />

Símbolo uso Descripción<br />

Tabla 4.13. Operadores <strong>de</strong> asignación adicionales.<br />

= b I Desplaza a a la clerecha b bits y asigna el resultado a a.<br />

&= a &= b Asigna a <strong>de</strong>l valor a&b.<br />

A- a ^= b Establece a a a “b.<br />

I= a I = b Establece a a a l b.


Operadores y expresiones 13 1<br />

4.7.2. Operadores <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> bits (>>, ) y SHL ( >) o a la izquierda (> 2<br />

= 0000 O110 = 6 ( división por 4 ) */<br />

d = y miembro


132 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Tabla 4.14. Operadores <strong>de</strong> direcciones.<br />

~~~ ~<br />

Operador Acción<br />

* Lee o modifica el valor apuntado por la expresión. Se correspon<strong>de</strong> con un puntero y el resultado es <strong>de</strong>l<br />

tipo apuntado.<br />

&<br />

-><br />

Devuelve un puntero al objeto utilizado como operando, que <strong>de</strong>be ser un lvalue (variable dotada <strong>de</strong> una<br />

dirección <strong>de</strong> memoria). El resultado es un puntero <strong>de</strong> tipo idéntico al <strong>de</strong>l operando.<br />

Permite acce<strong>de</strong>r a un miembro <strong>de</strong> un dato agregado (unión, <strong>estructura</strong>).<br />

Acce<strong>de</strong> a un miembro <strong>de</strong> un dato agregado (unión, <strong>estructura</strong>) apuntado por el operando <strong>de</strong> la<br />

izquierda.<br />

4.8. OPERADOR CONDICIONAL<br />

El operador condicional, ? : , es un operador ternario que <strong>de</strong>vuelve un resultado cuyo valor <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong><br />

la condición comprobada. Ti<strong>en</strong>e asociatividad a <strong>de</strong>rechas (<strong>de</strong>recha a izquierda).<br />

Al ser un operador ternario requiere tres operandos. El operador condicional se utiliza para<br />

reemplazar a la s<strong>en</strong>t<strong>en</strong>cia i f -e 1 se lógica <strong>en</strong> algunas situaciones. El formato <strong>de</strong>l operador condicional<br />

es:<br />

expresion-c ?<br />

expresion-v : expresion-f;<br />

Se evalúa expresion-c y su valor (cero = falso, distinto <strong>de</strong> cero = verda<strong>de</strong>ro) <strong>de</strong>termina cuál es<br />

la expresión a ejecutar; si la condición es verda<strong>de</strong>ra se ejecuta expresion-v y si es falsa se ejecuta<br />

expres i on- f.<br />

La Figura 4.3 muestra el funcionami<strong>en</strong>to <strong>de</strong>l operador condicional.<br />

(v<strong>en</strong>tas > 150000) ? comision = 100 comision = O;<br />

si v<strong>en</strong>tas es mayor<br />

que 150.000 se<br />

ejecuta:<br />

comision = 100<br />

si v<strong>en</strong>tas no es<br />

mayor que 150.000 se<br />

ajecuzu:<br />

comision = O<br />

Figura 4.3. Formato <strong>de</strong> un operador condicional.<br />

Otros ejemplos <strong>de</strong>l uso <strong>de</strong>l operador ? : son:<br />

n >= O ? 1 : -1 /*I si n es positivo, -1 si es negativo */<br />

m>=n?m:n /* <strong>de</strong>vuelve el mayor valor <strong>de</strong> m y n */<br />

/*escribe x, y escribe el carácter fin <strong>de</strong> línea(\n) si x%5(resto 5) es<br />

o, <strong>en</strong> caso contrario un tabulador(\t) */<br />

printf("%d %c", x, x%5 ?'\t':'\n');<br />

La preced<strong>en</strong>cia <strong>de</strong> ? y : es m<strong>en</strong>or que la <strong>de</strong> cualquier otro operando tratado hasta ese mom<strong>en</strong>to.<br />

Su asociatividad es a <strong>de</strong>rechas.


Operadores y expresiones 133<br />

r4.9. OPERADOR COMA<br />

El operador coma permite combinar dos o más expresiones separadas por comas <strong>en</strong> una sola línea. Se<br />

evalúa primero la expresión <strong>de</strong> la izquierda y luego las restantes expresiones <strong>de</strong> izquierda a <strong>de</strong>recha. La<br />

expresión más a la <strong>de</strong>recha <strong>de</strong>termina el resultado global. El uso <strong>de</strong>l operador coma es como sigue:<br />

expresión , expresión , expresión , . . . , expresión<br />

Cada expresión se evalúa com<strong>en</strong>zando <strong>de</strong>s<strong>de</strong> la izquierda y continuando hacia la <strong>de</strong>recha. Por<br />

ejemplo, <strong>en</strong><br />

int i = 10, j = 25;<br />

dado que el operador coma se asocia <strong>de</strong> izquierda a <strong>de</strong>recha, la primera variable está <strong>de</strong>clarada e<br />

inicializada antes que la segunda variable j. Otros ejemplos son:<br />

i++, j++ ;<br />

i++, j++, k++ ;<br />

equivale u<br />

equivale a<br />

i++; j++;<br />

i++; j++; k++;<br />

El operador coma ti<strong>en</strong>e la m<strong>en</strong>or prioridad <strong>de</strong> todos los operadores C, y se asocia <strong>de</strong> izquierda<br />

a <strong>de</strong>recha.<br />

El resultado <strong>de</strong> la expresión global se <strong>de</strong>termina por el valor <strong>de</strong> expresión,,. Por ejemplo,<br />

int i, j, resultado;<br />

resultado = j = 10, i = j, ++i;<br />

El valor <strong>de</strong> esta expresión y valor asignado a resultado es 11. En primer lugar, a j se asigna el<br />

valor 1 o, a continuación a i se asigna el valor <strong>de</strong> j . Por último, i se increm<strong>en</strong>ta a 11.<br />

La técnica <strong>de</strong>l operador coma permite operaciones interesantes<br />

i = 10;<br />

j = (i = 12, i + 8);<br />

Cuando se ejecute la sección <strong>de</strong> código anterior, j vale 2 O, ya que i vale 1 O <strong>en</strong> la primera s<strong>en</strong>t<strong>en</strong>cia,<br />

<strong>en</strong> la segunda toma i el valor 12 y al sumar i + 8 resulta 2 O.<br />

4.10. OPERADORES ESPECIALES ( 1 [ 1<br />

C admite algunos operadores especiales que sirv<strong>en</strong> para propósitos difer<strong>en</strong>tes. Entre ellos se <strong>de</strong>stacan:<br />

o, [ I .<br />

4.10.1. El operador ( 1<br />

El operador ( ) es el operador <strong>de</strong> llamada a funciones. Sirve para <strong>en</strong>cerrar los argum<strong>en</strong>tos <strong>de</strong> una<br />

función, efectuar conversiones explícitas <strong>de</strong> tipo, indicar <strong>en</strong> el s<strong>en</strong>o <strong>de</strong> una <strong>de</strong>claración que un<br />

id<strong>en</strong>tificador correspon<strong>de</strong> a una función, resolver los conflictos <strong>de</strong> prioridad <strong>en</strong>tre operadores.<br />

4.10.2. El operador 1<br />

Sirve para dim<strong>en</strong>sionar los arrays y <strong>de</strong>signar un elem<strong>en</strong>to <strong>de</strong> un array.


134 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplos <strong>de</strong> ello:<br />

double v[201; /* <strong>de</strong>fine un array <strong>de</strong> 20 elem<strong>en</strong>tos */<br />

printf ("v[2] = %e",v[21); /* escribe el elem<strong>en</strong>to 2 <strong>de</strong> v */<br />

return vli-INFERIOR]; /* <strong>de</strong>vuelve el elem<strong>en</strong>to i-INFERIOR */<br />

4.11. EL OPERADOR SIZEOF<br />

Con frecu<strong>en</strong>cia su programa necesita conocer el tamaño <strong>en</strong> bytes <strong>de</strong> un tipo <strong>de</strong> dato o variable. C<br />

proporciona el operador s i zeo f , que toma un argum<strong>en</strong>to, bi<strong>en</strong> un tipo <strong>de</strong> dato o bi<strong>en</strong> el nombre <strong>de</strong> una<br />

variable (escalar, array, registro, etc.). El formato <strong>de</strong>l operador es<br />

sizeof (nombre-variabl e)<br />

s i z eo f ( t ipo-da t o )<br />

sizeof(expresión)<br />

Ejemplo 4.9<br />

Si se supone que el tipo in t consta <strong>de</strong> cuatro bytes y el tipo double consta <strong>de</strong> ocho bytes, las sigui<strong>en</strong>tes<br />

expresiones proporcionarán los valores 1, 4 y 8 respectivam<strong>en</strong>te<br />

sizeof (char)<br />

sizeof (unsigned int)<br />

sizeof(doub1e).<br />

El operador sizeof se pue<strong>de</strong> aplicar también a expresiones. Se pue<strong>de</strong> escribir<br />

printf ("La variable k es Xd bytes 'I, sizeof (k));<br />

printf("La expresión a + b ocupa %d bytes ",sizeof (a + b));<br />

El operador si z eo f es un operador unitario, ya que opera sobre un valor Único. Este operador<br />

produce un resultado que es el tamaño, <strong>en</strong> bytes, <strong>de</strong>l dato o tipo <strong>de</strong> dato especificados. Debido a que la<br />

mayoría <strong>de</strong> los tipos <strong>de</strong> <strong>datos</strong> y variables requier<strong>en</strong> difer<strong>en</strong>tes cantida<strong>de</strong>s <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to interno <strong>en</strong><br />

computadores difer<strong>en</strong>tes, el operador si zeo f permite consist<strong>en</strong>cia <strong>de</strong> programas <strong>en</strong> difer<strong>en</strong>tes tipos <strong>de</strong><br />

computadores.<br />

El operador sizeof se d<strong>en</strong>omina también operador <strong>en</strong> tiempo <strong>de</strong> compilación, ya que <strong>en</strong> tiempo <strong>de</strong><br />

compilación, el compilador sustituye cada ocurr<strong>en</strong>cia <strong>de</strong> si zeof <strong>en</strong> su programa por un valor <strong>en</strong>tero<br />

sin signo (unsigned). El operador si zeof se utiliza <strong>en</strong> programación avanzada.<br />

Ejercicio 4.1<br />

Suponga que se <strong>de</strong>sea conocer el tamaño, <strong>en</strong> bytes, <strong>de</strong> variables <strong>de</strong> coma~flotante y <strong>de</strong> doble precisiátz<br />

<strong>de</strong> su computadora. El sigui<strong>en</strong>te programa realiza esta tarea:<br />

/* Imprime el tamaño <strong>de</strong> valores <strong>de</strong> coma flotante y double */<br />

#inclu<strong>de</strong> <br />

int main()<br />

i<br />

printf("E1 tamaño <strong>de</strong> variables <strong>de</strong> coma flotante es Xd \n",<br />

sizeof(f1oat));<br />

printf('E1 tamaño <strong>de</strong> variables <strong>de</strong> doble precisión es %d \n",<br />

sizeof(doub1e) );


~ _ _<br />

}<br />

return O;<br />

Operadores y expresiones 135<br />

Este programa producirá difer<strong>en</strong>tes resultados <strong>en</strong> difer<strong>en</strong>tes clases <strong>de</strong> computadores. Compilando<br />

este programa bajo C, el programa produce la salida sigui<strong>en</strong>te:<br />

El tamaño <strong>de</strong> variables <strong>de</strong> coma flotante es: 4<br />

El tamaño <strong>de</strong> variables <strong>de</strong> doble precisión es: 8<br />

4.12. CONVERSIONES DE TIPOS<br />

Con frecu<strong>en</strong>cia, se necesita convertir un valor <strong>de</strong> un tipo a otro sin cambiar el valor que repres<strong>en</strong>ta. Las<br />

conversiones <strong>de</strong> tipos pued<strong>en</strong> ser implícitus (ejecutadas automáticam<strong>en</strong>te) o explícitas (solicitadas<br />

específicam<strong>en</strong>te por el programador). C hace muchas conversiones <strong>de</strong> tipos automáticam<strong>en</strong>te:<br />

O C convierte valores cuando se asigna un valor <strong>de</strong> un tipo a una variable <strong>de</strong> otro tipo.<br />

O C convierte valores cuando se combinan tipos mixtos <strong>en</strong> expresiones.<br />

O C convierte valores cuando se pasan argum<strong>en</strong>tos a funciones.<br />

4.12.1. Conversión implícita<br />

Los tipos fundam<strong>en</strong>tales (básicos) pued<strong>en</strong> ser mezclados librem<strong>en</strong>te <strong>en</strong> asignaciones y expresiones. Las<br />

conversiones se ejecutan automáticam<strong>en</strong>te: los operandos <strong>de</strong> tipo más bajo se conviert<strong>en</strong> <strong>en</strong> los <strong>de</strong> tipo<br />

más alto.<br />

int i = 12;<br />

double x = 4;<br />

x = xci; /*valor <strong>de</strong> i se convierte <strong>en</strong> double antes <strong>de</strong> la suma */<br />

x = i/5; /* primero hace una división <strong>en</strong>tera i/5==2, 2 se convierte a<br />

tipo doble: 2.0 y se asigna a x */<br />

x = 4.0;<br />

x = x/5 /* convierte 5 a tipo double, hace una división real: 0.8 y se<br />

asigna a x */<br />

4.12.2. Reglas<br />

Si cualquier operando es <strong>de</strong> tipo char, short o <strong>en</strong>umerado se convierte <strong>en</strong> tipo int y si los<br />

operandos ti<strong>en</strong><strong>en</strong> difer<strong>en</strong>tes tipos, la sigui<strong>en</strong>te lista <strong>de</strong>termina a qué operación convertirá. Esta operación<br />

se llama promoción integral.<br />

int<br />

unsigned int<br />

long<br />

unsigned long<br />

float<br />

double<br />

El tipo que vi<strong>en</strong>e primero <strong>en</strong> esta lista se convierte <strong>en</strong> el que vi<strong>en</strong>e segundo. Por ejemplo, si los tipos<br />

operandos son int y long, el operando int se convierte <strong>en</strong> long.<br />

char c = 65; /* 65 se convierte <strong>en</strong> tipo char permitido */<br />

char c = 10000; /* permitido, pero resultados impre<strong>de</strong>cibles */


~~~<br />

136 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

4.12.3. Conversión explícita<br />

C fuerza la conversión explícita <strong>de</strong> tipos mediante el operador <strong>de</strong> mol<strong>de</strong> (cast). El operador mol<strong>de</strong> ti<strong>en</strong>e<br />

el formato:<br />

(tiponombre) valor /* convierte va.lor a tiponombre */<br />

(float) i; /* convierte i a float */<br />

(int) 3.4; /* convierte 3.4 a <strong>en</strong>tero, 3*/<br />

(int*) malloc(2*16) ; /* convierte el valor <strong>de</strong>vuelto por malloc: void*<br />

a int*. Es una conversión <strong>de</strong> punteros. */<br />

El operador mol<strong>de</strong> (tipo, cast) ti<strong>en</strong>e la misma prioridad que otros operadores unitarios tales como<br />

+,-Y!<br />

precios = (int)19.99 + (int)11.99;<br />

4.13. PRIORIDAD Y ASOCIATIVIDAD<br />

La prioridad o preced<strong>en</strong>cia <strong>de</strong> operadores <strong>de</strong>termina el ord<strong>en</strong> <strong>en</strong> el que se aplican los operadores a un<br />

valor. Los operadores C vi<strong>en</strong><strong>en</strong> <strong>en</strong> una tabla con dieciséis grupos. Los operadores <strong>de</strong>l grupo 1 ti<strong>en</strong><strong>en</strong><br />

mayor prioridad que los <strong>de</strong>l grupo 2, y así sucesivam<strong>en</strong>te:<br />

o Si dos operadores se aplican al mismo operando, el operador con mayor prioridad se aplica<br />

primero.<br />

o Todos los operadores <strong>de</strong>l mismo grupo ti<strong>en</strong><strong>en</strong> igual prioridad y asociatividad.<br />

o La asociatividad izquierda-<strong>de</strong>recha significa aplicar el operador más a la izquierda primero, y <strong>en</strong><br />

la asociatividad <strong>de</strong>recha-izquierda se aplica primero el operador más a la <strong>de</strong>recha.<br />

0 Los paréntesis ti<strong>en</strong><strong>en</strong> la máxima prioridad.<br />

Prioridad<br />

3<br />

4<br />

5<br />

6<br />

7<br />

8<br />

9<br />

10<br />

11<br />

12<br />

13<br />

14<br />

15<br />

16<br />

Operadores<br />

1 x -> [I o I-D<br />

2 ++ -- - ! - + & * sizeof D-I<br />

I-D<br />

I-D<br />

I-D<br />

I-D<br />

&&<br />

I1<br />

?: (expresión condicional)<br />

- *- ~ /= %= += -=<br />

i>= &= /=<br />

, (operador coma)<br />

Asociatividad<br />

I-D<br />

I-D<br />

I-D<br />

I-D<br />

I-D<br />

I-D<br />

I-D<br />

D-I<br />

D-I<br />

I-D<br />

1 - D : Izquierda - Derecha.<br />

D - 1 : Derecha - Izquierda.


Operadores y expresiones 137<br />

4.14. RESUMEN<br />

Este capítulo examina los sigui<strong>en</strong>tes temas:<br />

Concepto <strong>de</strong> operadores y expresiones.<br />

Operadores <strong>de</strong> asignación: básicos y aritméticos.<br />

Operadores aritméticos, incluy<strong>en</strong>do +, -, *, / y<br />

% (módulos).<br />

Operadores <strong>de</strong> increm<strong>en</strong>to y <strong>de</strong>crem<strong>en</strong>to. Estos<br />

operadores se aplican <strong>en</strong> formatos pre (anterior)<br />

y post (posterior). C permite aplicar estos operadores<br />

a variables que almac<strong>en</strong>an caracteres,<br />

<strong>en</strong>teros e incluso números <strong>en</strong> coma flotante.<br />

Operadores relacionales y lógicos que permit<strong>en</strong><br />

construir expresiones lógicas. C no soporta un<br />

tipo lógico (boolean) pre<strong>de</strong>finido y <strong>en</strong> su lugar<br />

consi<strong>de</strong>ra O (cero) comofalso y cualquier valor<br />

distinto <strong>de</strong> cero como verda<strong>de</strong>ro,<br />

Operadores <strong>de</strong> manipulación <strong>de</strong> bits que realizan<br />

operaciones bit a bit (bitwise), AND, OR,<br />

XOR y NOT. C soporta los operadores <strong>de</strong> <strong>de</strong>splazami<strong>en</strong>to<br />

<strong>de</strong> bits cc y >>.<br />

Operadores <strong>de</strong> asignación <strong>de</strong> manipulación <strong>de</strong><br />

bits que ofrec<strong>en</strong> formatos abreviados para<br />

s<strong>en</strong>t<strong>en</strong>cias simples <strong>de</strong> manipulación <strong>de</strong> bits.<br />

El operador coma, que es un operador muy<br />

especial, separa expresiones múltiples <strong>en</strong> las<br />

mismas s<strong>en</strong>t<strong>en</strong>cias y requiere que el programa<br />

evalúe totalm<strong>en</strong>te una expresión antes <strong>de</strong><br />

evaluar la sigui<strong>en</strong>te.<br />

La expresión condicional, que ofrece una forma<br />

abreviada para la s<strong>en</strong>t<strong>en</strong>cia alternativa simpledoble<br />

if-else, que se estudiará <strong>en</strong> el capítulo<br />

sigui<strong>en</strong>te.<br />

0 Operadores especiales: (), [I .<br />

Conversión <strong>de</strong> tipos (typecasting) o mol<strong>de</strong>ado,<br />

que permite forzar la conversión <strong>de</strong> tipos <strong>de</strong> una<br />

expresión.<br />

Reglas <strong>de</strong> prioridad y asociatividad <strong>de</strong> los<br />

difer<strong>en</strong>tes operadores cuando se combinan <strong>en</strong><br />

expresiones.<br />

El operador cizeof, que <strong>de</strong>vuelve el tamaño <strong>en</strong><br />

bytes <strong>de</strong> cualquier tipo <strong>de</strong> dato o una variable.<br />

4.15. EJERCICIOS<br />

4.1. Determinar el valor <strong>de</strong> las sigui<strong>en</strong>tes expresiones<br />

aritméticas:<br />

15 / 12 15 % 12<br />

24 / 12 24 % 12<br />

123 / 100 123 % 100<br />

200 / 100 200 % 100<br />

4.2. ¿Cuál es el valor <strong>de</strong> cada una <strong>de</strong> las sigui<strong>en</strong>tes<br />

expresiones?<br />

~ ) 1 5 * 14 - 3 * 7<br />

b ) - 4 * 5 * 2<br />

c) (24 + 2 * 6) / 4<br />

d)a / a / a*b<br />

e) 3 + 4 *í8 * (4 - (9 + 31/61]<br />

f i 4 * 3 * 5 + 8 * 4 * 2 - 5<br />

g)4 - 40 1 5<br />

h) (-5) % (-2)<br />

4.3. Escribir las sigui<strong>en</strong>tes expresiones aritméticas<br />

como expresiones <strong>de</strong> computadora: La pot<strong>en</strong>cia<br />

pue<strong>de</strong> hacerse con la función pow(), por ejemplo<br />

(x + y)'==pow(x+y,2)<br />

X<br />

a) - +1<br />

Y<br />

C<br />

e) (a+ b) - d


138 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

4.10.<br />

Escribir un programa que convierte un número<br />

dado <strong>de</strong> segundos <strong>en</strong> el equival<strong>en</strong>te <strong>de</strong> minutos<br />

y segundos.<br />

Y<br />

c) x+ -<br />

Z<br />

Y<br />

x+ - z<br />

XY<br />

g) -<br />

1-4~<br />

b<br />

4- c+d<br />

i) (x + y)' . (a - b)<br />

4.4. ¿Cuál <strong>de</strong> los sigui<strong>en</strong>tes id<strong>en</strong>tificadores son<br />

válidos?<br />

n<br />

Miproblema<br />

Mi Juego<br />

Mi Juego<br />

write<br />

m&m<br />

4.5.<br />

4.6.<br />

4.7.<br />

4.8.<br />

4.9.<br />

-m-m<br />

registro<br />

AB<br />

85 Nombre<br />

AAAAAAAAAA<br />

Nombre- Apellidos<br />

Saldo-Actual<br />

92<br />

Universidad<br />

Pontificia<br />

Set 15<br />

* 143Edad<br />

X es una variable <strong>en</strong>tera e Y una variable<br />

carácter. Qué resultados producirá la s<strong>en</strong>t<strong>en</strong>cia<br />

scanf ( "%d %d" , & x, & y) si la <strong>en</strong>trada<br />

es<br />

a) 5 c<br />

b) 5C<br />

Escribir un programa que lea un <strong>en</strong>tero, lo<br />

multiplique por 2 y a continuación lo escriba<br />

<strong>de</strong> nuevo <strong>en</strong> la pantalla.<br />

Escribir las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> asignación que<br />

permitan intercambiar los cont<strong>en</strong>idos (valores)<br />

<strong>de</strong> dos variables.<br />

Escribir un programa que lea dos <strong>en</strong>teros <strong>en</strong> las<br />

variables x e y, y a continuación obt<strong>en</strong>ga los<br />

valores <strong>de</strong> : 1. x / y, 2. x % y. Ejecute el<br />

programa varias veces con difer<strong>en</strong>tes pares <strong>de</strong><br />

<strong>en</strong>teros como <strong>en</strong>trada.<br />

Escribir un programa que solicite al usuario la<br />

longitud y anchura <strong>de</strong> una habitación y a<br />

continuación visualice su superficie con cuatro<br />

<strong>de</strong>cimales.<br />

4.11.<br />

4.13.<br />

4.14.<br />

4.15.<br />

Escribir un programa que solicite dos números<br />

<strong>de</strong>cimales y calcule su suma, visualizando la<br />

suma ajustada a la <strong>de</strong>recha. Por ejemplo, si los<br />

números son 57.45 y 425.55, el programa<br />

visualizará:<br />

57.45<br />

425.55<br />

483.00<br />

4.12, ¿Cuáles son los resultados visualizados por el<br />

sigui<strong>en</strong>te programa, si los <strong>datos</strong> proporcionados<br />

son 5 y 8?<br />

#inclu<strong>de</strong> <br />

const int M = 6;<br />

int main ( )<br />

i<br />

int a, b, c;<br />

gets("1ntroduce el valor <strong>de</strong> a<br />

y <strong>de</strong> b") ;<br />

scanf ("ad%d",&a,&b);<br />

c=2*a-b;<br />

c -= M;<br />

b = a + c - M;<br />

a=b*M;<br />

printf ("\n a = %d\n",a);<br />

b = - 1;<br />

printf (" %6d %ód",b,c);<br />

return O;<br />

I<br />

Escriba un programa para calcular la longitud<br />

<strong>de</strong> la circunfer<strong>en</strong>cia y el área <strong>de</strong>l círculo para<br />

un radio introducido por el teclado.<br />

Escribir un programa que visualice valores<br />

tales como:<br />

7.1<br />

7.12<br />

7.123<br />

7.1234<br />

7.12345<br />

7.123456<br />

Escribir un programa que lea tres <strong>en</strong>teros y<br />

emita un m<strong>en</strong>saje que indique si están o no <strong>en</strong><br />

ord<strong>en</strong> numérico.


Operadores y expresiones 139<br />

4.16. Escribir una s<strong>en</strong>t<strong>en</strong>cia lógica (boolean)<br />

que clasifique un <strong>en</strong>tero x <strong>en</strong> una <strong>de</strong> las<br />

sigui<strong>en</strong>tes categorías.<br />

x < o obi<strong>en</strong> O I x I100<br />

obi<strong>en</strong> x > 100<br />

4.17. Escribir un programa que introduzca el número<br />

<strong>de</strong> un mes (1 a 12) y visualice el número <strong>de</strong><br />

días <strong>de</strong> ese mes.<br />

4.18. Escribir un programa que lea dos números y<br />

visualice el mayor, utilizar el operador ternario<br />

? :.<br />

4.19. El domingo <strong>de</strong> Pascua es el primer domingo<br />

<strong>de</strong>spués <strong>de</strong> la primera luna ll<strong>en</strong>a posterior al<br />

equinoccio <strong>de</strong> primavera, y se <strong>de</strong>termina<br />

mediante el sigui<strong>en</strong>te cálculo s<strong>en</strong>cillo.<br />

A = año % 19<br />

B = año % 4<br />

C = año % 7<br />

D = (19 * A + 24) % 30<br />

E= (2 *3+4* C + 6 * D + 5 )<br />

% 7<br />

N = (22 + D + E)<br />

don<strong>de</strong> N indica el número <strong>de</strong> día <strong>de</strong>l mes <strong>de</strong><br />

marzo (si N es igual o m<strong>en</strong>or que 31) o<br />

abr i 1 (si es mayor que 3 1). Construir un<br />

programa que t<strong>en</strong>ga como <strong>en</strong>trada un año y<br />

<strong>de</strong>termine la fecha <strong>de</strong>l domingo <strong>de</strong> Pascua.<br />

Nota: utilizar el operador ternario ? : para<br />

seleccionar.<br />

4.20. Determinar si el carácter asociado a un código<br />

introducido por teclado correspon<strong>de</strong> a un<br />

carácter alfabético, dígito, <strong>de</strong> puntuación,<br />

especial o no imprimible.<br />

4.1 6. PROBLEMAS<br />

4.1. Escribir un programa que lea dos <strong>en</strong>teros <strong>de</strong> tres<br />

dígitos y calcule e imprima su producto, coci<strong>en</strong>te<br />

y el resto cuando el primero se divi<strong>de</strong> por el<br />

segundo. La salida será justificada a <strong>de</strong>recha.<br />

4.2. Una temperatura Celsius (c<strong>en</strong>tígrados) pue<strong>de</strong> ser<br />

convertida a una temperatura equival<strong>en</strong>te F <strong>de</strong><br />

acuerdo a la sigui<strong>en</strong>te fórmula:<br />

f =(y) 9 c32<br />

4.3. Un sistema <strong>de</strong> ecuaciones lineales<br />

ax+by=c<br />

dx+ey=f<br />

se pue<strong>de</strong> resolver con las sigui<strong>en</strong>tes fórmulas:<br />

ce - bf<br />

x= Y =<br />

ae - bd<br />

af - cd<br />

ae - bd<br />

Diseñar un programa que lea dos conjuntos <strong>de</strong><br />

coefici<strong>en</strong>tes (a, b y c ; d, e yfi y visualice los<br />

valores <strong>de</strong> x e y.<br />

'<br />

,/ I<br />

I<br />

I<br />

Escribir un programa que lea la temperatura <strong>en</strong><br />

grados Celsius y la escriba <strong>en</strong> F.


140 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

4.4. Escribir un programa que dibuje el rectángulo<br />

sigui<strong>en</strong>te:<br />

************<br />

* *<br />

* *<br />

* *<br />

* *<br />

************<br />

4.5.<br />

4.6.<br />

4.7.<br />

4.8.<br />

4.9.<br />

Modificar el programa anterior, <strong>de</strong> modo que<br />

se lea una palabra <strong>de</strong> cinco letras y se imprima<br />

<strong>en</strong> el c<strong>en</strong>tro <strong>de</strong>l rectángulo.<br />

Escribir un programa C que lea dos números y<br />

visualice el mayor.<br />

Escribir un programa para convertir una<br />

medida dada <strong>en</strong> pies a sus equival<strong>en</strong>tes <strong>en</strong> : a)<br />

yardas; b) pulgadas; c) c<strong>en</strong>tímetros, y d)<br />

metros (1 pie = 12 pulgadas, 1 yarda = 3 pies,<br />

1 pulgada = 2,54 cm, 1 m = 100 cm). Leer el<br />

número <strong>de</strong> pies e imprimir el número <strong>de</strong><br />

yardas, pies, pulgadas, c<strong>en</strong>tímetros y metros.<br />

T<strong>en</strong>i<strong>en</strong>do como <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada el radio y la<br />

altura <strong>de</strong> un cilindro queremos calcular: el área<br />

lateral y el volum<strong>en</strong> <strong>de</strong>l cilindro.<br />

Calcular el área <strong>de</strong> un triángulo mediante la<br />

fórmula:<br />

Área = (p(p - a)(p - b)(p - c))'"<br />

don<strong>de</strong> p es el semiperímetro, p = (a + b + c)n,<br />

si<strong>en</strong>do a, b, c los tres lados <strong>de</strong>l triángulo.<br />

4.10. Escribimos un programa <strong>en</strong> el que se<br />

introduc<strong>en</strong> como <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada la longitud<br />

<strong>de</strong>l perímetro <strong>de</strong> un terr<strong>en</strong>o, expresada con tres<br />

números <strong>en</strong>teros que repres<strong>en</strong>tan hectómetros,<br />

<strong>de</strong>cámetros y metros respectivam<strong>en</strong>te. Se ha <strong>de</strong><br />

escribir, con un rótulo repres<strong>en</strong>tativo, la<br />

longitud <strong>en</strong> <strong>de</strong>címetros.<br />

4.11. Construir un programa que calcule y escriba el<br />

producto, coci<strong>en</strong>te <strong>en</strong>tero y resto <strong>de</strong> dos<br />

números <strong>de</strong> tres cifras.<br />

4.12. Construir un programa para obt<strong>en</strong>er la<br />

hipot<strong>en</strong>usa y los ángulos agudos <strong>de</strong> un<br />

triángulo rectángulo a partir <strong>de</strong> las longitu<strong>de</strong>s<br />

<strong>de</strong> los catetos.<br />

4.13. Escribir un programa que <strong>de</strong>sglose cierta<br />

cantidad <strong>de</strong> segundos introducida por teclado<br />

<strong>en</strong> su equival<strong>en</strong>te <strong>en</strong> semanas, días, horas,<br />

minutos y segundos.<br />

4.14. Escribir un programa que exprese cierta<br />

cantidad <strong>de</strong> pesetas <strong>en</strong> billetes y monedas <strong>de</strong><br />

curso legal.<br />

4.15. La fuerza <strong>de</strong> atracción <strong>en</strong>tre dos masas, m, y<br />

m, separadas por una distancia d, está dada por<br />

la fórmula:<br />

F=<br />

G *in, *in2<br />

dz<br />

don<strong>de</strong> G es la constante <strong>de</strong> gravitación<br />

universal<br />

G = 6.673 x lo-' cm'/g. seg2<br />

Escribir un programa que lea la masa <strong>de</strong><br />

dos cuerpos y la distancia <strong>en</strong>tre ellos y a<br />

continuación obt<strong>en</strong>ga la fuerza gravitacional<br />

<strong>en</strong>tre ella. La salida <strong>de</strong>be ser <strong>en</strong> dinas; un dina<br />

es igual a g>: cdseg'.<br />

4.16. La famosa ecuación <strong>de</strong> Einstein para<br />

conversión <strong>de</strong> una masa in <strong>en</strong> <strong>en</strong>ergía vi<strong>en</strong>e<br />

dada por la fórmula<br />

E = cm'<br />

c es la velocidad <strong>de</strong> la luz<br />

c= 2.997925 x 10'"cdsg<br />

Escribir un programa que lea una masa <strong>en</strong><br />

gramos y obt<strong>en</strong>ga la cantidad <strong>de</strong> <strong>en</strong>ergía<br />

producida cuando la masa se convierte <strong>en</strong><br />

<strong>en</strong>ergía.<br />

Nota: Si la masa se da <strong>en</strong> gramos, la fórmula<br />

produce le <strong>en</strong>ergía <strong>en</strong> ergios.<br />

4.17. La relación <strong>en</strong>tre los lados (a,b) <strong>de</strong> un triángulo<br />

y la hipot<strong>en</strong>usa (h) vi<strong>en</strong>e dada por la fórmula<br />

a' + b = h2


Operadores y expresiones 141<br />

Escribir un programa que lea la longitud <strong>de</strong><br />

los lados y calcule la hipot<strong>en</strong>usa.<br />

4.18. Escribir un programa que lea la hora <strong>de</strong> un día<br />

<strong>de</strong> notación <strong>de</strong> 24 horas y la respuesta <strong>en</strong><br />

notación <strong>de</strong> 12 horas. Por ejemplo, si la <strong>en</strong>trada<br />

es 13:45, la salida será<br />

1:45 PM<br />

El programa pedirá al usuario que introduzca<br />

exactam<strong>en</strong>te cinco caracteres. Así, por ejemplo,<br />

las nueve <strong>en</strong> punto se introduce como<br />

o9 : O0<br />

4.19. Escribir un programa que lea el radio <strong>de</strong> un<br />

círculo y a continuación visualice: área <strong>de</strong>l<br />

círculo, diámetro <strong>de</strong>l círculo y longitud <strong>de</strong> la<br />

circunfer<strong>en</strong>cia <strong>de</strong>l círculo.<br />

4.20. Escribir un programa que <strong>de</strong>temine si un año<br />

es bisiesto. Un año es bisiesto si es múltiplo<br />

<strong>de</strong> 4 (por ejemplo, 1984). Sin embargo, los<br />

años múltiplos <strong>de</strong> 100 sólo son bisiestos<br />

cuando a la vez son múltiples <strong>de</strong> 400 (por<br />

ejemplo, 1800 no es bisiesto, mi<strong>en</strong>tras que<br />

2000 si lo será).<br />

4.21. Construir un programa que indique si un<br />

número introducido por teclado es positivo,<br />

igual a cero, o negativo, utilizar para hacer la<br />

selección el operador ? : .


CAPíTULO 5<br />

ESTRUCTURAS DE SELECCIÓN:<br />

SENTENCIAS IF Y SWITCH<br />

CONTENIDO<br />

5.1. Estructuras <strong>de</strong> control.<br />

cc.<br />

5.2. La s<strong>en</strong>t<strong>en</strong>cia if.<br />

5.3. S<strong>en</strong>t<strong>en</strong>cia if <strong>de</strong> dos alternativas:<br />

if -else.<br />

5.4. S<strong>en</strong>t<strong>en</strong>cias if -else anidadas.<br />

5.5. S<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> control switch.<br />

5.6. Expresiones condicionales:<br />

el operador ?:.<br />

5.7. Evaluación <strong>en</strong> cortocircuito<br />

<strong>de</strong> expresiones lógicas.<br />

5.8. Puesta a punto <strong>de</strong> programas.<br />

5.9. Errores frecu<strong>en</strong>tes<br />

<strong>de</strong> programación.<br />

5.1 O. Resum<strong>en</strong>.<br />

5.10. Ejercicios.<br />

5.12. Problemas.<br />

142


I<br />

INTRODUCCI~N<br />

Los programas <strong>de</strong>finidos hasta este punto se ejecutan <strong>de</strong> modo secu<strong>en</strong>cial, es<br />

<strong>de</strong>cir, una s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>spués <strong>de</strong> otra. La ejecución comi<strong>en</strong>za con la primera<br />

s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> la función y prosigue hasta la Última s<strong>en</strong>t<strong>en</strong>cia, cada una <strong>de</strong> las<br />

cuales se ejecuta una sola vez. Esta forma <strong>de</strong> programación es a<strong>de</strong>cuada para<br />

resolver problemas s<strong>en</strong>cillos. Sin embargo, para la resolución <strong>de</strong> problemas <strong>de</strong><br />

tipo g<strong>en</strong>eral se necesita la capacidad <strong>de</strong> controlar cuáles son las s<strong>en</strong>t<strong>en</strong>cias que<br />

se ejecutan, <strong>en</strong> qué mom<strong>en</strong>tos. Las <strong>estructura</strong>s o construcciones <strong>de</strong> control<br />

controlan la secu<strong>en</strong>cia o flujo <strong>de</strong> ejecución <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias. Las <strong>estructura</strong>s<br />

<strong>de</strong> control se divid<strong>en</strong> <strong>en</strong> tres gran<strong>de</strong>s categorías <strong>en</strong> función <strong>de</strong>l flujo <strong>de</strong><br />

ejecución: secu<strong>en</strong>cia, selección y repetición.<br />

Este capítulo consi<strong>de</strong>ra las <strong>estructura</strong>s selectivas o condicionales -s<strong>en</strong>t<strong>en</strong>cias<br />

if y switch- que controlan si una s<strong>en</strong>t<strong>en</strong>cia o lista <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias se<br />

ejecutan <strong>en</strong> función <strong>de</strong>l cumplimi<strong>en</strong>to o no <strong>de</strong> una condición.<br />

c<br />

CONCEPTOS CLAVE<br />

Estructura <strong>de</strong> control.<br />

S<strong>en</strong>t<strong>en</strong>cia <strong>en</strong>um.<br />

Estructura <strong>de</strong> control selectiva. S<strong>en</strong>t<strong>en</strong>cia if.<br />

S<strong>en</strong>t<strong>en</strong>cia break.<br />

S<strong>en</strong>t<strong>en</strong>cia switch.<br />

S<strong>en</strong>t<strong>en</strong>cia compuesta. Tipo lógico <strong>en</strong> C.


144 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

5.1. ESTRUCTURAS DE CONTROL<br />

Las <strong>estructura</strong>s <strong>de</strong> control controlan el flujo <strong>de</strong> ejecución <strong>de</strong> un programa o función. Las <strong>estructura</strong>s<br />

<strong>de</strong> control permit<strong>en</strong> combinar instrucciones o s<strong>en</strong>t<strong>en</strong>cias individuales <strong>en</strong> una simple unidad lógica con<br />

un punto <strong>de</strong> <strong>en</strong>trada y un punto <strong>de</strong> salida.<br />

Las instrucciones o s<strong>en</strong>t<strong>en</strong>cias se organizan <strong>en</strong> tres tipos <strong>de</strong> <strong>estructura</strong>s <strong>de</strong> control que sirv<strong>en</strong> para<br />

controlar el flujo <strong>de</strong> la ejecución: secu<strong>en</strong>cia, selección (<strong>de</strong>cisión) y repetición. Hasta este mom<strong>en</strong>to sólo<br />

se ha utilizado el flujo secu<strong>en</strong>cial. Una s<strong>en</strong>t<strong>en</strong>cia compuesta es un conjunto <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias <strong>en</strong>cerradas<br />

<strong>en</strong>tre llaves ({ y 1) que se utiliza para especificar un flujo secu<strong>en</strong>cial.<br />

,<br />

{<br />

s<strong>en</strong>t<strong>en</strong>cia ;<br />

s<strong>en</strong>t<strong>en</strong>cia ;<br />

1<br />

s<strong>en</strong>t<strong>en</strong>cia ;<br />

El control fluye <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>ciu, a la .s<strong>en</strong>t<strong>en</strong>cia2 y así sucesivam<strong>en</strong>te. Sin embargo, exist<strong>en</strong> problemas<br />

que requier<strong>en</strong> etapas con dos o más opciones o alternativas a elegir <strong>en</strong> función <strong>de</strong>l valor <strong>de</strong> una condición<br />

+ o expresión.<br />

5.2. LA SENTENCIA if<br />

En C, la <strong>estructura</strong> <strong>de</strong> control <strong>de</strong> selección principal es una s<strong>en</strong>t<strong>en</strong>cia if. La s<strong>en</strong>t<strong>en</strong>cia if ti<strong>en</strong>e dos<br />

alternativas o formatos posibles. El formato más s<strong>en</strong>cillo ti<strong>en</strong>e la sintaxis sigui<strong>en</strong>te:<br />

i f (Expresión) Acr’ión<br />

\<br />

Expresión lógica que <strong>de</strong>termina<br />

SI la acción se ha <strong>de</strong> ejecutar<br />

Acción se ejecuta si la expresión<br />

lógica es verda<strong>de</strong>ra<br />

La s<strong>en</strong>t<strong>en</strong>cia i f funciona <strong>de</strong> la sigui<strong>en</strong>te manera. Cuando se alcanza la s<strong>en</strong>t<strong>en</strong>cia if d<strong>en</strong>tro <strong>de</strong> un<br />

programa, se evalúa la expresión <strong>en</strong>tre paréntesis que vi<strong>en</strong>e a continuación <strong>de</strong> if. Si Expresión es<br />

verda<strong>de</strong>ra, se ejecuta Acción; <strong>en</strong> caso contrario no se ejecuta Acción (<strong>en</strong> su formato más simple,<br />

Acción es una s<strong>en</strong>t<strong>en</strong>cia simple y <strong>en</strong> los restantes formatos es una s<strong>en</strong>t<strong>en</strong>cia compuesta). En cualquier<br />

caso la ejecución <strong>de</strong>l programa continúa con la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>l programa. La Figura 5.1 muestra<br />

un diagrama <strong>de</strong>flujo que indica el flujo <strong>de</strong> ejecución <strong>de</strong>l programa.<br />

Otro sistema <strong>de</strong> repres<strong>en</strong>tar la s<strong>en</strong>t<strong>en</strong>cia i f es:<br />

if (condición) s<strong>en</strong>t<strong>en</strong>cia;<br />

condi ci Ón<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

es una expresión <strong>en</strong>tera(1ógica).<br />

es cualquier s<strong>en</strong>t<strong>en</strong>cia ejecutable, que se ejecutará sólo si la condición toma<br />

un valor distinto <strong>de</strong> cero.


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias I f y S W t ~ c.h 145<br />

verda<strong>de</strong>ra<br />

falsa<br />

Figura 5.1. Diagrama <strong>de</strong> flujo <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia básica i f<br />

Ejemplo 5.1<br />

Prueba <strong>de</strong> divisibilidad<br />

#inclu<strong>de</strong> <br />

int main( )<br />

i<br />

int n, d;<br />

printf ( "Introduzca dos <strong>en</strong>teros: ");<br />

scanf ("%d%d",&n,&d);<br />

if (n%d == O) printf (" %d es divisible por %d\n",n,d);<br />

return O;<br />

1<br />

Int <strong>en</strong>teros: 36 4<br />

36 r4<br />

Este programa lee dos números <strong>en</strong>teros y comprueba cuál es el valor <strong>de</strong>l resto <strong>de</strong> la división n <strong>en</strong>tre<br />

d (n%d). Si el resto es cero, n es divisible por d (<strong>en</strong> nuestro caso 36 es divisible por 4, ya que 36 : 4 = 9<br />

y el resto es O).<br />

Ejemplo 5.2<br />

Repres<strong>en</strong>tar la superación <strong>de</strong> un exam<strong>en</strong> (Nota > = 5, Aprobado).<br />

if (Nota >= 5)<br />

verda<strong>de</strong>ra puts("Aprobad0") ;<br />

Imprimir<br />

aprobado<br />

4<br />

falsa


146 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

#inclu<strong>de</strong> <br />

void main()<br />

{<br />

1<br />

float numero;<br />

/* comparar número introducido por usuario */<br />

printf("1ntroduzca un número positivo o negativo: ' I ) ;<br />

scanf ('%f",&numero) ;<br />

/* comparar número con cero */<br />

if (numero > O)<br />

printf ("%f es mayor que cero\n" ,numero) ;<br />

La ejecución <strong>de</strong> este programa produce<br />

Introduzca un número positivo o negativo: 10.15<br />

10.15 es mayor que cero<br />

Si <strong>en</strong> lugar <strong>de</strong> introducir un número positivo se introduce un número negativo ¿Qué suce<strong>de</strong>?: nada.<br />

El programa es tan simple que sólo pue<strong>de</strong> comprobar si el número es mayor que cero.<br />

\ '<br />

d<br />

Ejemplo 5.3<br />

#inclu<strong>de</strong> <br />

void main()<br />

{<br />

float numero;<br />

/* comparar número introducido por usuario */<br />

printf("1ntroduzca un número positivo o negativo: ");<br />

scanf ("%f",&numero);<br />

/* comparar número */<br />

if (numero > O)<br />

printf ("%f es mayor que cero\n",numero) ;<br />

if (numero < O)<br />

printf ("%f es m<strong>en</strong>or que cero\n",numero) ;<br />

if (numero == O)<br />

printf ("%f es igual a cero\n",numero) ;<br />

1<br />

Este programa simplem<strong>en</strong>te aña<strong>de</strong> otra s<strong>en</strong>t<strong>en</strong>cia i f que comprueba si el número introducido es<br />

m<strong>en</strong>or que cero. Realm<strong>en</strong>te, una tercera s<strong>en</strong>t<strong>en</strong>cia if se aña<strong>de</strong> también y comprueba si el número es<br />

igual a cero.<br />

Ejercicio 5.1<br />

Visualizar la tarifa <strong>de</strong> la luz según el gasto <strong>de</strong> corri<strong>en</strong>te eléctrica. Para un gasto m<strong>en</strong>or <strong>de</strong> 1.000Kwxh<br />

la tarifa es 1.2, <strong>en</strong>tre 1.OOOy I.850Kwxh es 1.0 y mayor <strong>de</strong> 1.XSOKwxh 0.9.<br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine TARIFA1 1.2<br />

#<strong>de</strong>fine TARIFA2 1.0<br />

#<strong>de</strong>fine TARIFA3 O. 9


--in<br />

int main()<br />

i<br />

float gasto, tasa;<br />

printf ("\n Gasto <strong>de</strong> corri<strong>en</strong>te: ") ;<br />

scanf ("%f", &gasto);<br />

if (gasto < 1000.0)<br />

tasa = TARIFA1;<br />

if (gasto >=lOOO.O && gasto 1850.0)<br />

tasa = TARIFA3;<br />

I<br />

Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias 1 t y c w 1 t c-h 147<br />

printf("\nTasa que le correspon<strong>de</strong> a %.lf Kwxh es <strong>de</strong> %f\n",<br />

gasto,tasa);<br />

return O;<br />

En el ejercicio se <strong>de</strong>ci<strong>de</strong> <strong>en</strong>tre tres rangos la tasa que le correspon<strong>de</strong>. Se ha resuelto con tres<br />

selecciones simples.<br />

5.3. SENTENCIA if DE DOS ALTERNATIVAS: if -else<br />

Un segundo formato <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia i f es la s<strong>en</strong>t<strong>en</strong>cia if -else. Este formato <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia if ti<strong>en</strong>e<br />

la sigui<strong>en</strong>te sintaxis:<br />

if (Expresión)<br />

Acción else Acción<br />

1<br />

Expresión lógica que<br />

Acción que se ejecuta<br />

<strong>de</strong>termina la acción Acción que se realiza si la expresión lógica<br />

a ejecutar<br />

si la expresión lógica<br />

es verda<strong>de</strong>ra<br />

es falsa<br />

En este formato Acción y Acción son individualm<strong>en</strong>te, o bi<strong>en</strong> una Única s<strong>en</strong>t<strong>en</strong>cia que termina<br />

<strong>en</strong> un punto y coma (;) o un grupo <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias <strong>en</strong>cerrado <strong>en</strong>tre llaves. Cuando se ejecuta la s<strong>en</strong>t<strong>en</strong>cia<br />

if -else, se evalúa Expresión. Si Expresión es verda<strong>de</strong>ra, se ejecuta Acción y <strong>en</strong> caso contrario<br />

se ejecuta Acción . La Figura 5.2 muestra la semántica <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia if -else.<br />

1<br />

Acción,<br />

verda<strong>de</strong>ra<br />

Acción,<br />

falsa<br />

Figura 5.2. Diagrama <strong>de</strong> flujo <strong>de</strong> la repres<strong>en</strong>tación <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia if -else.


F-- -- -=<br />

- -_<br />

- -~ - ____ __<br />

148 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplos<br />

1.if (salario > IOOOOO)<br />

salario-neto = salario - impuestos;<br />

else<br />

salario-neto = salario;<br />

Si salario es mayor que 100.000 se calcula el salario neto, restándole los impuestos; <strong>en</strong> caso<br />

contrario (else) el salario neto es igual al salario (bruto).<br />

2. if (Nota >= 5)<br />

puts ("Aprobado');<br />

else<br />

puts ("susp<strong>en</strong>so");<br />

Si Nota es mayor o igual que 5 se escribe Aprobado; <strong>en</strong> caso contrario, Nota m<strong>en</strong>or que 5, se escribe<br />

Susp<strong>en</strong>so.<br />

Formatos<br />

if ( expresi ón-1 ógi ca ) 2.<br />

s <strong>en</strong> t <strong>en</strong> ci a<br />

3.1 if (expresión-lógica) s<strong>en</strong>t<strong>en</strong>cia I<br />

4. I if (expresión-lógica) s<strong>en</strong>t<strong>en</strong>cia else s<strong>en</strong>t<strong>en</strong>cia I<br />

if (expresión-lógica)<br />

s<strong>en</strong>t<strong>en</strong>cia,<br />

else<br />

s<strong>en</strong>t <strong>en</strong>cia,<br />

Si expresión 1 ógi ca es verda<strong>de</strong>ra se ejecuta s<strong>en</strong>t<strong>en</strong>cia o bi<strong>en</strong> s<strong>en</strong>t <strong>en</strong>cia, , si es falsa (sino,<br />

<strong>en</strong> caso contrario) se ejecuta s<strong>en</strong>t<strong>en</strong>cia ,.<br />

Ejemplos<br />

1. if (x > 0.0)<br />

producto = producto * x;<br />

2. if (x != 0.0)<br />

producto = producto * x;<br />

/* Se ejecuta la s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> asignación cuando x no es igual a O.<br />

<strong>en</strong> este caso producto se multiplica por x y el nuevo valor se<br />

guarda <strong>en</strong> producto reemplazando el valor antiguo.<br />

Si x es igual a O, la multiplicación no se ejecuta.<br />

*/<br />

Ejemplo 5.4<br />

Prueba <strong>de</strong> divisibilidad (igual que el Ejemplo 5.1, al que se ha añadido la cláusula else)<br />

#inclu<strong>de</strong> <br />

int main()


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias I f y sw1 t ch 149<br />

I<br />

1<br />

int n, d;<br />

print f ( "Introduzca dos <strong>en</strong>teros : " ) ;<br />

scanf ("%d%d",&n,&d);<br />

if (n%d ==O)<br />

printf ("%d es divisible por %d\n',n,d);<br />

else<br />

printf ("%d no es divisible por %d\n",n,d) ;<br />

return O;<br />

Ejscucián<br />

Introduzca dos <strong>en</strong>teros 36 5<br />

Com<strong>en</strong>tario<br />

36 no es divisible por 5 ya que 36 dividido <strong>en</strong>tre 5 produce un resto <strong>de</strong> 1 (n%d == O, es falsa, y se<br />

ejecuta la cláusula else).<br />

Ejemplo 5.5<br />

Calcular el mayor <strong>de</strong> dos números leídos <strong>de</strong>l teclado y visualizarlo <strong>en</strong> pantalla.<br />

#inclu<strong>de</strong> <br />

int main( )<br />

i<br />

int x, y;<br />

printf ( "Introduzca dos <strong>en</strong>teros: 'I);<br />

scanf ("%d%d",&x,&y);<br />

if (x > y)<br />

printf ("%6d\n",x) ;<br />

else<br />

printf ("%6d\n",y) ;<br />

return O;<br />

1<br />

Com<strong>en</strong>tario<br />

La condición es (x > y). Si x es mayor que y, la condición es «verda<strong>de</strong>ra» (true) y se evalúa a 1 ; <strong>en</strong> caso<br />

contrario la condición es «falsa» (false) y se evalúa a O. De este modo se imprime x (<strong>en</strong> un campo <strong>de</strong><br />

ancho 6, %6d) cuando es mayor que y, como <strong>en</strong> el ejemplo <strong>de</strong> la ejecución.


150 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 5.6<br />

Duda la función.f(x), calcular lu función puru un vulor dudo <strong>de</strong> x y visualizarlo <strong>en</strong> pantalla<br />

I<br />

x - x para x 50.0<br />

f (x) =<br />

-x i 3x para x >O<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main()<br />

i<br />

float f,x;<br />

printf("\n Elige un valor <strong>de</strong> x: ");<br />

scanf ("%f", &x);<br />

/* selección <strong>de</strong>l rango <strong>en</strong> que se <strong>en</strong>cu<strong>en</strong>tra x */<br />

if (x


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias J f y :;w~ t ~ ‘11 151<br />

Ejemplo 5.7<br />

/* increm<strong>en</strong>tar contadores <strong>de</strong> números positivos, números negativos o<br />

ceros */<br />

if (x > O)<br />

num-pos = num-pos + 1;<br />

else<br />

I<br />

num-ceros = num-ceros + 1;<br />

I J I<br />

La s<strong>en</strong>t<strong>en</strong>cia if anidada ti<strong>en</strong>e tres alternativas. Se increm<strong>en</strong>ta una <strong>de</strong> las tres variables (num-pos ,<br />

num-neg y num-ceros) <strong>en</strong> 1, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> que x sea mayor que cero, m<strong>en</strong>or que cero o igual a<br />

cero, respectivam<strong>en</strong>te. Las cajas muestran la <strong>estructura</strong> lógica <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia if anidada; la segunda<br />

s<strong>en</strong>t<strong>en</strong>cia if es la acción o tarea falsa (a continuación <strong>de</strong> else) <strong>de</strong> la primera s<strong>en</strong>t<strong>en</strong>cia if.<br />

La ejecución <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia if anidada se realiza como sigue: se comprueba la primera condición<br />

(x > O); si es verda<strong>de</strong>ra, numjos se increm<strong>en</strong>ta <strong>en</strong> 1 y se salta el resto <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia if. Si la primera<br />

condición es falsa, se comprueba la segunda condición (x < O); si es verda<strong>de</strong>ra num-neg se increm<strong>en</strong>ta<br />

<strong>en</strong> uno; <strong>en</strong> caso contrario se increm<strong>en</strong>ta nun]-ceros <strong>en</strong> uno. Es importante consi<strong>de</strong>rar que la segunda<br />

condición se comprueba sólo si la primera condición es falsa.<br />

5.4.1. Sangría <strong>en</strong> las s<strong>en</strong>t<strong>en</strong>cias if anidadas<br />

El formato muitibifurcación se compone <strong>de</strong> una serie <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias if anidadas, que se pued<strong>en</strong> escribir<br />

<strong>en</strong> cada línea una s<strong>en</strong>t<strong>en</strong>cia if. La sintaxis myltibifurcación anidada es:<br />

Formato I:<br />

Formato 2:<br />

if (expresión-lógica)<br />

s <strong>en</strong> t<strong>en</strong> ci a<br />

else<br />

if (expresión-lógica)<br />

else<br />

if (expresión-lógica)<br />

s<strong>en</strong> t<strong>en</strong> ci a<br />

else<br />

if (expresión-lógica)<br />

s<strong>en</strong>t <strong>en</strong>ci a<br />

else<br />

s<strong>en</strong> t<strong>en</strong> ci a<br />

if (expresión-lógica)<br />

s<strong>en</strong> t<strong>en</strong> ci a<br />

else if (expresión-lógica)<br />

s<strong>en</strong> t <strong>en</strong> ci a<br />

else if (expresión-lógica)<br />

s<strong>en</strong> t <strong>en</strong>ci a<br />

else if (expresión-lógica)<br />

s<strong>en</strong> t <strong>en</strong>ci a<br />

else<br />

s<strong>en</strong>t <strong>en</strong>cia<br />

Ejemplos<br />

1. if (x > O)<br />

z = 2*log(x);<br />

else<br />

if (y > O)


152 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

z = sqrt(x) + sqrt(y);<br />

2. if (x > O)<br />

z = 2*log(x) ;<br />

else if (y > O)<br />

z = sqrt (x) + sqrt (y);<br />

else<br />

puts ( "\n *** Imposible calcular 2") ;<br />

Ejemplo 5.8<br />

El sigui<strong>en</strong>te programa realiza selecciones múltiples con la s<strong>en</strong>t<strong>en</strong>cia compuestas if else.<br />

#inclu<strong>de</strong> <br />

void main()<br />

1<br />

float numero;<br />

printf( I' introduzca un número positivo o negativo: ");<br />

scanf ("%f", &numero);<br />

/* comparar número con cero */<br />

if (numero > O)<br />

I<br />

printf ("%.2f%s", numero, "es mayor que cero\n") ;<br />

puts( "pruebe <strong>de</strong> nuevo introduci<strong>en</strong>do un número negativo");<br />

I<br />

else if (numero < O)<br />

t<br />

printf ("%.2f%su',<br />

numero, 'les m<strong>en</strong>or que cero\n") ;<br />

puts( "pruebe <strong>de</strong> nuevo introduci<strong>en</strong>do un número negativo");<br />

I<br />

else<br />

{ printf ("%.2f%s", numero, 'les igual a cero\n") ;<br />

puts ( " ¿por qué no introduce otro número? ");<br />

I<br />

5.4.2. Comparación <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias if anidadas y secu<strong>en</strong>cias <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias if<br />

Los programadores ti<strong>en</strong><strong>en</strong> dos alternativas: 1) usar una secu<strong>en</strong>cia <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias if; 2) una Única<br />

s<strong>en</strong>t<strong>en</strong>cia if anidada. Por ejemplo, la s<strong>en</strong>t<strong>en</strong>cia i f <strong>de</strong>l Ejemplo 5.7 se pue<strong>de</strong> reescribir como la sigui<strong>en</strong>te<br />

secu<strong>en</strong>cia <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias if.<br />

if (x > O)<br />

numsos = num-pos + 1;<br />

if (x < O)<br />

num-neg = num-neg + 1;<br />

if ( x == O)<br />

num-ceros = num-ceros + 1;


P<br />

Estructuras <strong>de</strong> selección: S<strong>en</strong>t<strong>en</strong>cias i i y swi t cti 153<br />

Aunque la secu<strong>en</strong>cia anterior es lógicam<strong>en</strong>te equival<strong>en</strong>te a la original, no es tan legible ni efici<strong>en</strong>te.<br />

Al contrario que la s<strong>en</strong>t<strong>en</strong>cia i f anidada, la secu<strong>en</strong>cia no muestra claram<strong>en</strong>te cual es la s<strong>en</strong>t<strong>en</strong>cia a<br />

ejecutar para un valor <strong>de</strong>terminado <strong>de</strong> x. Con respecto a la efici<strong>en</strong>cia, la s<strong>en</strong>t<strong>en</strong>cia if anidada se ejecuta<br />

más rápidam<strong>en</strong>te cuando x es positivo ya que la primera condición (x > o) es verda<strong>de</strong>ra, lo que significa<br />

que la parte <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia i f a continuación <strong>de</strong>l primer e i s e se salta. En contraste, se comprueban<br />

siempre las tres condiciones <strong>en</strong> la secu<strong>en</strong>cia <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias if. Si x es negativa, se comprueban dos<br />

condiciones <strong>en</strong> las s<strong>en</strong>t<strong>en</strong>cias if anidadas fr<strong>en</strong>te a las tres condiciones <strong>de</strong> las secu<strong>en</strong>cias <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias<br />

if. Una <strong>estructura</strong> típica if -else anidada permitida es:<br />

if (numero > O)<br />

{<br />

...<br />

1<br />

else<br />

{<br />

1<br />

if ( ...)<br />

I<br />

...<br />

1<br />

else<br />

I<br />

1<br />

...<br />

if ( ... )<br />

I<br />

...<br />

1<br />

Ejercicio 5.9<br />

Exist<strong>en</strong> difer<strong>en</strong>tes formas <strong>de</strong> escribir s<strong>en</strong>t<strong>en</strong>cias if anidadas.<br />

1. if (a > O) if (b > O) ++a; else if (c > O)<br />

if (a < 5) ++b; else if (b < 5) ++c; else --a;<br />

else if (c < 5) --b; else --c; else a = O;<br />

2. if (a > O) /* forma más legible */<br />

if (b > O) ++a;<br />

else<br />

if (c > O)<br />

if (a < 5) ++b;<br />

else<br />

if (b < 5) ++e;<br />

else --a;<br />

else<br />

if (c < 5) --b;<br />

else --c;<br />

else<br />

a = O;<br />

3. if (a > O) /* forma más legible */


154 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

if (b > O) ++a;<br />

else if (c > O)<br />

if (a < 5) ++b;<br />

else if (b < 5) ++c;<br />

else --a;<br />

else if (c < 5) --b;<br />

else --c;<br />

else<br />

a = O;<br />

Ejercicio 5.10<br />

Calcular el mayor <strong>de</strong> tres números <strong>en</strong>teros.<br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

int a, b, c, mayor;<br />

printf ("\nIntroduzca tres <strong>en</strong>teros :" ) ;<br />

scanf ("%d%d %d",&a,&b,&c);<br />

if (a > b)<br />

if (a > c) mayor = a;<br />

else mayor = c;<br />

else<br />

if (b > c) mayor = b;<br />

else mayor = c;<br />

print f ( "El mayor es %d \n",mayor);<br />

return O;<br />

i<br />

Ejecución<br />

Introduzca tres <strong>en</strong>teros: 77 54 85<br />

El mayor es 85<br />

Análisis<br />

Al ejecutar el primer if, la condición (a > b) es verda<strong>de</strong>ra, <strong>en</strong>tonces se ejecuta la segunda i f. En el<br />

segundo if la condición (a > c) es falsa, <strong>en</strong> consecu<strong>en</strong>cia el primer else mayor = 85 y se termina<br />

la s<strong>en</strong>t<strong>en</strong>cia if, a continuación se ejecuta la última línea y se visualiza ~i mayor es 85.<br />

5.5. SENTENCIA DE CONTROL switch<br />

La s<strong>en</strong>t<strong>en</strong>cia switch es una s<strong>en</strong>t<strong>en</strong>cia C que se utiliza para seleccionar una <strong>de</strong> <strong>en</strong>tre múltiples alternativas.<br />

La s<strong>en</strong>t<strong>en</strong>cia switch es especialm<strong>en</strong>te Útil cuando la selección se basa <strong>en</strong> el valor <strong>de</strong> una<br />

variable simple o <strong>de</strong> una expresión simple d<strong>en</strong>ominada expresicín <strong>de</strong> control o selector. El valor <strong>de</strong> esta<br />

expresión pue<strong>de</strong> ser <strong>de</strong> tipo int o char, pero no <strong>de</strong> tipo float ni double.


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias I t y SWI t ch 155<br />

Sintaxis<br />

i<br />

switch (selector)<br />

t<br />

case etiqueta, : s<strong>en</strong>t<strong>en</strong>cias,;<br />

case etiqueta, : s<strong>en</strong>t<strong>en</strong>cias,;<br />

case etiqueta, : s<strong>en</strong>t<strong>en</strong>cias,;<br />

<strong>de</strong>fault: s<strong>en</strong>t<strong>en</strong>cias,;<br />

/* opcional. */<br />

La expresión <strong>de</strong> control o se1 ector se evalúa y se compara con cada una <strong>de</strong> las etiquetas <strong>de</strong> case.<br />

La expresión selector <strong>de</strong>be ser un tipo ordinal (por ejemplo, int , char, pero no float o string).<br />

Cada etiqueta es un valor Único, constante y cada etiqueta <strong>de</strong>be t<strong>en</strong>er un valor difer<strong>en</strong>te <strong>de</strong> los otros.<br />

Si el valor <strong>de</strong> la expresión selector es igual a una <strong>de</strong> las etiquetas case -por ejemplo, etiqueta -<br />

<strong>en</strong>tonces la ejecución com<strong>en</strong>zará con la primera s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> la secu<strong>en</strong>cia s<strong>en</strong>t<strong>en</strong>cia y continuará<br />

hasta que se <strong>en</strong>cu<strong>en</strong>tra el final <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> control sw I tch, o hasta <strong>en</strong>contrar la s<strong>en</strong>t<strong>en</strong>cia break.<br />

Es habitual que <strong>de</strong>spués <strong>de</strong> cada bloque <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias correspondi<strong>en</strong>te a una secu<strong>en</strong>cia se <strong>de</strong>see terminar<br />

la ejecución <strong>de</strong>l switch; para ello se sitúa la s<strong>en</strong>t<strong>en</strong>cia break como Última s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>l bloque.<br />

break hace que siga la ejecución <strong>en</strong> la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia ai switch.<br />

Sintaxis con break<br />

switch ( select or)<br />

{<br />

case etiqueta, : s<strong>en</strong>t<strong>en</strong>cias,;<br />

breaki<br />

case etiqueta, : s<strong>en</strong>t<strong>en</strong>cias,;<br />

break ;<br />

case etiqueta, : s<strong>en</strong>t<strong>en</strong>cias,;<br />

break;<br />

<strong>de</strong>fault: s<strong>en</strong>t<strong>en</strong>cias,;<br />

/* opcional */<br />

El tipo <strong>de</strong> cada etiqueta <strong>de</strong>be ser el mismo que la expresión <strong>de</strong> se1 ector. Las expresiones están<br />

permitidas como etiquetas pero sólo si cada operando <strong>de</strong> la expresión es por sí misma una constante<br />

-por ejemplo, 4 + 8 o bi<strong>en</strong> m * 15-, siempre que m hubiera sido <strong>de</strong>finido anteriorm<strong>en</strong>te como<br />

constante con nombre.<br />

Si el valor <strong>de</strong>l selector no está listado <strong>en</strong> ninguna etiqueta case, no se ejecutará ninguna <strong>de</strong> las<br />

opciones a m<strong>en</strong>os que se especifique una acción por <strong>de</strong>fecto (omisión). La omisión <strong>de</strong> una etiqueta<br />

<strong>de</strong>fault pue<strong>de</strong> crear un error lógico difícil <strong>de</strong> prever. Aunque la etiqueta <strong>de</strong>fault es opcional, se<br />

recomi<strong>en</strong>da su uso a m<strong>en</strong>os que se esté absolutam<strong>en</strong>te seguro <strong>de</strong> que todos los valores <strong>de</strong> sekctor estén<br />

incluidos <strong>en</strong> las etiquetas case.


1 156 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Una s<strong>en</strong>t<strong>en</strong>cia break consta <strong>de</strong> la palabra reservada break seguida por un punto y coma. Cuando<br />

la computadora ejecuta las s<strong>en</strong>t<strong>en</strong>cias sigui<strong>en</strong>tes a una etiqueta case, continúa hasta que se alcanza una<br />

s<strong>en</strong>t<strong>en</strong>cia break. Si la computadora <strong>en</strong>cu<strong>en</strong>tra una s<strong>en</strong>t<strong>en</strong>cia break, termina la s<strong>en</strong>t<strong>en</strong>cia switch. Si<br />

se omit<strong>en</strong> las s<strong>en</strong>t<strong>en</strong>cias break, <strong>de</strong>spués <strong>de</strong> ejecutar el código <strong>de</strong> case, la computadora ejecutará el<br />

código que sigue a la sigui<strong>en</strong>te case.<br />

Ejemplo 5.1 1<br />

switch (opcion)<br />

{<br />

case O:<br />

puts ("Cero!");<br />

break;<br />

case 1:<br />

puts ("Uno! 'I ) ;<br />

break;<br />

case 2:<br />

puts('Dos!');<br />

break;<br />

<strong>de</strong>fault:<br />

puts ("Fuera <strong>de</strong> rango") ;<br />

1<br />

!<br />

~~~~<br />

Ejemplo 5.12<br />

switch (opcion)<br />

{<br />

1<br />

case O:<br />

case 1:<br />

case 2:<br />

puts ("M<strong>en</strong>or <strong>de</strong> 3") ;<br />

break;<br />

case 3:<br />

puts( "Igual a 3 ");<br />

break;<br />

<strong>de</strong>fault:<br />

puts ("Mayor que 3") ;<br />

Ejemplo 5.13<br />

Comparación <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias if -else-if y swi t ch. Se necesita saber si un <strong>de</strong>terminado carácter<br />

car es una vocal. Solución con if-else-if.<br />

if ((car == 'a') I I (car == 'A'))<br />

printf ( "%c es una vocal\n",car) ;<br />

else if ((car == 'e') 1 1 (car == 'E'))<br />

printf ( "%c es una vocal\n",car) ;<br />

else if ((car == 'i') 1 1 (car == '1'))<br />

printf ( "%c es una vocal\n",car) ;


-157<br />

else if ((car == 'o') I I (car == 'O'))<br />

printf ( "%c es una vocal\n", car) ;<br />

else if ((car == 'u') 1 I (car == 'U'))<br />

printf ( "%c es una vocal\n", car) ;<br />

else<br />

printf (<br />

Solución con s wi t ch.<br />

switch (car) {<br />

I ,<br />

case a<br />

I ,<br />

case e<br />

case 'i'<br />

case 'o'<br />

case 'u'<br />

print f<br />

break;<br />

I<br />

<strong>de</strong>fault<br />

printf<br />

"%c no es una vocal\n",car) ;<br />

case 'A' :<br />

case 'E ' :<br />

case '1':<br />

case 'O ' :<br />

case 'u':<br />

II o,<br />

oc es una vocal\n",car) ;<br />

II o,<br />

oc no es una vocal\n", car) ;<br />

Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias if y swi t ch<br />

Ejemplo 5.15<br />

Dada una nota <strong>de</strong> un exam<strong>en</strong> mediante un código escribir el literal que le correspon<strong>de</strong> a la nota.<br />

/* Programa resuelto con la s<strong>en</strong>t<strong>en</strong>cia switch */<br />

#inclu<strong>de</strong> <br />

int main()<br />

I<br />

char nota;<br />

printf("1ntroduzca calificación (A-F) y pulse Intro:");<br />

scanf (%c",&nota);<br />

I<br />

switch (nota)<br />

{<br />

case 'A' : puts ( "Excel<strong>en</strong>te. Exam<strong>en</strong> superado") ;<br />

break;<br />

case 'B': puts ( "Notable. Sufici<strong>en</strong>cia") ;<br />

break;<br />

case 'C ' : puts ( "Aprobado" ) ;<br />

break;<br />

case 'D ' :<br />

case 'F' :<br />

puts ("Susp<strong>en</strong>dido");<br />

break;<br />

<strong>de</strong>fault:<br />

puts ("No es posible esta nota") ;<br />

I<br />

puts ( "Final <strong>de</strong> programa" ;<br />

return O;<br />

Cuando se ejecuta la s<strong>en</strong>t<strong>en</strong>cia switch, se evalúa nota; si el valor <strong>de</strong> la expresión es igual al valor<br />

<strong>de</strong> una etiqueta, <strong>en</strong>tonces se transfiere el flujo <strong>de</strong> control a las s<strong>en</strong>t<strong>en</strong>cias asociadas con la etiqueta


~ _____~<br />

158 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

correspondi<strong>en</strong>te. Si ninguna etiqueta coinci<strong>de</strong> con el valor <strong>de</strong> nota se ejecuta la s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>fault y<br />

las s<strong>en</strong>t<strong>en</strong>cias que vi<strong>en</strong><strong>en</strong> <strong>de</strong>trás <strong>de</strong> ella. Normalm<strong>en</strong>te la última s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias que vi<strong>en</strong><strong>en</strong><br />

<strong>de</strong>spués <strong>de</strong> una case es una s<strong>en</strong>t<strong>en</strong>cia break. Esta s<strong>en</strong>t<strong>en</strong>cia hace que el flujo <strong>de</strong> control <strong>de</strong>l programa<br />

salte a la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> switch. Si no existiera break, se ejecutm'an también las s<strong>en</strong>t<strong>en</strong>cias<br />

restantes <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia switch.<br />

Ejecución <strong>de</strong> prueba I<br />

Introduzca calificación (A-F) y pulse Intro: A<br />

Excel<strong>en</strong>te. Exam<strong>en</strong> superado<br />

Final <strong>de</strong> programa<br />

Ejecución <strong>de</strong> prueba 2<br />

introduzca calificación (A-F) y pulse Intro: B<br />

Notable. Sufici<strong>en</strong>cia<br />

Final <strong>de</strong> programa<br />

Ejecución <strong>de</strong> prueba 3<br />

Introduzca calificación (A-F) y pulse Intro: E<br />

No es posible esta nota<br />

Final <strong>de</strong> programa<br />

Preeaución<br />

Si se olvida break <strong>en</strong> una s<strong>en</strong>t<strong>en</strong>cia switch, el compilador no emitirá un m<strong>en</strong>saje <strong>de</strong> error<br />

ya que se habrá escrito una s<strong>en</strong>t<strong>en</strong>cia switch correcta sintácticam<strong>en</strong>te pero no realizará las<br />

tareas previstas.<br />

Ejemplo 5.15<br />

Seleccionar un tipo <strong>de</strong> vehículo según un valor numérico.<br />

int tipo-vehiculo;<br />

printf ("Introduzca tipo <strong>de</strong> vehiculo: ") ;<br />

scanf ( "%di', &tipo-vehículo) ;<br />

switch(tipo-vehículo)<br />

{<br />

case 1:<br />

printf ("turismo\n") ;<br />

peaje = 500;<br />

break; + Si se omite esta break el vehículo primero será turismo<br />

case 2:<br />

y luego autobús<br />

printf ("autobus\n");<br />

peaje = 3000;<br />

break ;<br />

case 3:<br />

pr in t f ( 'Irno t oc i c 1 et a \ n " ) ;<br />

peaje = 300;<br />

break;<br />

<strong>de</strong>fault:<br />

printf ("vehículo no autorizado\n") ;<br />

i


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias 1 f y swl t d l 159<br />

Cuando la computadora comi<strong>en</strong>za a ejecutar un case no termina la ejecución <strong>de</strong>l switch hasta<br />

que se <strong>en</strong>cu<strong>en</strong>tra, o bi<strong>en</strong> una s<strong>en</strong>t<strong>en</strong>cia break, o bi<strong>en</strong> la Última s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>l switch.<br />

5.5.1. Caso particular <strong>de</strong> case<br />

Está permitido t<strong>en</strong>er varias expresiones case <strong>en</strong> una alternativa dada d<strong>en</strong>tro <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia switch. Por<br />

ejemplo, se pue<strong>de</strong> escribir:<br />

switch(c) {<br />

case '0':case '1': case '2': case '3': case '4':<br />

case '5':case '6': case '7': case '8': case '9':<br />

num-digitos++; /*se increm<strong>en</strong>ta <strong>en</strong> 1 el valor <strong>de</strong> num-digitos */<br />

break;<br />

I,<br />

case : case '\t': case '\n':<br />

num-blancos++; /*se increm<strong>en</strong>ta <strong>en</strong> 1 el valor <strong>de</strong> num-blancos*/<br />

break;<br />

<strong>de</strong>fault:<br />

num-distintos++;<br />

1<br />

5.5.2. Uso <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias switch <strong>en</strong> m<strong>en</strong>ús<br />

La s<strong>en</strong>t<strong>en</strong>cia i f -else es más versátil que la s<strong>en</strong>t<strong>en</strong>cia switch y se pue<strong>de</strong> utilizar unas s<strong>en</strong>t<strong>en</strong>cias if -<br />

else anidadas o multi<strong>de</strong>cisión, <strong>en</strong> cualquier parte que se utiliza una s<strong>en</strong>t<strong>en</strong>cia case. Sin embargo,<br />

normalm<strong>en</strong>te, la s<strong>en</strong>t<strong>en</strong>cia switch es más clara. Por ejemplo, la s<strong>en</strong>t<strong>en</strong>cia switch es idónea para<br />

implem<strong>en</strong>tar m<strong>en</strong>ús.<br />

Un m<strong>en</strong>ú <strong>de</strong> un restaurante pres<strong>en</strong>ta una lista <strong>de</strong> alternativas para que un cli<strong>en</strong>te elija <strong>en</strong>tre sus<br />

difer<strong>en</strong>tes opciones. Un m<strong>en</strong>ú <strong>en</strong> un programa <strong>de</strong> computadora hace la misma función: pres<strong>en</strong>tar una<br />

lista <strong>de</strong> alternativas <strong>en</strong> la pantalla para que el usuario elija una <strong>de</strong> ellas.<br />

En los capítulos sigui<strong>en</strong>tes se volverá a tratar el tema <strong>de</strong> los m<strong>en</strong>ús <strong>en</strong> programación con ejemplos<br />

prácticos.<br />

5.6. EXPRESIONES CONDICIONALES: EL OPERADOR ? :<br />

Las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> selección (if y switch) consi<strong>de</strong>radas hasta ahora, son similares a las s<strong>en</strong>t<strong>en</strong>cias<br />

previstas <strong>en</strong> otros l<strong>en</strong>guajes, tales como Pascal y Fortran 90. C ti<strong>en</strong>e un tercer mecanismo <strong>de</strong> selección,<br />

una expresión que produce uno <strong>de</strong> dos valores, resultado <strong>de</strong> una expresión lógica o booleana (también<br />

d<strong>en</strong>ominada condición). Este mecanismo se d<strong>en</strong>omina expresión condicional. Una expresión<br />

condicional ti<strong>en</strong>e el formato C ? A : B y es realm<strong>en</strong>te una operación ternaria (tres operandos) <strong>en</strong> el<br />

que c , A y B son los tres operandos y ? : es el operador.<br />

Sintaxis<br />

condición ? expresión, : expresión,<br />

condi ci ón<br />

expresión /expresión<br />

es una expresión lógica<br />

son expresiones compatibles <strong>de</strong> tipos


160 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Se evalúa condición, si el valor <strong>de</strong> condición es verda<strong>de</strong>ra (distinto <strong>de</strong> cero) <strong>en</strong>tonces se<br />

<strong>de</strong>vuelve como resultado el valor <strong>de</strong> expresión ; si el valor <strong>de</strong> condición es falsa (cero) se <strong>de</strong>vuelve<br />

como resultado el valor <strong>de</strong> expresión .<br />

Uno <strong>de</strong> los medios más s<strong>en</strong>cillos <strong>de</strong>l operador condicional (? : ) es utilizar el operador condicional<br />

y llamar a una <strong>de</strong> dos funciones.<br />

Ejemplos<br />

1. Selecciona con el operador ? : la ejecución <strong>de</strong> una función u otra.<br />

a == b ? funcionlo : funcion20;<br />

es equival<strong>en</strong>te a la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia:<br />

if (a == b)<br />

funcionl ();<br />

else<br />

funcion2 ();<br />

2. El operador ? : se utiliza <strong>en</strong> el sigui<strong>en</strong>te segm<strong>en</strong>to <strong>de</strong> código para asignar el m<strong>en</strong>or <strong>de</strong> dos valores<br />

<strong>de</strong> <strong>en</strong>trada a m<strong>en</strong>or.<br />

int <strong>en</strong>tradal, <strong>en</strong>trada2;<br />

int m<strong>en</strong>or;<br />

scanf ( "%d %d" , &<strong>en</strong>tradal, &<strong>en</strong>trada2 ) ;<br />

m<strong>en</strong>or = <strong>en</strong>tradal n2)<br />

printf ("%d > %d",nl,n2);<br />

else<br />

printf ("%d n2 ? printf("%d > %d",nl,n2): printf("%d


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias 1 r y i w1 ti ti 161<br />

I<br />

1:<br />

I<br />

1<br />

5.7. EVALUACIÓN EN CORTOCIRCUITO DE EXPRESIONES LÓGICAS<br />

Cuando se evalúan expresiones lógicas <strong>en</strong> C se pue<strong>de</strong> emplear una técnica d<strong>en</strong>ominada evaluación <strong>en</strong><br />

cortocircuito. Este tipo <strong>de</strong> evaluación significa que se pue<strong>de</strong> <strong>de</strong>t<strong>en</strong>er la evaluación <strong>de</strong> una expresión<br />

lógica tan pronto como su valor pueda ser <strong>de</strong>terminado con absoluta certeza. Por ejemplo, si el valor <strong>de</strong><br />

(soltero == ’s ’) es falso, la expresión lógica (soltero == ’s’) &&(sexo = ’h’) hh (edad<br />

> 18) && (edad = O) && (y > 1)<br />

se evalúa <strong>en</strong> cortocircuito ya que x >= O será falso y, por tanto, el valor final <strong>de</strong> la expresión será falso.<br />

En el caso <strong>de</strong>l operador I I se produce una situación similar. Si la primera <strong>de</strong> las dos expresiones unidas<br />

por el operador I t es verda<strong>de</strong>ra, <strong>en</strong>tonces la expresión completa es verdu<strong>de</strong>ru, con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia<br />

<strong>de</strong> que el valor <strong>de</strong> la segunda expresión sea verda<strong>de</strong>ro o fulso. La razón es que el operador OR produce<br />

resultado verda<strong>de</strong>ro si el primer operando es verda<strong>de</strong>ro.<br />

Otros l<strong>en</strong>guajes, distintos <strong>de</strong> C, utilizan evaluación completa. En evaluación completa, cuando dos<br />

expresiones se un<strong>en</strong> por un símbolo && o 1 1 , se evalúan siempre ambas expresiones y a continuación se<br />

utilizan las tablas <strong>de</strong> verdad <strong>de</strong> && o bi<strong>en</strong> I I para obt<strong>en</strong>er el valor <strong>de</strong> la expresión final.<br />

Ejemplo 5.18<br />

Si x es cero, la condición<br />

if ((x != 0.0) && (y/x > 7.5))<br />

es falsa ya que (x ! = O. o) es falsa. Por consigui<strong>en</strong>te, no hay necesidad <strong>de</strong> evaluar la expresión (y/.<br />

> 7 .5) cuando x sea cero, <strong>de</strong> utilizar evaluación completa se produciría un error <strong>en</strong> tiempo <strong>de</strong> ejecución.<br />

Sin embargo, si altera el ord<strong>en</strong> <strong>de</strong> las expresiones, al evaluar el compilador la s<strong>en</strong>t<strong>en</strong>cia if<br />

If ((y/x > 7.5) && (x != 0.0))<br />

se produciría un error <strong>en</strong> tiempo <strong>de</strong> ejecución <strong>de</strong> división por cero (“division by zero” 1.<br />

El ord<strong>en</strong> <strong>de</strong> las expresiones con operadores && y I I pue<strong>de</strong> ser crítico <strong>en</strong> <strong>de</strong>terminadas<br />

situaciones.


162 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

5.8. PUESTA A PUNTO DE PROGRAMAS<br />

Estilo y diseño<br />

1. El estilo <strong>de</strong> escritura <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia if e i f -el se es el sangrado <strong>de</strong> las difer<strong>en</strong>tes líneas <strong>en</strong> el<br />

formato sigui<strong>en</strong>te:<br />

if (expresión-lógica)<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

else<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

if ( expresión-1 ógi ca)<br />

i<br />

s<strong>en</strong> t <strong>en</strong>c 1 d<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

1<br />

else<br />

i<br />

s<strong>en</strong> t <strong>en</strong>c i il<br />

I<br />

s<strong>en</strong> t<strong>en</strong>ci d<br />

1<br />

En el caso G, s<strong>en</strong>t<strong>en</strong>cias if -else- I t utilimdas para implem<strong>en</strong>tar una <strong>estructura</strong> <strong>de</strong> selección<br />

multialternativa se suele escribir <strong>de</strong> la sigui<strong>en</strong>te forma:<br />

if (expresión-lógica )<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

else if (expresión-lógicd )<br />

s<strong>en</strong> t<strong>en</strong> ci a<br />

else if (expresión-lógica 1<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

else<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

2. Una construcción <strong>de</strong> selección múltiple se pue<strong>de</strong> implem<strong>en</strong>tar más efici<strong>en</strong>tem<strong>en</strong>te con una<br />

estmctura 1 t-else- 1 f que con una secu<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes If. Por ejemplo:<br />

printf ('introduzca nota") ;<br />

scanf ("%dd", &nota);<br />

if (nota < 0 1 I nota > 100)<br />

i<br />

print€(" %d no es una nota válida.\n",nota);<br />

return'? ';<br />

i<br />

if ((nota >= 90) && (nota = 80) && (nota < 90))<br />

return 'B ';<br />

if ((nota >=70) && (nota < 80))


Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias I f y SWI ti-h 163<br />

return 'C';<br />

if ((nota >= 60) && (nota < 70))<br />

return 'D';<br />

if (nota < 60)<br />

return 'F ' ;<br />

Con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia <strong>de</strong>l valor <strong>de</strong> nota se ejecutan todas las s<strong>en</strong>t<strong>en</strong>cias if; 5 <strong>de</strong> las expresiones<br />

lógicas son expresiones compuestas, <strong>de</strong> modo que se ejecutan 16 operaciones con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia <strong>de</strong><br />

la nota introducida. En contraste, las s<strong>en</strong>t<strong>en</strong>cias i f anidadas reduc<strong>en</strong> consi<strong>de</strong>rablem<strong>en</strong>te el número<br />

<strong>de</strong> operaciones a realizar (3 a 7), todas las expresiones son simples y no se evalúan todas ellas<br />

siempre.<br />

printf ("Introduzca nota") ;<br />

scanf ("%d", &nota);<br />

if (nota < 0 I I nota > 100)<br />

I<br />

printf ("%d no es una nota válida. \n",nota);<br />

return '?';<br />

I<br />

else if (nota >= 90)<br />

return 'A,;<br />

else if (nota >= 80)<br />

return 'B ' ;<br />

else if (nota >= 70)<br />

return 'C';<br />

else if (nota >= 60)<br />

return 'D';<br />

else<br />

return 'F';<br />

5.9. ERRORES FRECUENTES DE PROGRAMACIÓN<br />

I. Uno <strong>de</strong> los errores más comunes <strong>en</strong> una s<strong>en</strong>t<strong>en</strong>cia i f es utilizar un operador <strong>de</strong> asignación (=)<br />

<strong>en</strong> lugar <strong>de</strong> un operador <strong>de</strong> igualdad (==).<br />

2. En una s<strong>en</strong>t<strong>en</strong>cia i f anidada, cada cláusula el se se correspon<strong>de</strong> con la i f preced<strong>en</strong>te más<br />

cercana. Por ejemplo, <strong>en</strong> el segm<strong>en</strong>to <strong>de</strong> programa sigui<strong>en</strong>te<br />

if (a > O)<br />

if (b > O)<br />

c=a+b;<br />

else<br />

c = a + abs(b) ;<br />

d=a*b*c;<br />

¿Cuál es la s<strong>en</strong>t<strong>en</strong>cia i f asociada a el se?<br />

El sistema más fácil para evitar errores es el sangrado o ind<strong>en</strong>tación, con lo que ya se aprecia<br />

que la cláusula else se correspon<strong>de</strong> a la s<strong>en</strong>t<strong>en</strong>cia que conti<strong>en</strong>e la condición b > o<br />

if (a > O)<br />

if (b > O)<br />

c=a+b;<br />

else<br />

c = a + abs(b) ;<br />

d=a*b*c;


164 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

3. Las comparaciones con operadores == <strong>de</strong> cantida<strong>de</strong>s algebraicam<strong>en</strong>te iguales pued<strong>en</strong> producir<br />

una expresión lógica falsa, <strong>de</strong>bido u que la mayoría <strong>de</strong> los números reales no se almac<strong>en</strong>an<br />

exactam<strong>en</strong>te. Por ejemplo, aunque las expresiones reales sigui<strong>en</strong>tes son equival<strong>en</strong>tes:<br />

a * (l/a)<br />

1.0<br />

son algebraicam<strong>en</strong>te iguales, la expresión<br />

a * (l/a) == 1.0<br />

pue<strong>de</strong> ser falsa <strong>de</strong>bido a que a es real.<br />

F<br />

4. Cuando <strong>en</strong> una s<strong>en</strong>t<strong>en</strong>cia swi tch o <strong>en</strong> un bloque <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias falta una <strong>de</strong> las llaves ( {, 1)<br />

aparece un m<strong>en</strong>saje <strong>de</strong> error tal como:<br />

Error ... : Cumpound statem<strong>en</strong>t missing } in funct-ion<br />

Si no se ti<strong>en</strong>e cuidado con la pres<strong>en</strong>tación <strong>de</strong> la escritura <strong>de</strong>l código, pue<strong>de</strong> ser muy difícil<br />

localizar la llave que falta.<br />

5. El selector <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia swl t ch <strong>de</strong>be ser <strong>de</strong> tipo <strong>en</strong>tero o compatible <strong>en</strong>tero. Así las<br />

constantes reales<br />

2.4, -4.5, 3.1416<br />

no pued<strong>en</strong> ser utilizadas <strong>en</strong> el selector.<br />

6. Cuando se utiliza una s<strong>en</strong>t<strong>en</strong>cia swi t ch, asegúrese que el selector <strong>de</strong> swi t ch y las etiquetas<br />

case son <strong>de</strong>l mismo tipo (in t , char pero no f 1 oat). Si el selector se evalúa a un valor no<br />

listado <strong>en</strong> ninguna <strong>de</strong> las etiquetas case, la s<strong>en</strong>t<strong>en</strong>cia swi t ch no gestionará ninguna acción;<br />

por esta causa se suele poner ma etiqueta <strong>de</strong>fdul t pura resolver este problema.<br />

5.10. RESUMEN<br />

S<strong>en</strong>t<strong>en</strong>cia if<br />

Una alternativa<br />

resultado = a/b;<br />

Dos alternativas<br />

if (a >= O)<br />

f = 5*cos(a*pi/l80.);<br />

f = -2*sin(a*pi/180.) + 0.5;<br />

Múltiples alternativas<br />

if (x < O)<br />

{<br />

puts ( "Negativo") ;<br />

absx = -x;<br />

else if (x == O)<br />

i<br />

puts ( "Cero" ) ;<br />

abs-x = O;<br />

else<br />

{<br />

puts ("positivo") ;<br />

abs-x = x;<br />

1


7<br />

Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias L t y Awl t cii 165<br />

I<br />

S<strong>en</strong>t<strong>en</strong>cia switch<br />

I<br />

l.<br />

case 'A' : case 'a ':<br />

puts ("Sobresali<strong>en</strong>te");<br />

break;<br />

case 'B': case 'b':<br />

puts ("Notable');<br />

break;<br />

case 'C': case 'c ':<br />

puts ("Aprobado");<br />

break;<br />

case 'D': case 'd':<br />

puts (BSusp<strong>en</strong>so'') ;<br />

break;<br />

<strong>de</strong>fault:<br />

puts ("nota no válida") ;<br />

J<br />

5.1 I. EJERCICIOS<br />

5.1. ¿Qué errores <strong>de</strong> sintaxis ti<strong>en</strong>e la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia?<br />

if x > 25.0<br />

y=x<br />

else<br />

y = z;<br />

5.2. ¿Qué valor se asigna a consumo <strong>en</strong> la s<strong>en</strong>t<strong>en</strong>cia<br />

if sigui<strong>en</strong>te si velocidad es 120?<br />

if (velocidad > 80)<br />

consumo = 10.00;<br />

else if (velocidad > 100)<br />

consumo = 12.00;<br />

else if (velocidad > 120)<br />

consumo = 15.00;<br />

5.3. Explique las difer<strong>en</strong>cias <strong>en</strong>tre las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong><br />

la columna <strong>de</strong> la izquierda y <strong>de</strong> la columna <strong>de</strong> la<br />

<strong>de</strong>recha. Para cada una <strong>de</strong> ellas <strong>de</strong>ducir el valor<br />

final <strong>de</strong> x si el valor inicial <strong>de</strong> x es O.<br />

if (x >= O) if (x >= O)<br />

x++ ; X++ ;<br />

else if (x >= 1); if (x >= 1)<br />

X+= 2; x+= 2;<br />

5.4. ¿Qué salida producirá el código sigui<strong>en</strong>te, cuando<br />

se empotra <strong>en</strong> un programa completo y<br />

primera-opcion vale l?¿Ysiprimera-opcion<br />

vale 2?<br />

int primera-opcion;<br />

switch (primera-opcion + 1)<br />

t<br />

case 1:<br />

puts ("Cor<strong>de</strong>ro asado") ;<br />

break;<br />

case 2:<br />

puts ("Chuleta lechal") ;<br />

break;<br />

case 3:<br />

puts ("Chuletón");<br />

1<br />

case 4:<br />

puts ("Postre<br />

<strong>de</strong> Pastel") ;<br />

break;<br />

<strong>de</strong>fault:<br />

puts ("Bu<strong>en</strong> apetito") ;


- -<br />

166 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

5.5.<br />

5.6.<br />

5.7.<br />

5.8.<br />

5.9.<br />

5.10.<br />

5.11.<br />

LQué salida producirá el sigui<strong>en</strong>te código,<br />

cuando se empotra <strong>en</strong> un programa completo?<br />

int x = 2;<br />

puts ("Arranque") ;<br />

if (x 3)<br />

if (x != O)<br />

puts ("Hola <strong>de</strong>s<strong>de</strong> el segundo<br />

if .'I) ;<br />

else<br />

puts ("Hola <strong>de</strong>s<strong>de</strong> el else. ");<br />

puts ("De nuevo fin" ) ;<br />

Escribir una s<strong>en</strong>t<strong>en</strong>cia i f - e 1 se que visualice<br />

la palabra Alta si el valor <strong>de</strong> la variable nota<br />

es mayor que 100 y Baja si el valor <strong>de</strong> esa<br />

nota es m<strong>en</strong>or que 100.<br />

¿Qué hay <strong>de</strong> incorrecto <strong>en</strong> el sigui<strong>en</strong>te código?<br />

if (x = O) printf("%d = O\n",x);<br />

else printf ("%d != O\n",x) ;<br />

¿Cuál es el error <strong>de</strong>l sigui<strong>en</strong>te código?<br />

if (x < y < z) printf('%d < %d <<br />

%d\n",x,y, z) ;<br />

¿Cuál es el error <strong>de</strong> este código?<br />

printf ("Introduzca n:") ;<br />

scanf ("%d", &n);<br />

if (n < O)<br />

puts('Este número es negativo.<br />

Pruebe <strong>de</strong> nuevo.") ;<br />

scanf ("%d", &n);<br />

else<br />

printf ("conforme. n =%d\n",n) ;<br />

Escribir un programa que lea tres <strong>en</strong>teros y<br />

emita un m<strong>en</strong>saje que indique si están o no <strong>en</strong><br />

ord<strong>en</strong> numérico.<br />

Escribir una s<strong>en</strong>t<strong>en</strong>cia i f -e 1 se que clasifique<br />

un <strong>en</strong>tero n <strong>en</strong> una <strong>de</strong> las sigui<strong>en</strong>tes categorías<br />

y escriba un m<strong>en</strong>saje a<strong>de</strong>cuado:<br />

5.12.<br />

5.13.<br />

5.14.<br />

5.15.<br />

5.16.<br />

5.17.<br />

x < O o bi<strong>en</strong> o I x I 100<br />

o bi<strong>en</strong> x > 100<br />

Escribir un programa que introduzca el número<br />

<strong>de</strong> un mes (1 a 12) y visualice el número <strong>de</strong><br />

días <strong>de</strong> ese mes.<br />

Se trata <strong>de</strong> escribir un programa que clasifique<br />

<strong>en</strong>teros leídos <strong>de</strong>l teclado <strong>de</strong> acuerdo a los<br />

sigui<strong>en</strong>tes puntos:<br />

0 si el <strong>en</strong>tero es 30 o mayor, o negativo,<br />

visualizar un m<strong>en</strong>saje <strong>en</strong> ese s<strong>en</strong>tido;<br />

<strong>en</strong> caso contrario, si es un nuevo primo,<br />

pot<strong>en</strong>cia <strong>de</strong> 2, o un número compuesto,<br />

visualizar el m<strong>en</strong>saje correspondi<strong>en</strong>te;<br />

si son cero o 1, visualizar 'cero'o 'unidad'.<br />

Escribir un programa que <strong>de</strong>termine el mayor<br />

<strong>de</strong> tres números.<br />

El domingo <strong>de</strong> Pascua es el primer domingo<br />

<strong>de</strong>spués <strong>de</strong> la primera luna ll<strong>en</strong>a posterior al<br />

equinoccio <strong>de</strong> primavera, y se <strong>de</strong>termina<br />

mediante el sigui<strong>en</strong>te cálculo s<strong>en</strong>cillo:<br />

A = año mod 19<br />

B = año mod 4<br />

C = año mod 7<br />

D = (19 * A + 24) mod 30<br />

E = (2 * B + 4 * C + 6 * D + 5)<br />

mod 7<br />

N = (22 + D + E)<br />

Don<strong>de</strong> N indica el número <strong>de</strong> día <strong>de</strong>l mes <strong>de</strong><br />

marzo (si N es igual o m<strong>en</strong>or que 3) o abril (si<br />

es mayor que 31). Construir un programa que<br />

<strong>de</strong>termine fechas <strong>de</strong> domingos <strong>de</strong> Pascua.<br />

Codificar un programa que escriba la calificación<br />

correspondi<strong>en</strong>te a una nota, <strong>de</strong> acuerdo<br />

con el sigui<strong>en</strong>te criterio:<br />

O a ~5.0 Susp<strong>en</strong>so<br />

5 a ~6.5 Aprobado<br />

6.5 a c 8.5 Notable<br />

8.5 a c 10 Sobresali<strong>en</strong>te<br />

10 Matrícula <strong>de</strong> honor.<br />

Determinar si el carácter asociado a<br />

introducido por teclado correspon<strong>de</strong> a un<br />

carácter alfabético, dígito, <strong>de</strong> puntuación,<br />

especial o no impnmible.


~<br />

T<br />

Estructuras <strong>de</strong> selección: s<strong>en</strong>t<strong>en</strong>cias L t y swl t ih 167<br />

5.12. PROBLEMAS<br />

5.1.<br />

5.2.<br />

5.3.<br />

5.4.<br />

5.5.<br />

Cuatro <strong>en</strong>teros <strong>en</strong>tre O y 100 repres<strong>en</strong>tan las puntuaciones<br />

<strong>de</strong> un estudiante <strong>de</strong> un curso <strong>de</strong> informática.<br />

Escribir un programa para <strong>en</strong>contrar la<br />

media <strong>de</strong> estas puntuaciones y visualizar una<br />

tabla <strong>de</strong> notas <strong>de</strong> acuerdo al sigui<strong>en</strong>te cuadro:<br />

Media<br />

90-100<br />

80-89<br />

70-79<br />

0-59<br />

Puntuación<br />

Escribir un programa que lea la hora <strong>de</strong> un día<br />

<strong>de</strong> notación <strong>de</strong> 24 horas y la respuesta <strong>en</strong><br />

notación <strong>de</strong> 12 horas. Por ejemplo, si la <strong>en</strong>trada<br />

es 13:45, la salida será<br />

1:45 PM<br />

El programa pedirá al usuario que introduzca<br />

exactam<strong>en</strong>te cinco caracteres. Así, por<br />

ejemplo, las nueve <strong>en</strong> punto se introduce como<br />

09:oo<br />

Escribir un programa que acepte fechas escritas<br />

<strong>de</strong> modo usual y las visualice como tres<br />

números. Por ejemplo, la <strong>en</strong>trada<br />

15, Febrero 1989<br />

producirá la salida<br />

15 2 1989<br />

Escribir un programa que acepte un número <strong>de</strong><br />

tres dígitos escrito <strong>en</strong> palabra y a continuación<br />

los visualice como un valor <strong>de</strong> tipo <strong>en</strong>tero. La<br />

<strong>en</strong>trada se termina con un punto. por ejemplo,<br />

la <strong>en</strong>trada<br />

dosci<strong>en</strong>tos veinticinco<br />

producirá la salida<br />

225<br />

Escribir un programa que acepte un año escrito<br />

<strong>en</strong> cifras arábigas y visualice el año escrito <strong>en</strong><br />

números romanos, d<strong>en</strong>tro <strong>de</strong>l rango IO00 a 2000.<br />

Nota: Recuer<strong>de</strong> que V = 5 X = 10 L = 50<br />

C=100 D=500 M=1000<br />

IV=4<br />

MCM = 1900<br />

MCMLX = 1960<br />

MCMLXXXXIX = 1989<br />

I<br />

XL=40<br />

CM = 900<br />

MCML = 1950<br />

MCMXL = 1940<br />

5.6. Se <strong>de</strong>sea redon<strong>de</strong>ar un <strong>en</strong>tero positivo N a la<br />

c<strong>en</strong>t<strong>en</strong>a más próxima y visualizar la salida.<br />

Para ello la <strong>en</strong>trada <strong>de</strong> <strong>datos</strong> <strong>de</strong>be ser los cuatro<br />

dígitos A,E,C,D, <strong>de</strong>l <strong>en</strong>tero N. Por ejemplo, si<br />

A es 2, I3 es 3, C es 6 y D es 2, <strong>en</strong>tonces N será<br />

2362 y el resultado redon<strong>de</strong>ado será 2400. Si N<br />

es 2342, el resultado será 2300, y si N = 2962,<br />

<strong>en</strong>tonces el número será 3000. Diseñar el<br />

programa correspondi<strong>en</strong>te.<br />

5.7.<br />

5.8.<br />

Se quiere calcular la edad <strong>de</strong> un individuo, para<br />

ello se va a t<strong>en</strong>er como <strong>en</strong>trada dos fechas <strong>en</strong> el<br />

formato día (1 a 31), mes (1 a 12) y año (<strong>en</strong>tero<br />

<strong>de</strong> cuatro dígitos), correspondi<strong>en</strong>tes a la fecha<br />

<strong>de</strong> nacimi<strong>en</strong>to y la fecha actual, respectivam<strong>en</strong>te.<br />

Escribir un programa que calcule y<br />

visualice la edad <strong>de</strong>l individuo. Si es la fecha<br />

<strong>de</strong> un bebe (m<strong>en</strong>os <strong>de</strong> un año <strong>de</strong> edad), la edad<br />

se <strong>de</strong>be dar <strong>en</strong> meses y días; <strong>en</strong> caso contrario,<br />

la edad se calculará <strong>en</strong> años.<br />

Escribir un programa que <strong>de</strong>termine si un año<br />

es bisiesto. Un año es bisiesto si es múltiplo <strong>de</strong><br />

4 (por ejemplo, 1984). Sin embargo, los años<br />

múltiplos <strong>de</strong> 100 sólo son bisiestos cuando a la<br />

vez son múltiples <strong>de</strong> 400 (por ejemplo, 1800 no<br />

es bisiesto, mi<strong>en</strong>tras que 2000 sí lo será).<br />

5.9. Escribir un programa que calcule el número <strong>de</strong><br />

días <strong>de</strong> un mes, dados los valores numéricos <strong>de</strong>l<br />

mes y el año.<br />

5.10. Se <strong>de</strong>sea calcular el salario neto semanal <strong>de</strong> los<br />

trabajadores <strong>de</strong> una empresa <strong>de</strong> acuerdo a las<br />

sigui<strong>en</strong>tes normas:<br />

* Horas semanales trabajadas < 38 a una tasa<br />

dada.<br />

Horas extras (38 o más) a una tasa 50 por<br />

100 superior a la ordinaria.<br />

0 Impuestos O por 100, si el salario bruto es<br />

m<strong>en</strong>or o igual a 50.000 pesetas.<br />

Impuestos 10 por 100, si el salario bruto es<br />

mayor <strong>de</strong> 50.000 pesetas.<br />

5.11. Determinar el m<strong>en</strong>or número <strong>de</strong> billetes y<br />

monedas <strong>de</strong> curso legal equival<strong>en</strong>tes a cierta<br />

cantidad <strong>de</strong> pesetas (cambio óptimo).<br />

5.12. Escribir y ejecutar un programa que simule un<br />

calculador simple. Lee dos <strong>en</strong>teros y un carácter.<br />

Si el carácter es un +, se imprime la suma;<br />

si es un-, se imprime la difer<strong>en</strong>cia; si es un *,<br />

se imprime el producto; si es un /, se imprime<br />

el coci<strong>en</strong>te; y si es un % se imprime el resto.<br />

Nota: utilizar la s<strong>en</strong>t<strong>en</strong>cia switch.


CAPíTULO 6<br />

ESTRUCTURAS DE CONTROL:<br />

BUCLES<br />

1<br />

11<br />

1<br />

1<br />

CONTENlDU<br />

6.1. La s<strong>en</strong>t<strong>en</strong>cia while.<br />

ticion: el bucle for.<br />

6.3. Precauciones <strong>en</strong> el uso <strong>de</strong> for.<br />

1 6.4. Repetición: el bucle do-while. 6.10. ProblemaB.<br />

I<br />

6,s. Comparación <strong>de</strong> bucles while,<br />

€or y do-while.<br />

6.6. Diseño <strong>de</strong> bucles.<br />

6.7. Bucles anidados.<br />

6.8. Resum<strong>en</strong>.<br />

6.9. Ejercidos.<br />

6.11. Proyectos <strong>de</strong> programación.


INTRODUCCI~N<br />

Una <strong>de</strong> las características <strong>de</strong> las computadoras que aum<strong>en</strong>tan consi<strong>de</strong>rablem<strong>en</strong>te<br />

su pot<strong>en</strong>cia es su capacidad para ejecutar una tarea muchas (repetidas)<br />

veces con gran velocidad, precisión y fiabilidad. Las tareas repetitivas es algo<br />

que los humanos <strong>en</strong>contramos difíciles y tediosas <strong>de</strong> realizar. En este capítulo<br />

se estudian las <strong>estructura</strong>s <strong>de</strong> control iterativas o repetitivas que realizan la<br />

repetición o iteración <strong>de</strong> acciones. C soporta tres tipos <strong>de</strong> <strong>estructura</strong>s <strong>de</strong> control:<br />

los bucles while, for y do-while. Estas <strong>estructura</strong>s <strong>de</strong> control o s<strong>en</strong>t<strong>en</strong>cias<br />

repetitivas controlan el número <strong>de</strong> veces que una s<strong>en</strong>t<strong>en</strong>cia o listas <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias<br />

se ejecutan.<br />

CONCEPTOS CLAVE<br />

Bucle.<br />

Comparación <strong>de</strong> while, for y do.<br />

Control <strong>de</strong> bucles.<br />

Iteraciódrepetición.<br />

Optimización <strong>de</strong> bucles.<br />

S<strong>en</strong>t<strong>en</strong>cia break.<br />

S<strong>en</strong>t<strong>en</strong>cia do-while.<br />

S<strong>en</strong>t<strong>en</strong>cia for.<br />

S<strong>en</strong>t<strong>en</strong>cia whi 1 e.<br />

Terminación <strong>de</strong> un bucle.<br />

1 69<br />

A


170 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

6.1. LA SENTENCIA while<br />

Un bucle (ciclo) es cualquier construcción <strong>de</strong> programa que repite una s<strong>en</strong>t<strong>en</strong>cia o secu<strong>en</strong>cia <strong>de</strong><br />

s<strong>en</strong>t<strong>en</strong>cias un número <strong>de</strong> veces. La s<strong>en</strong>t<strong>en</strong>cia (o grupo <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias) que se repit<strong>en</strong> <strong>en</strong> un bloque se<br />

d<strong>en</strong>omina cuerpo <strong>de</strong>l bucle y cada repetición <strong>de</strong>l cuerpo <strong>de</strong>l bucle se llama iteración <strong>de</strong>l bucle. Las dos<br />

principales cuestiones <strong>de</strong> diseño <strong>en</strong> la construcción <strong>de</strong>l bucle son: ¿Cuál es el cuerpo <strong>de</strong>l bucle? ¿Cuántas<br />

veces se iterará el cuerpo <strong>de</strong>l bucle?<br />

Un bucle while ti<strong>en</strong>e una condición <strong>de</strong>l bucle (una expresión lógica) que controla la secu<strong>en</strong>cia <strong>de</strong><br />

repetición. La posición <strong>de</strong> esta condición <strong>de</strong>l bucle es <strong>de</strong>lante <strong>de</strong>l cuerpo <strong>de</strong>l bucle y significa que un<br />

bucle while es un bucle pretest <strong>de</strong> modo que cuando se ejecuta el mismo, se evalúa la condición<br />

antes <strong>de</strong> que se ejecute el cuerpo <strong>de</strong>l bucle. La Figura 6.1 repres<strong>en</strong>ta el diagrama <strong>de</strong>l bucle while.<br />

El diagrama indica que la ejecución <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia o s<strong>en</strong>t<strong>en</strong>cias expresadas se repite mi<strong>en</strong>tras la<br />

condición <strong>de</strong>l bucle permanece verda<strong>de</strong>ra y termina cuando se hace falsa. También indica el diagrama<br />

anterior que la condición <strong>de</strong>l bucle se evalúa antes <strong>de</strong> que se ejecute el cuerpo <strong>de</strong>l bucle y, por<br />

consigui<strong>en</strong>te, si esta condición es inicialm<strong>en</strong>te falsa, el cuerpo <strong>de</strong>l bucle no se ejecutará. En otras<br />

palabras, el cuerpo <strong>de</strong> un bucle while se ejecutará cero o más veces.<br />

falsa<br />

-7-<br />

T verda<strong>de</strong>ra<br />

e<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

$.<br />

Figura 6.1<br />

Sintaxis<br />

-<br />

I while (condición-bucle)<br />

S<strong>en</strong>t<strong>en</strong>cia; cuerpo<br />

2 while (condición-bucle)<br />

t<br />

s<strong>en</strong>t<strong>en</strong>cia-1;<br />

s<strong>en</strong>t<strong>en</strong>cia -2;<br />

cuerpo<br />

1<br />

s <strong>en</strong> t<strong>en</strong> ci a -n ;


while<br />

con d i ci Ón-bu cl e<br />

s<strong>en</strong>t<strong>en</strong>cia<br />

Estructuras <strong>de</strong> control: bucles 17 1<br />

es una palabra reservada C<br />

es una expresión lógica o booleana<br />

es una s<strong>en</strong>t<strong>en</strong>cia simple o compuesta<br />

El comportami<strong>en</strong>to ofuncionami<strong>en</strong>to <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia (bucle) whi i e es:<br />

1. Se evalúa la condición-bucle<br />

2. Si condición-bucle es verda<strong>de</strong>ra (distinto <strong>de</strong> cero) :<br />

a. La s<strong>en</strong>t<strong>en</strong>cia especificada, d<strong>en</strong>ominada el cuerpo <strong>de</strong>l bucle, se<br />

ejecuta.<br />

b. Vuelve el control al paso I.<br />

3. En caso contrario:<br />

El control se transfiere a la s<strong>en</strong>t<strong>en</strong>cia sigui<strong>en</strong>te al bucle o<br />

s<strong>en</strong>t<strong>en</strong>cia while.<br />

Las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l cuerpo <strong>de</strong>l bucle se repit<strong>en</strong> mi<strong>en</strong>tras que la expresión lógica (condición<br />

<strong>de</strong>l bucle) sea verda<strong>de</strong>ra. Cuando se evalúa la expresión lógica y resulta falsa, se termina y se<br />

sale <strong>de</strong>l bucle y se ejecuta la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> programa <strong>de</strong>spués <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia while.<br />

/* cu<strong>en</strong>ta hasta 10 */<br />

int x = O;<br />

while (x < 10)<br />

printf ("x: %d",x++);<br />

Ejemplo<br />

/* visualizar n asteriscos */<br />

contador = O; + inicializacicín<br />

while (contador < n) -+ pruebdcondicióti<br />

{<br />

printf (I' * ");<br />

contador++;<br />

1 /* fin <strong>de</strong> while */<br />

+ ac~tualización (increm<strong>en</strong>ta <strong>en</strong> 1 contador)<br />

La variable que repres<strong>en</strong>ta la condición <strong>de</strong>l bucle se d<strong>en</strong>omina también variable <strong>de</strong> control <strong>de</strong>l<br />

bucle <strong>de</strong>bido a que su valor <strong>de</strong>termina si el cuerpo <strong>de</strong>l bucle se repite. La variable <strong>de</strong> control <strong>de</strong>l bucle<br />

<strong>de</strong>be ser: 1) inicializada, 2) comprobada, y 3) actualizada para que el cuerpo <strong>de</strong>l bucle se ejecute<br />

a<strong>de</strong>cuadam<strong>en</strong>te. Cada etapa se resume así:<br />

1. Znicialización. Contador se establece a un valor inicial (se inicializa a cero, aunque podría ser<br />

otro su valor) antes <strong>de</strong> que se alcance la s<strong>en</strong>t<strong>en</strong>cia whi 1 e.<br />

2. Pruebdcondición. Se comprueba el valor <strong>de</strong> contador antes <strong>de</strong> que comi<strong>en</strong>ce la repetición <strong>de</strong><br />

cada bucle (d<strong>en</strong>ominada iteración o pasada).<br />

3. Actualización. Contador se actualiza (su valor se increm<strong>en</strong>ta <strong>en</strong> I, mediante el operador ++)<br />

durante cada iteración.<br />

Si la variable <strong>de</strong> control no se actualiza el bucle se ejecutará «siempre». Tal bucle se d<strong>en</strong>omina bucle<br />

infinito. En otras palabras un bucle infinito (sin terminación) se producirá cuando la condición <strong>de</strong>l bucle<br />

permanece y no se hace falsa <strong>en</strong> ninguna iteración.<br />

/* bucle infinito */<br />

contador = 1;<br />

while (contador < 100)


172 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

{<br />

I<br />

printf ("%d\n',contadorj ;<br />

contador- -;<br />

<strong>de</strong>cr<strong>en</strong>i<strong>en</strong>ta <strong>en</strong> 1 contudor<br />

contador se inicializa a 1 (m<strong>en</strong>or <strong>de</strong> 100) y como contador-- <strong>de</strong>crem<strong>en</strong>ta <strong>en</strong> 1 el valor <strong>de</strong><br />

contador <strong>en</strong> cada iteración, el valor <strong>de</strong>l contador nunca llegará a valer 100, valor necesario <strong>de</strong><br />

contador para que la condición <strong>de</strong>l bucle sea falsa. Por consigui<strong>en</strong>te, la condición contador < 1 O O<br />

siempre será verda<strong>de</strong>ra, resultando un bucle infinito, cuya salida será:<br />

1<br />

O<br />

-1<br />

-2<br />

-3<br />

-4<br />

Ejemplo<br />

/* Bucle <strong>de</strong> muestra con while */<br />

#inclu<strong>de</strong> <br />

int main0<br />

{<br />

int contador = O; /* inicializa la condición */<br />

while(contador < 5) /* condición <strong>de</strong> prueba */<br />

i<br />

contador ++; /* cuerpo <strong>de</strong>l bucle */<br />

printf ("contador: %d \n",contador);<br />

i<br />

printf("Terminado.Contador: %d \n",contadorj;<br />

return O;<br />

I<br />

Ejecución<br />

contador: 1<br />

contador : 2<br />

contador: 3<br />

contador: 4<br />

contador : 5<br />

Terminado.Contador: 5<br />

6.1 .I. Operadores <strong>de</strong> increm<strong>en</strong>to y <strong>de</strong>crem<strong>en</strong>to (++, - -1<br />

C ofrece los operadores <strong>de</strong> increm<strong>en</strong>to (+ +) y <strong>de</strong>crem<strong>en</strong>to (- -) que soporta una sintaxis abreviada para<br />

añadir (increm<strong>en</strong>tar) o restar (<strong>de</strong>crem<strong>en</strong>tar) 1 al valor <strong>de</strong> una variable. Recor<strong>de</strong>mos <strong>de</strong>l Capítulo 4 la<br />

sintaxis <strong>de</strong> ambos operadores:


Estructuras <strong>de</strong> control: bucles 173<br />

++nombrevariable<br />

nombrevariable++<br />

--nombrevariable<br />

nombrevariable--<br />

/*<br />

/*<br />

/*<br />

/*<br />

preincrem<strong>en</strong>to */<br />

postincrem<strong>en</strong>to */<br />

pre<strong>de</strong>crem<strong>en</strong>to */<br />

post<strong>de</strong>crem<strong>en</strong>to */<br />

Ejemplo 6.1<br />

Si i es una variable <strong>en</strong>tera cuyo valor es 3, las variables k e i toman los valores sucesivos que se<br />

indican <strong>en</strong> las s<strong>en</strong>t<strong>en</strong>cias sigui<strong>en</strong>tes:<br />

/* asigna el valor 3 a k y 4 a i */<br />

/* asigna el valor 5 a k y 5 a i */<br />

/* asigna el valor 5 a k y 4 a i */<br />

/* asigna el valor 3 a k y 3 a i */<br />

Ejemplo 6.2<br />

Uso <strong>de</strong>l operador <strong>de</strong> increm<strong>en</strong>to ++para controlar la iteracicín <strong>de</strong> un bucle (una <strong>de</strong> las aplicaciones más<br />

usuales <strong>de</strong> + +).<br />

/* programa cálculo <strong>de</strong> calorías */<br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

int num-<strong>de</strong>-elem<strong>en</strong>tos, cu<strong>en</strong>ta,<br />

caloriasgor-alim<strong>en</strong>to, calorias-total;<br />

printf("iCuántos alim<strong>en</strong>tos ha comido hoy? " );<br />

scanf ( "%d 'I, &num-<strong>de</strong>-elem<strong>en</strong>tos) ;<br />

calorias-total = O;<br />

cu<strong>en</strong>ta = 1;<br />

printf("1ntroducir el número <strong>de</strong> calorias <strong>de</strong> cada uno <strong>de</strong> los ");<br />

printf ("%d %s", num-elem<strong>en</strong>tos, "alim<strong>en</strong>tos tomados :\n") ;<br />

while (cu<strong>en</strong>ta++


174 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

6.1.2. Terminaciones anormales <strong>de</strong> un bucle (ciclo)<br />

Un error típico <strong>en</strong> el diseño <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia while se produce cuando el bucle sólo ti<strong>en</strong>e una s<strong>en</strong>t<strong>en</strong>cia<br />

<strong>en</strong> lugar <strong>de</strong> varias s<strong>en</strong>t<strong>en</strong>cias como se planeó. El código sigui<strong>en</strong>te<br />

contador = 1;<br />

while (contador < 25)<br />

printf ("%d\n", contador) ;<br />

contador++;<br />

visualizará infinitas veces el valor 1. Es <strong>de</strong>cir, <strong>en</strong>tra <strong>en</strong> un bucle infinito <strong>de</strong>l que nunca sale porque no<br />

se actualiza (modifica) la variable <strong>de</strong> control contador.<br />

La razón es que el punto y coma al final <strong>de</strong> la línea printf ("Bd\n", contador) ; hace que el<br />

bucle termine <strong>en</strong> ese punto y coma, aunque apar<strong>en</strong>tem<strong>en</strong>te el sangrado pueda dar la s<strong>en</strong>sación <strong>de</strong> que<br />

el cuerpo <strong>de</strong> while conti<strong>en</strong>e dos s<strong>en</strong>t<strong>en</strong>cias, printf ( ) y contador++ ; El error se hubiera <strong>de</strong>tectado<br />

rápidam<strong>en</strong>te si el bucle se hubiera escrito correctam<strong>en</strong>te<br />

contador = 1;<br />

while (contador < 25)<br />

printf ('%d\n",contador);<br />

contador++;<br />

La solución es muy s<strong>en</strong>cilla, utilizar las llaves <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia compuesta:<br />

contador = 1;<br />

while (contador < 25)<br />

1<br />

1<br />

printf ("%d\n",contador) ;<br />

contador++;<br />

6.1.3. Diseño efici<strong>en</strong>te <strong>de</strong> bucles<br />

Una cosa es analizar la operación <strong>de</strong> un bucle y otra diseñar efici<strong>en</strong>tem<strong>en</strong>te sus propios bucles. Los<br />

principios a consi<strong>de</strong>rar son: primero, analizar los requisitos <strong>de</strong> un nuevo bucle con el objetivo <strong>de</strong><br />

<strong>de</strong>terminar su inicialización, prueba (condición) y actualización <strong>de</strong> la variable <strong>de</strong> control <strong>de</strong>l bucle. El<br />

segundo es <strong>de</strong>sarrollar patrones <strong>estructura</strong>les <strong>de</strong> los bucles que se utilizan frecu<strong>en</strong>tem<strong>en</strong>te.<br />

6.1.4. Bucles while con cero iteraciones<br />

El cuerpo <strong>de</strong> un bucle no se ejecuta nunca si la prueba o condición <strong>de</strong> repetición <strong>de</strong>l bucle no se cumple,<br />

es falsa (es cero <strong>en</strong> C), cuando se alcanza while la primera vez.<br />

contador = 10;<br />

while (contador > 100)<br />

{<br />

1<br />

...<br />

El bucle anterior nunca se ejecutará ya que la condición <strong>de</strong>l bucle ( contador > 1 O O ) es falsa<br />

la primera vez que se ejecuta. El cuerpo <strong>de</strong>l bucle nunca se ejecutará.


Estructuras <strong>de</strong> control: bucles 175<br />

6.1.5. Bucles controlados por c<strong>en</strong>tinelas<br />

Normalm<strong>en</strong>te, no se conoce con exactitud cuantos elem<strong>en</strong>tos <strong>de</strong> <strong>datos</strong> se procesarán antes <strong>de</strong> com<strong>en</strong>zar<br />

su ejecución. Esto se produce bi<strong>en</strong> porque hay muchos <strong>datos</strong> a contar normalm<strong>en</strong>te o porque el número<br />

<strong>de</strong> <strong>datos</strong> a procesar <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong> cómo prosigue el proceso <strong>de</strong> cálculo.<br />

Un medio para manejar esta situación es instruir al usuario a introducir un único dato <strong>de</strong>finido y<br />

especificado d<strong>en</strong>ominado valor c<strong>en</strong>tinela como Último dato. La condición <strong>de</strong>l bucle comprueba cada<br />

dato y termina cuando se lee el valor c<strong>en</strong>tinela. El valor c<strong>en</strong>tinela se <strong>de</strong>be seleccionar con mucho cuidado<br />

y <strong>de</strong>be ser un valor que no pueda producirse como dato. En realidad el c<strong>en</strong>tinela es un valor que sirve<br />

para terminar el proceso <strong>de</strong>l bucle.<br />

En el sigui<strong>en</strong>te fragm<strong>en</strong>to <strong>de</strong> código hay un bucle con c<strong>en</strong>tinela; se introduc<strong>en</strong> notas mi<strong>en</strong>tras que<br />

ésta sea distinta <strong>de</strong> c<strong>en</strong>tinela.<br />

/*<br />

<strong>en</strong>trada <strong>de</strong> <strong>datos</strong> numéricos,<br />

c<strong>en</strong>tinela -1<br />

*/<br />

const int c<strong>en</strong>tinela = -1;<br />

printf ("Introduzca primera nota:") ;<br />

scanf ("%d", &nota);<br />

while (nota != c<strong>en</strong>tinela)<br />

{<br />

cu<strong>en</strong>ta++ ;<br />

suma += nota;<br />

printf ("Introduzca la siqui<strong>en</strong>te nota: ") ;<br />

scanf ("%d", &nota);<br />

1 /* fin <strong>de</strong> while */<br />

puts ("Final");<br />

Ejecución<br />

Si se lee el primer valor <strong>de</strong> nota, por ejemp 2 5 y luego se ejecuta, la salida poLía ser ésta:<br />

Introduzca primera nota: 25<br />

Introduzca sigui<strong>en</strong>te nota: 30<br />

Introduzca sigui<strong>en</strong>te nota: 90<br />

Introduzca sigui<strong>en</strong>te nota: -1<br />

Final<br />

/* valor <strong>de</strong>l c<strong>en</strong>tinela */<br />

6.1.6. Bucles controlados por indicadores (ban<strong>de</strong>ras)<br />

En l<strong>en</strong>guajes, como Pascal, que ti<strong>en</strong><strong>en</strong> el tipo boo1 , se utiliza una variable booleana con frecu<strong>en</strong>cia<br />

como indicadores o ban<strong>de</strong>ras <strong>de</strong> estado para controlar la ejecución <strong>de</strong> un bucle. El valor <strong>de</strong>l indicador<br />

se inicializa (normalm<strong>en</strong>te a falso "false") antes <strong>de</strong> la <strong>en</strong>trada al bucle y se re<strong>de</strong>fine (normalm<strong>en</strong>te a<br />

verda<strong>de</strong>ro "true") cuando un suceso específico ocurre d<strong>en</strong>tro <strong>de</strong>l bucle. En C no existe el tipo boolean,<br />

por lo que se utiliza como ban<strong>de</strong>ra una variable <strong>en</strong>tera que pue<strong>de</strong> tomar dos valores, 1 o O. Un bucle<br />

controlado por ban<strong>de</strong>ra-indicador se ejecuta hasta que se produce el suceso anticipado y se cambia el<br />

valor <strong>de</strong>l indicador.


176 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 6.3<br />

Se <strong>de</strong>sea leer diversos <strong>datos</strong> tipo carácter introducidos por teclado mediante un bucle whi 1 e y se <strong>de</strong>be<br />

terminar el bucle cuando se lea un duro tipo dígito (rango 'O á '9').<br />

La variable ban<strong>de</strong>ra, digito-leido se utiliza como un indicador que repres<strong>en</strong>ta cuando un dígito<br />

se ha introducido por teclado.<br />

Variable ban<strong>de</strong>ra<br />

digito-leido<br />

Significado<br />

su valor es falso (cero) antes <strong>de</strong> <strong>en</strong>trar <strong>en</strong> el bucle y mi<strong>en</strong>tras el<br />

dato leído sea un carácter y es verda<strong>de</strong>ro cuando el dato leído es<br />

un dígito.<br />

El problema que se <strong>de</strong>sea resolver es la lectura <strong>de</strong> <strong>datos</strong> carácter y la lectura <strong>de</strong>be <strong>de</strong>t<strong>en</strong>erse cuando<br />

el dato leído sea numérico (un dígito <strong>de</strong> 'O'a '9'). Por consigui<strong>en</strong>te. antes <strong>de</strong> que el bucle se ejecute y<br />

se lean los <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada, la variable digito-le ido se inicializa a falso (cero). Cuando se ejecuta<br />

el bucle, éste <strong>de</strong>be continuar ejecutándose mi<strong>en</strong>tras el dato leído sea un carácter y <strong>en</strong> consecu<strong>en</strong>cia la<br />

variable digito-leido ti<strong>en</strong>e <strong>de</strong> valor falso y se <strong>de</strong>be <strong>de</strong>t<strong>en</strong>er el bucle cuando el dato leído sea un<br />

dígito y <strong>en</strong> este caso el valor <strong>de</strong> la variable digito-leido se <strong>de</strong>be cambiar a verda<strong>de</strong>ro (uno). En<br />

consecu<strong>en</strong>cia la condición <strong>de</strong>l bucle <strong>de</strong>be ser !dig i to-leido ya que esta condición es verda<strong>de</strong>ra<br />

cuando digito-leido es falso. El bucle while sera similar a:<br />

digito-leido = O; /* no se ha leído ningún dato */<br />

while (!digito-leido)<br />

i<br />

printf ("Introduzca un carácter: " ) ;<br />

scanf ("%c",&car);<br />

digito-leido = ( ( 'O'


~~~~<br />

Estructuras <strong>de</strong> control: bucles 177<br />

2. Mi<strong>en</strong>tras la condici6n <strong>de</strong> control <strong>de</strong>l bucle sea verda<strong>de</strong>ra:<br />

2.1. Realizar las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l cuerpo <strong>de</strong>l bucle.<br />

a la condición <strong>de</strong> salida (<strong>en</strong> el ejemplo anterior que el dato<br />

) se cambia el valor <strong>de</strong> able indicador o<br />

<strong>en</strong>tonces la condición <strong>de</strong> c kga faisa y, por '<br />

3. Ejecuci6n <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias sigui<strong>en</strong>tes ai bucle.<br />

Ejemplo 6.4<br />

Se <strong>de</strong>sea leer un dato numérico x cuyo valor ha <strong>de</strong> ser mayor que cero para calcular la función f(x) =<br />

x*log(x).<br />

La variable ban<strong>de</strong>ra, x-pos i t ivo se utiliza como un indicador que repres<strong>en</strong>ta que el dato leído es<br />

mayor que cero. Por consigui<strong>en</strong>te, antes <strong>de</strong> que el bucle se ejecute y se lea el dato <strong>de</strong> <strong>en</strong>trada, la variable<br />

x-pos i t ivo se inicializa a falso (O). Cuando se ejecuta el bucle, éste <strong>de</strong>be continuar ejecutándose<br />

mi<strong>en</strong>tras el número leído sea negativo o cero y <strong>en</strong> consecu<strong>en</strong>cia la variable x_pos i t ivo t<strong>en</strong>ga el valor<br />

falso y se <strong>de</strong>be <strong>de</strong>t<strong>en</strong>er el bucle cuando el número leído sea mayor que cero y <strong>en</strong> este caso el valor <strong>de</strong> la<br />

variable xgos i t ivo se <strong>de</strong>be cambiar a verda<strong>de</strong>ro (uno). En consecu<strong>en</strong>cia la condición <strong>de</strong>l bucle <strong>de</strong>be<br />

ser !x-posit ivo ya que esta condición es verda<strong>de</strong>ra cuando xjositivo es falso. A la salida <strong>de</strong>l<br />

bucle se calcula el valor <strong>de</strong> la función y se escribe:<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main ( )<br />

i<br />

float f,x;<br />

int xgositivo;<br />

xjositivo = 0; /* inicializado a falso */<br />

while (!xgositivo)<br />

{<br />

printf ("\n Valor <strong>de</strong> x: ");<br />

scanf ("%f",&x);<br />

xgositivo = (x > 0.0); /* asigna verda<strong>de</strong>ro(1) si cumple la<br />

condición*/<br />

1<br />

f = x*log(x);<br />

printf (" f (%.If) = %.3e',x,f);<br />

return O;<br />

6.1.7. La s<strong>en</strong>t<strong>en</strong>cia break <strong>en</strong> los bucles<br />

La s<strong>en</strong>t<strong>en</strong>cia break se utiliza, a veces, para realizar una terminación anormal <strong>de</strong>l bucle. Dicho <strong>de</strong> otro<br />

modo, una terminación antes <strong>de</strong> lo previsto. Su sintaxis es:<br />

break;<br />

La s<strong>en</strong>t<strong>en</strong>cia break se utiliza para la salida <strong>de</strong> un bucle while o do-while, aunque también se pue<strong>de</strong><br />

utilizar d<strong>en</strong>tro <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia switch, si<strong>en</strong>do éste su uso más frecu<strong>en</strong>te.


178 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

while (condición)<br />

{<br />

1<br />

i f ( condí cí ón2 )<br />

break;<br />

/* s<strong>en</strong>t<strong>en</strong>cias */<br />

~<br />

Ejemplo 6.5<br />

El sigui<strong>en</strong>te código extrae y visualiza valores <strong>de</strong> <strong>en</strong>trada <strong>de</strong>s<strong>de</strong> el dispositivo estándar <strong>de</strong> <strong>en</strong>trada (stdin)<br />

hasta que se <strong>en</strong>cu<strong>en</strong>tra un valor especificado<br />

int clave = -9;<br />

int <strong>en</strong>trada;<br />

while (scanf("%da',<br />

&<strong>en</strong>trada))<br />

i<br />

if (<strong>en</strong>trada != clave)<br />

printf ("%d\n", <strong>en</strong>trada) ;<br />

else<br />

break ;<br />

1<br />

;Cómo funciona este bucle while? La función scant ( ) <strong>de</strong>vuelve el número <strong>de</strong> <strong>datos</strong> captados <strong>de</strong><br />

dispositivo <strong>de</strong> <strong>en</strong>trada o bi<strong>en</strong> cero si se ha introducido fin-<strong>de</strong>-fichero. Al <strong>de</strong>volver un valor distinto <strong>de</strong><br />

cero el bucle se ejecutaría in<strong>de</strong>finidam<strong>en</strong>te, sin embargo, cuando <strong>en</strong>trada==clave la ejecución sigue<br />

por else y la s<strong>en</strong>t<strong>en</strong>cia break que hace que la ejecución siga <strong>en</strong> la s<strong>en</strong>t<strong>en</strong>cia sigui<strong>en</strong>te al bucle while.<br />

Ejecución<br />

El uso <strong>de</strong> break <strong>en</strong> un bucle no es muy recom<strong>en</strong>dable ya que pue<strong>de</strong> hacer difícil la<br />

compr<strong>en</strong>sión <strong>de</strong>l comportami<strong>en</strong>to <strong>de</strong>l programa. En particular, suele hacer muy difícil<br />

verifícar los invariantes <strong>de</strong> los bucles. Por otra parte suele ser fácil la reescritura <strong>de</strong> los bucles<br />

sin la s<strong>en</strong>t<strong>en</strong>cia break. El bucle <strong>de</strong>l Ejemplo 6.5 escrito sin la escritura <strong>de</strong> break:<br />

int clave;<br />

int <strong>en</strong>trada;<br />

while ( (scanf("%d",&<strong>en</strong>trada) && (<strong>en</strong>trada != clave) )<br />

i<br />

printf ("%d\n", <strong>en</strong>trada) ;<br />

1<br />

6.1.8. Bucles while (true)<br />

La condición que se comprueba <strong>en</strong> un bucle whi le pue<strong>de</strong> ser cualquier expresión válida C. Mi<strong>en</strong>tras que<br />

la condición permanezca verda<strong>de</strong>ra (distinto <strong>de</strong> O), el bucle while continuará ejecutándose. Se pue<strong>de</strong><br />

crear un bucle que nunca termine utilizando el valor 1 (verda<strong>de</strong>ro) para la condición que se comprueba.<br />

1: /*Listado while (true) */<br />

2: #inclu<strong>de</strong> <br />

3: int main0


Estructuras <strong>de</strong> control: bucles 179<br />

Salida<br />

4: 1<br />

5: int flag = 1, contador = O;<br />

6 : while (flag)<br />

I: {<br />

8: contador++;<br />

9: if (contador > 10)<br />

10: break;<br />

11: 1<br />

12 : printf ( "Contador: %d\n" , contador) ;<br />

13: return O;<br />

14: 1<br />

Contador: 11<br />

Análisis<br />

En la línea 6, un bucle while se establece con una condición que nunca pue<strong>de</strong> ser falsa. El bucle<br />

increm<strong>en</strong>ta la variable contador <strong>en</strong> la línea 8, y a continuación la línea 9 comprueba si el contador es<br />

mayor que 10. Si no es así el bucle se itera <strong>de</strong> nuevo. Si contador es mayor que IO, la s<strong>en</strong>t<strong>en</strong>cia break<br />

<strong>de</strong> la línea 10 termina el bucle while, y la ejecución <strong>de</strong>l programa pasa a la línea 12.<br />

Ejercicio 6.1<br />

Calcular la media <strong>de</strong> seis números.<br />

El cálculo típico <strong>de</strong> una media <strong>de</strong> valores numéricos es: leer sucesivam<strong>en</strong>te los valores, sumarlos y<br />

dividir la suma total por el número <strong>de</strong> valores leídos. El código más simple podría ser:<br />

float numl;<br />

float num2;<br />

float num3;<br />

float num4;<br />

float num5;<br />

float num6;<br />

float media;<br />

scanf("%f %f %f %f %f %f",hnuml,&num2,hnum3,hnum4,&num4,&num5,&num6);<br />

media = (numl+num2+num3+num4+num5+num6)/6;<br />

Evid<strong>en</strong>tem<strong>en</strong>te, si <strong>en</strong> lugar <strong>de</strong> 6 valores fueran 1 .OOO, la modificación <strong>de</strong>l código no sólo sería <strong>de</strong><br />

longitud <strong>en</strong>orme sino que la labor repetitiva <strong>de</strong> escritura sería tediosa. Por ello, la necesidad <strong>de</strong> utilizar<br />

un bucle. El algoritmo más simple sería:<br />

<strong>de</strong>finir número <strong>de</strong> elem<strong>en</strong>tos como constante <strong>de</strong> valor 6<br />

Inicializar contador <strong>de</strong> números<br />

Inicializar acumulador (sumador) <strong>de</strong> números<br />

M<strong>en</strong>saje <strong>de</strong> petición <strong>de</strong> <strong>datos</strong><br />

mi<strong>en</strong>tras no estén leídos todos los <strong>datos</strong> hacer<br />

Leer número<br />

Acumular valor <strong>de</strong>l número a variable acumulador<br />

Increm<strong>en</strong>tar contador <strong>de</strong> números<br />

fin-mi<strong>en</strong>tras<br />

Calcular media (Acumulador/Total número)<br />

Visualizar valor <strong>de</strong> la media<br />

Fin


180<br />

Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El código <strong>en</strong> C es:<br />

/* Calculo <strong>de</strong> la media <strong>de</strong> seis números */<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main()<br />

{<br />

const int TotalNum = 6;<br />

int ContadorNum = O;<br />

float SumaNum = o;<br />

float media;<br />

printf("1ntroduzca %d nÚmeros\n',TotalNum);<br />

while (ContadorNum < TotalNum)<br />

{<br />

/* valores a procesar */<br />

float numero;<br />

scanf ("%f",&numero); /* leer sigui<strong>en</strong>te número */<br />

SumaNum += numero; /* añadir valor a Acumulador */<br />

++ContadorNum; /* increm<strong>en</strong>tar números leídos */<br />

1<br />

media = SumaNum/ContadorNum;<br />

print f ( "Media: %.2 f \n", media) ;<br />

return O;<br />

1<br />

I<br />

I 6.2.<br />

REPETICI~N: EL BUCLE for<br />

El bucle for <strong>de</strong> C es superior a los bucles for <strong>de</strong> otros l<strong>en</strong>guajes <strong>de</strong> programación tales como BASIC,<br />

Pascal y Fortran ya que ofrece más control sobre la inicialización y el increm<strong>en</strong>to <strong>de</strong> las variables <strong>de</strong><br />

control <strong>de</strong>l bucle.<br />

A<strong>de</strong>más <strong>de</strong>l bucle while, C proporciona otros dos tipos <strong>de</strong> bucles for y do. El bucle for que se<br />

estudia <strong>en</strong> esta sección es el más a<strong>de</strong>cuado para implem<strong>en</strong>tar bucles controlados por contador que son<br />

bucles <strong>en</strong> los que un conjunto <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias se ejecutan una vez por cada valor <strong>de</strong> un rango especificado,<br />

<strong>de</strong> acuerdo al algoritmo:<br />

por cada valor <strong>de</strong> una variable-contador <strong>de</strong> un rango especljcico: ejecutar s<strong>en</strong>t<strong>en</strong>cias<br />

La s<strong>en</strong>t<strong>en</strong>cia for (bucle for) es un método para ejecutar un bloque <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias un número fijo <strong>de</strong><br />

veces. El bucle for se difer<strong>en</strong>cia <strong>de</strong>l bucle while <strong>en</strong> que las operaciones <strong>de</strong> control <strong>de</strong>l bucle se sitúan<br />

<strong>en</strong> un solo sitio: la cabecera <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia.<br />

Sintaxis<br />

(I) Inicializa la variable<br />

<strong>de</strong> control <strong>de</strong>l bucle<br />

(2) Expresión lógica que <strong>de</strong>termina<br />

si las s<strong>en</strong>t<strong>en</strong>cias se han <strong>de</strong> ejecutar<br />

mi<strong>en</strong>tras sea verda<strong>de</strong>ra<br />

for ( Inicial i zaci ón ; Condi ci ÓnI t eraci ón ; Increm<strong>en</strong>t o)<br />

4<br />

(3) Increm<strong>en</strong>ta o <strong>de</strong>crem<strong>en</strong>ta<br />

la variable <strong>de</strong> control <strong>de</strong>l bucle<br />

s<strong>en</strong>t<strong>en</strong>cias<br />

1 ( 4 ) s<strong>en</strong>t<strong>en</strong>cias a ejecutar <strong>en</strong> cada iteración <strong>de</strong>l bucle


Estructuras <strong>de</strong> control: bucles 181<br />

El bucle for conti<strong>en</strong>e las cuatro partes sigui<strong>en</strong>tes:<br />

o Parte <strong>de</strong> inicializacicín, que inicializa la variables <strong>de</strong> control <strong>de</strong>l bucle. Se pued<strong>en</strong> utilizar variables<br />

<strong>de</strong> control <strong>de</strong>l bucle simples o múltiples.<br />

o Parte <strong>de</strong> condicicín, que conti<strong>en</strong>e una expresión lógica que hace que el bucle realice las iteraciones<br />

<strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias, mi<strong>en</strong>tras que la expresión sea verda<strong>de</strong>ra.<br />

o Parte <strong>de</strong> increm<strong>en</strong>to, que increm<strong>en</strong>ta o <strong>de</strong>crern<strong>en</strong>ta la variable o variables <strong>de</strong> control <strong>de</strong>l bucle.<br />

0 S<strong>en</strong>t<strong>en</strong>cias, acciones o s<strong>en</strong>t<strong>en</strong>cias que se ejecutarán por cada iteración <strong>de</strong>l bucle.<br />

La s<strong>en</strong>t<strong>en</strong>cia for es equival<strong>en</strong>te al sigui<strong>en</strong>te código while<br />

inicial ización;<br />

while ( condi ci Ónlt eraci ón)<br />

{<br />

1<br />

s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l bucle for;<br />

in crem<strong>en</strong> to;<br />

I<br />

~<br />

I<br />

l<br />

8<br />

Ejemplo 1<br />

int i;<br />

/* imprimir Hola 10 veces */<br />

for (i = O; i < 10; i++)<br />

printf ("Hola!");<br />

Ejemplo 2<br />

int i;<br />

for (i =<br />

{<br />

1<br />

print f<br />

print f<br />

O; i < 10; i++)<br />

"Hola!\n") ;<br />

"El valor <strong>de</strong> i es: %d", i ) ;<br />

Ejemplo 3<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine M 15<br />

#<strong>de</strong>fine f (x) exp(2*x) - x<br />

int main()<br />

i<br />

int i;<br />

double x;<br />

for (i = 1; i


182 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En este ejemplo se <strong>de</strong>fine la constante simbólica M y una «función <strong>en</strong> línea» (también llamada una<br />

macro con argum<strong>en</strong>tos). El bucle se realiza I5 veces; cada iteración pi<strong>de</strong> un valor <strong>de</strong> x, calcula la función<br />

y escribe los resultados. El diagrama <strong>de</strong> sintaxis <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia for es:<br />

Variable-control = Valor-inicial<br />

verda<strong>de</strong>ra<br />

S<strong>en</strong>t<strong>en</strong>cia<br />

r-5<br />

Expresión-increm<strong>en</strong>to<br />

Figura 6.2. Diagrama <strong>de</strong> sintaxis <strong>de</strong> un bucle f OL<br />

Exist<strong>en</strong> dos formas <strong>de</strong> implem<strong>en</strong>tar la s<strong>en</strong>t<strong>en</strong>cia for que se utilizan normalm<strong>en</strong>te para implem<strong>en</strong>tx<br />

bucles <strong>de</strong> conteo: formato asc<strong>en</strong>d<strong>en</strong>te, <strong>en</strong> el que la variable <strong>de</strong> control se increm<strong>en</strong>ta y formato<br />

<strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te, <strong>en</strong> el que la variable <strong>de</strong> control se <strong>de</strong>crem<strong>en</strong>ta.<br />

for (var-control=valor-inicial; var-control=valor-lfmite; exp-<strong>de</strong>crem<strong>en</strong>to)<br />

s<strong>en</strong>t<strong>en</strong>cia ;<br />

Ejemplo <strong>de</strong> formato asc<strong>en</strong>d<strong>en</strong>te<br />

int n:<br />

for (n = 1; n


Estructuras <strong>de</strong> control: bucles 183<br />

1 1<br />

2 4<br />

3 9<br />

4 16<br />

5 25<br />

6 36<br />

7 49<br />

8 64<br />

9 81<br />

10 100<br />

Ejemplo <strong>de</strong> formato <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te<br />

int n;<br />

for (n = 10; n.> 5; n--)<br />

printf("%d \t %d \n",n, n * n );<br />

La salida <strong>de</strong> este bucle es:<br />

10 100<br />

9 81<br />

8 64<br />

7 49<br />

6 36<br />

<strong>de</strong>bido a que el valor inicial <strong>de</strong> la variable <strong>de</strong> control es 10, y el límite que se ha puesto es n > 5 (es <strong>de</strong>cir,<br />

verda<strong>de</strong>ra cuando n = 10, 9, 8, 7, 6); la expresión <strong>de</strong> <strong>de</strong>crem<strong>en</strong>to es el operador <strong>de</strong> <strong>de</strong>crem<strong>en</strong>to n--<br />

que <strong>de</strong>crem<strong>en</strong>ta <strong>en</strong> 1 el valor <strong>de</strong> n tras la ejecución <strong>de</strong> cada iteración.<br />

Otros intervalos <strong>de</strong> increm<strong>en</strong>to/<strong>de</strong>rrem<strong>en</strong>to<br />

Los rangos <strong>de</strong> increm<strong>en</strong>to/<strong>de</strong>crem<strong>en</strong>to <strong>de</strong> la variable o expresión <strong>de</strong> control <strong>de</strong>l bucle pued<strong>en</strong> ser<br />

cualquier valor y no siempre 1, es <strong>de</strong>cir 5, 10, 20,4, ..., <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> los intervalos que se necesit<strong>en</strong>.<br />

Así el bucle<br />

int n;<br />

for (n = O; n < 100; n += 20)<br />

printf("%d \t %d \n",n, n * n );<br />

utiliza la expresión <strong>de</strong> increm<strong>en</strong>to<br />

n += 20<br />

que increm<strong>en</strong>ta el valor <strong>de</strong> n <strong>en</strong> 2 0, dado que equivale a n = n + 2 0. Así la salida que producirá la<br />

ejecución <strong>de</strong>l bucle es:<br />

0 0<br />

20 400<br />

40 1600<br />

60 3600<br />

80 6400<br />

Ejemplos<br />

/* ejemplo 1 */<br />

int c ;<br />

for (c = 'A'; c


184 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

for (i = 9; i >= 0; i -= 3)<br />

printf ("%d 'I, ( i * i)) ;<br />

/* ejemplo 3 */<br />

for (i = 1; i < 100; i*=2)<br />

printf ("%d",i);<br />

/* ejemplo 4 */<br />

#<strong>de</strong>fine MAX 25<br />

int i, j;<br />

for (i = O, j = MAX; i < j; i++, j--)<br />

printf("%d ",(i + 2 * j));<br />

El primer ejemplo inicializa la variable <strong>de</strong> control <strong>de</strong>l bucle c al carácter 'A', equivale a inicializar<br />

al <strong>en</strong>tero 65 (ASCII <strong>de</strong> A), e itera mi<strong>en</strong>tras que el valor <strong>de</strong> la variable c es m<strong>en</strong>or o igual que el ordinal<br />

<strong>de</strong>l carácter z l . La parte <strong>de</strong> increm<strong>en</strong>to <strong>de</strong>l bucle increm<strong>en</strong>ta el valor <strong>de</strong> la variable <strong>en</strong> 1. Por<br />

consigui<strong>en</strong>te, el bucle se realiza tantas veces como letras mayúsculas.<br />

El segundo ejemplo muestra un bucle <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te que inicializa la variable <strong>de</strong> control a 9. El bucle<br />

se realiza mi<strong>en</strong>tras que i no sea negativo, como la variable se <strong>de</strong>crem<strong>en</strong>ta <strong>en</strong> 3, el bucle se ejecuta cuatro<br />

veces con el valor <strong>de</strong> la variable <strong>de</strong> control i , 9,6, 3 y O.<br />

El ejemplo 3, la variable <strong>de</strong> control i se inicializa a 1 y se increm<strong>en</strong>ta <strong>en</strong> múltiplos <strong>de</strong> 2, por<br />

consigui<strong>en</strong>te, i toma valores <strong>de</strong> 1, 2,4 ,8, 16, 32,64 y el sigui<strong>en</strong>te 128 no cumple la condición, termina<br />

el bucle.<br />

El ejemplo 4, <strong>de</strong>clara dos variables <strong>de</strong> control i y j y las inicializa a O y a la constante MAX. El bucle<br />

se ejecutará mi<strong>en</strong>tras i seam<strong>en</strong>or que j. Las variable <strong>de</strong> control i se increm<strong>en</strong>ta <strong>en</strong> 1, y a la<br />

vez j se <strong>de</strong>crem<strong>en</strong>ta <strong>en</strong> 1.<br />

Ejemplo 6.6<br />

Suma <strong>de</strong> los 1 O primeros números pares<br />

#inclu<strong>de</strong> <br />

int main()<br />

1<br />

int n, suma = O;<br />

for (n = 1; n


5)<br />

Estructuras <strong>de</strong> control: bucles 185<br />

int n;<br />

for ( n = 1; n 2.0; x = sqrt(x))<br />

printf ("x es ahora igual a %. 5e",x) ;<br />

6.3. PRECAUCIONES EN EL USO DE for<br />

Un bucle for se <strong>de</strong>be construir con gran precaución, asegurándose que la expresión <strong>de</strong> inicialización,<br />

la condición <strong>de</strong>l bucle y la expresión <strong>de</strong> increm<strong>en</strong>to harán que la condición <strong>de</strong>l bucle se convierta <strong>en</strong><br />

false <strong>en</strong> algún mom<strong>en</strong>to. En particular: «si el cuerpo <strong>de</strong> un bucle <strong>de</strong> conteo modijica los valores <strong>de</strong><br />

cualquier variable implicada <strong>en</strong> la condición <strong>de</strong>l bucle, <strong>en</strong>tonces el número <strong>de</strong> repeticiones se pue<strong>de</strong><br />

modificar».<br />

Esta regla anterior es importante, ya que su aplicación se consi<strong>de</strong>ra una mala práctica <strong>de</strong><br />

programación. Es <strong>de</strong>cir, no es recom<strong>en</strong>dable modificar el valor <strong>de</strong> cualquier variable <strong>de</strong> la condición<br />

<strong>de</strong>l bucle d<strong>en</strong>tro <strong>de</strong>l cuerpo <strong>de</strong> un bucle for, ya que se pued<strong>en</strong> producir resultados imprevistos. Por<br />

ejemplo, la ejecución <strong>de</strong><br />

int i,limite = 11;<br />

for (i = O; i


186 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

int i,limite = 1;<br />

for (i = O; i


P<br />

Estructuras <strong>de</strong> control: bucles 187<br />

producirá la salida<br />

Siempre así, te llamamos siempre así ...<br />

Siempre así, te llamamos siempre así ...<br />

...<br />

un número ilimitado <strong>de</strong> veces, a m<strong>en</strong>os que el usuario interrumpa la ejecución (normalm<strong>en</strong>te pulsando<br />

las teclas Ctrl y c <strong>en</strong> ambi<strong>en</strong>tes PC).<br />

Para evitar esta situación, se requiere el diseño <strong>de</strong>l bucle for <strong>de</strong> la forma sigui<strong>en</strong>te:<br />

1. El cuerpo <strong>de</strong>l bucle ha <strong>de</strong> cont<strong>en</strong>er todas las s<strong>en</strong>t<strong>en</strong>cias que se <strong>de</strong>sean ejecutar repetidam<strong>en</strong>te.<br />

2. Una s<strong>en</strong>t<strong>en</strong>cia terminará la ejecución <strong>de</strong>l bucle cuando se cumpla una <strong>de</strong>terminada condición.<br />

La s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> terminación suele ser if -break con la sintaxis<br />

if (condición) break;<br />

condi ci ón<br />

break<br />

y la sintaxis completa<br />

es una expresión lógica<br />

termina la ejecución <strong>de</strong>l bucle y transfiere el control a la s<strong>en</strong>t<strong>en</strong>cia<br />

sigui<strong>en</strong>te al bucle<br />

for (;;I /* bucle */<br />

i<br />

1 i s t a-s <strong>en</strong> t <strong>en</strong> ci as,<br />

if (condición-terminación) break;<br />

1 is t a-s<strong>en</strong>t <strong>en</strong>ci as,<br />

1 /* fin <strong>de</strong>l bucle */<br />

1 i s t a-s<strong>en</strong> t<strong>en</strong> ci as pue<strong>de</strong> ser vacía, simple o compuesta.<br />

Ejemplo 6.7<br />

#<strong>de</strong>fine CLAVE -999<br />

for (;;)<br />

i<br />

printf("1ntroduzca un número, (%d) para terminar",CLAVE);<br />

scanf ("%d 'I, &num) ;<br />

if (num == CLAVE) break;<br />

...<br />

6.3.2. Los bucles for vacíos<br />

T<strong>en</strong>ga cuidado <strong>de</strong> situar un punto y coma <strong>de</strong>spués <strong>de</strong>l paréntesis inicial <strong>de</strong>l bucle for. Es <strong>de</strong>cir, el bucle<br />

for (i = 1; i


188 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En realidad lo que suce<strong>de</strong> es que se visualiza una vez la frase "Sierra Magina" ya que la s<strong>en</strong>t<strong>en</strong>cia<br />

for es una s<strong>en</strong>t<strong>en</strong>cia vacía al terminar con un punto y coma (;). Suce<strong>de</strong> que la s<strong>en</strong>t<strong>en</strong>cia for no hace<br />

absolutam<strong>en</strong>te nada durante 10 iteraciones y, por tanto, <strong>de</strong>spués <strong>de</strong> que el bucle for haya terminado, se<br />

ejecuta la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia puts y se escribe "Sierra Magina".<br />

El bucle for con cuerpos vacfos pue<strong>de</strong> t<strong>en</strong>er algunas aplicaciones, especialm<strong>en</strong>te cuando se<br />

requier<strong>en</strong> ral<strong>en</strong>tizaciones o temporizaciones <strong>de</strong> tiempo.<br />

6.3.3. S<strong>en</strong>t<strong>en</strong>cias nulas <strong>en</strong> bucles for<br />

Cualquiera o todas las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> un bucle for pued<strong>en</strong> ser nulas. Para ejecutar esta acción, se utiliza<br />

el punto y coma (;) para marcar la s<strong>en</strong>t<strong>en</strong>cia vacía. Si se <strong>de</strong>sea crear un bucle for que actúe exactam<strong>en</strong>te<br />

como un bucle while, se <strong>de</strong>b<strong>en</strong> incluir las primeras y terceras s<strong>en</strong>t<strong>en</strong>cias vacías.<br />

1: /* Listado<br />

2: bucles for con s<strong>en</strong>t<strong>en</strong>cias nulas<br />

3: */<br />

4: #inclu<strong>de</strong> <br />

5:<br />

6: int main()<br />

7: {<br />

8: int contador = 0;<br />

9:<br />

10: for (;contador < 5;)<br />

11: {<br />

12 : contador++;<br />

13 : printf (";Bucle!");<br />

14: 1<br />

15 :<br />

16: printf ("\n Contador: %d \n", Contador) ;<br />

17: return O;<br />

18: 1<br />

Salida<br />

;Bucle! ;Bucle! ;Bucle! ;Bucle! ;Bucle!<br />

Contador: 5<br />

Análisis<br />

En la línea 8 se inicializa la variable <strong>de</strong>l contador. La s<strong>en</strong>t<strong>en</strong>cia for <strong>en</strong> la línea 10 no inicializa ningún<br />

valor, pero incluye una prueba <strong>de</strong> contador < 5. No existe ninguna s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> increm<strong>en</strong>tación, <strong>de</strong><br />

modo que el bucle se comporta exactam<strong>en</strong>te como la s<strong>en</strong>t<strong>en</strong>cia sigui<strong>en</strong>te.<br />

while(contador < 5)<br />

t<br />

contador++;<br />

printf (";Bucle!");<br />

6.3.4. S<strong>en</strong>t<strong>en</strong>cias break y continue<br />

La s<strong>en</strong>t<strong>en</strong>cia break termina la ejecución <strong>de</strong> un bucle, <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia switch, <strong>en</strong> g<strong>en</strong>eral <strong>de</strong> cualquier<br />

s<strong>en</strong>t<strong>en</strong>cia.


.<br />

Estructuras <strong>de</strong> control: bucles 189<br />

/*<br />

Ejemplo <strong>de</strong> utilización <strong>de</strong> break<br />

*/<br />

#inclu<strong>de</strong> <br />

int main ( )<br />

1<br />

int contador = O; /* inicialización */<br />

int max;<br />

printf ("Cuantos holas? ") ;<br />

scanf ("%d", &max);<br />

for (;;) /* bucle for que no termina nunca */<br />

i<br />

if(contador < max) /* test */<br />

i<br />

puts("Hola!') ;<br />

contador++;<br />

1<br />

else<br />

break;<br />

1<br />

return O;<br />

Salida<br />

Cuantos holas? 3<br />

Hola!<br />

Hola!<br />

Hola!<br />

/* increm<strong>en</strong>to */<br />

La s<strong>en</strong>t<strong>en</strong>cia continue hace que la ejecución <strong>de</strong> un bucle vuelva a la cabecera <strong>de</strong>l bucle.<br />

#inclu<strong>de</strong> <br />

int main()<br />

{<br />

1<br />

int clave,¡;<br />

puts ("Introduce -9 para acabar. ") ;<br />

clave = 1;<br />

for (i = O; i < 8; i++) {<br />

if (clave ==-9) continue;<br />

scanf ("%d", &clave);<br />

printf ("clave %d\n",clave);<br />

1<br />

printf('VAL0RES FINALES i = %d clave = %d",i,clave);<br />

return O;<br />

4<br />

clave 4<br />

9 para acabar


190 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

7<br />

clave 7<br />

-9<br />

VALORES FINALES i = 8 Clave = -9<br />

La s<strong>en</strong>t<strong>en</strong>cia continue ha hecho que la ejecución vuelva a la cabecera <strong>de</strong>l bucle for, como no se<br />

vuelve a cambiar el valor <strong>de</strong> clave, realiza el resto <strong>de</strong> las iteraciones hasta que i vale 8.<br />

6.4. REPETICIÓN: EL BUCLE do. . .while<br />

La s<strong>en</strong>t<strong>en</strong>cia do-while se utiliza para especificar un bucle condicional que se ejecuta al m<strong>en</strong>os una<br />

vez. Esta situación se suele dar <strong>en</strong> algunas circunstancias <strong>en</strong> las que se ha <strong>de</strong> t<strong>en</strong>er la seguridad <strong>de</strong> que<br />

una <strong>de</strong>terminada acción se ejecutará una o varias veces, pero al m<strong>en</strong>os una vez.<br />

Sintaxis<br />

1.<br />

Acción (s<strong>en</strong>t<strong>en</strong>cia) a ejecutar<br />

al m<strong>en</strong>os una vez<br />

/<br />

do s<strong>en</strong>t<strong>en</strong>cia while (expresión)<br />

Expresión lógica que<br />

<strong>de</strong>termina si la acción<br />

se repite<br />

2.<br />

do<br />

s<strong>en</strong> t<strong>en</strong> ci a<br />

while (expresión)<br />

La construcción do comi<strong>en</strong>za ejecutando s<strong>en</strong> t<strong>en</strong>cia. Se evalúa a continuación expresión. Si<br />

expresión es verda<strong>de</strong>ra, <strong>en</strong>tonces se repite la ejecución <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cia. Este proceso continúa hasta que<br />

expresión es falsa. La semántica <strong>de</strong>l bucle do se repres<strong>en</strong>ta gráficam<strong>en</strong>te <strong>en</strong> la Figura 6.3.<br />

I<br />

S<strong>en</strong>t<strong>en</strong>cia<br />

sa, se termina el bucle y se ejecuta la<br />

Figura 6.3. Diagrama <strong>de</strong> flujo <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia do.


Estructuras <strong>de</strong> control: bucles 191<br />

Ejemplo 6.8<br />

Bucle para introducir un dígito.<br />

do<br />

i<br />

printf ("Introduzca un dígito (0-9):<br />

") ;<br />

scanf ("%c", &digito);<br />

1 while ((digito < 'O') I I ('9'< digito));<br />

Este bucle se realiza mi<strong>en</strong>tras se introduzcan dígitos y se termina cuando se introduzca un carácter<br />

que no sea un dígito <strong>de</strong> 'O'a '9'.<br />

Ejercicio 6.2<br />

Aplicación simple <strong>de</strong> un bucle while: seleccionar una opción <strong>de</strong> saludo al usuario d<strong>en</strong>tro <strong>de</strong> un<br />

programa.<br />

#inclu<strong>de</strong> <br />

int main()<br />

{<br />

char opcion;<br />

do<br />

i<br />

puts ("Hola");<br />

puts ("¿Desea otro tipo <strong>de</strong> saludo?") ;<br />

puts("Pu1se s para si y n para no, ") ;<br />

printf ("y a continuación pulse intro: 'I) ;<br />

scanf ("%c", &opcion);<br />

} while (opcion == 's' 1 ! opcion == 'S');<br />

puts ("Adiós");<br />

return O;<br />

1<br />

Salida <strong>de</strong> muestra<br />

Hola<br />

¿Desea otro tipo <strong>de</strong> saludo?<br />

Pulse s para si y n para no,<br />

y a continuación pulse intro: S<br />

Hola<br />

¿Desea otro tipo <strong>de</strong> saludo?<br />

Pulse s para si y n para no,<br />

y a continuación pulse intro: N<br />

Adiós<br />

I<br />

6.4.1. Difer<strong>en</strong>cias <strong>en</strong>tre while y do-while !, I ,<br />

Una s<strong>en</strong>t<strong>en</strong>cia do-while es similar a una s<strong>en</strong>t<strong>en</strong>cia while excepto que el cuerpo <strong>de</strong>l bucle se ejecuta<br />

siempre al m<strong>en</strong>os una vez


192 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Sintaxis<br />

S<strong>en</strong>t<strong>en</strong>cia compuesta<br />

I<br />

do<br />

1<br />

i<br />

s<strong>en</strong> t<strong>en</strong> ci a-1 ;<br />

s<strong>en</strong>t<strong>en</strong>cia-1;<br />

s<strong>en</strong>t<strong>en</strong>cia-2; s<strong>en</strong>t <strong>en</strong>ci a-2 ;<br />

...<br />

...<br />

s<strong>en</strong>t <strong>en</strong>ci a-n ;<br />

s<strong>en</strong>t<strong>en</strong>cia-n;<br />

} while (expresion-lógica)<br />

while (Expresión-lógica)<br />

s<strong>en</strong> t <strong>en</strong>ci a<br />

\<br />

S<strong>en</strong>t<strong>en</strong>cia simple<br />

do<br />

s<strong>en</strong> t <strong>en</strong>ci a<br />

iie (expresión-lógica)<br />

I<br />

Ejemplo 1<br />

/*<br />

cu<strong>en</strong>ta <strong>de</strong> O a lO(sin incluir el 10)<br />

*/<br />

int x = O;<br />

do<br />

print (“X: %d“, x++) ;<br />

while (x < 10);<br />

Ejemplo 2<br />

/*<br />

Bucle para imprimir las letras minúsculas <strong>de</strong>l alfabeto<br />

*/<br />

char car = ’a’;<br />

do<br />

{<br />

print f ( “%d It, car) ;<br />

car++ ;<br />

}while (car


-<br />

Estructuras <strong>de</strong> control: bucles 193<br />

6.5. COMPARACIÓN DE BUCLES while, for Y do-while<br />

C proporciona tres s<strong>en</strong>t<strong>en</strong>cias para el control <strong>de</strong> bucles: while, for y do-while. El bucle while se<br />

repite mi<strong>en</strong>tras su condición <strong>de</strong> repetición <strong>de</strong>l bucle es verda<strong>de</strong>ro; el bucle for se utiliza normalm<strong>en</strong>te<br />

cuando el conteo esté implicado, o bi<strong>en</strong> el número <strong>de</strong> iteraciones requeridas se pueda <strong>de</strong>terminar al<br />

principio <strong>de</strong> la ejecución <strong>de</strong>l bucle, o simplem<strong>en</strong>te cuando exista una necesidad <strong>de</strong> seguir el número <strong>de</strong><br />

veces que un suceso particular ti<strong>en</strong>e lugar. El bucle do-whi 1 e se ejecuta <strong>de</strong> un modo similar a whi l-e<br />

excepto que las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l cuerpo <strong>de</strong>l bucle se ejecutan siempre al m<strong>en</strong>os una vez.<br />

La Tabla 6.1 <strong>de</strong>scribe cuando se usa cada uno <strong>de</strong> los tres bucles. En C, el bucle for es el más<br />

frecu<strong>en</strong>tem<strong>en</strong>te utilizado <strong>de</strong> los tres. Es relativam<strong>en</strong>te fácil reescribir un bucle do-whi le como un bucle<br />

while, insertando una asignación inicial <strong>de</strong> la variable condicional. Sin embargo, no todos los bucles<br />

whlle se pued<strong>en</strong> expresar <strong>de</strong> modo a<strong>de</strong>cuado como bucles do-wh I le, ya que un bucle do-while se<br />

ejecutará siempre al m<strong>en</strong>os una vez y el bucle while pue<strong>de</strong> no ejecutarse. Por esta razón un bucle<br />

whlle suele preferirse a un bucle do-whi 1 e, a m<strong>en</strong>os que esté claro que se <strong>de</strong>be ejecutar una iteración<br />

como mínimo.<br />

Tabla 6.1. Formatos <strong>de</strong> los bucles.<br />

~ ~~ -<br />

while<br />

for<br />

do-whi le<br />

El uso más frecu<strong>en</strong>te es cuando la repetición no está controlada por contador; el test <strong>de</strong><br />

condición prece<strong>de</strong> a cada repetición <strong>de</strong>l bucle; el cuerpo <strong>de</strong>l bucle pue<strong>de</strong> no ser ejecutado. Se<br />

<strong>de</strong>be utilizar cuando se <strong>de</strong>sea saltar el bucle si la condición es falsa.<br />

Bucle <strong>de</strong> conteo, cuando el número <strong>de</strong> repeticiones se conoce por anticipado y pue<strong>de</strong> ser<br />

controlado por un contador; también es a<strong>de</strong>cuado para bucles que implican control no contable<br />

<strong>de</strong>l bucle con simples etapas <strong>de</strong> inicialización y <strong>de</strong> actualización; el test <strong>de</strong> la condición prece<strong>de</strong><br />

a la ejecución <strong>de</strong>l cuerpo <strong>de</strong>l bucle.<br />

Es a<strong>de</strong>cuada para asegurar que al m<strong>en</strong>os se ejecuta el bucle una vez.<br />

Comparación <strong>de</strong> tres bucles<br />

cu<strong>en</strong>ta = valor-inicial;<br />

while (cu<strong>en</strong>ta < valor-parada)<br />

i<br />

...<br />

cu<strong>en</strong>ta+ + ;<br />

} /* fin <strong>de</strong> while */<br />

for (cu<strong>en</strong>ta = valor-inicial; cu<strong>en</strong>ta < valor-parada; cu<strong>en</strong>ta++)<br />

I<br />

} /* fin <strong>de</strong> for */<br />

cu<strong>en</strong>ta = valor-inicial;<br />

if (valor-inicial i valor-parada)<br />

do<br />

...<br />

cu<strong>en</strong>ta++ ;<br />

}while (cu<strong>en</strong>ta < valor-parada);<br />

6.6. DISENO DE BUCLES<br />

El diseño <strong>de</strong> un bucle necesita tres puntos a consi<strong>de</strong>rar:


194 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

I. El cuerpo <strong>de</strong>l bucle.<br />

2. Las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> inicialización.<br />

3. Las condiciones para la terminacicín <strong>de</strong>l bucle.<br />

6.6.1. Bucles para diseño <strong>de</strong> sumas y productos<br />

Muchas tareas frecu<strong>en</strong>tes implican la lectura <strong>de</strong> una lista <strong>de</strong> números y calculan su suma. Si se conoce<br />

cuántos números habrá, tal tarea se pue<strong>de</strong> ejecutar fácilm<strong>en</strong>te por el sigui<strong>en</strong>te pseudocódigo. El valor <strong>de</strong><br />

la variable total es el número <strong>de</strong> valores que se suman. La suma se acumula <strong>en</strong> la variable suma.<br />

suma = O;<br />

repetir lo sigui<strong>en</strong>te total veces:<br />

leer(sigui<strong>en</strong>te);<br />

suma = suma + sigui<strong>en</strong>te;<br />

f in-bucle<br />

1<br />

Este código se implem<strong>en</strong>ta fácilm<strong>en</strong>te con un bucle for<br />

int cu<strong>en</strong>ta, suma = O;<br />

for (cu<strong>en</strong>ta = 1; cu<strong>en</strong>ta


Estructuras <strong>de</strong> control: bucles 195<br />

Tamaño <strong>de</strong> la secu<strong>en</strong>cia <strong>de</strong> <strong>en</strong>trada<br />

Si su programa pue<strong>de</strong> <strong>de</strong>terminar el tamaño <strong>de</strong> la secu<strong>en</strong>cia <strong>de</strong> <strong>en</strong>trada por anticipado, bi<strong>en</strong> preguntando<br />

al usuario o por algún otro método, se pue<strong>de</strong> utilizar un bucle «repetir n veces» para leer la <strong>en</strong>trada<br />

exactam<strong>en</strong>te n veces, <strong>en</strong> don<strong>de</strong> n es el tamaño <strong>de</strong> la secu<strong>en</strong>cia.<br />

Preguntar antes <strong>de</strong> la iteración<br />

El segundo método para la terminación <strong>de</strong> un bucle <strong>de</strong> <strong>en</strong>trada es preguntar, simplem<strong>en</strong>te al usuario,<br />

<strong>de</strong>spués <strong>de</strong> cada iteración <strong>de</strong>l bucle, si el bucle <strong>de</strong>be ser o no iterado <strong>de</strong> nuevo. Por ejemplo:<br />

int numero, suma = O;<br />

char resp = 'S';<br />

while ((resp == 'S'l I<br />

1<br />

(resp == 'S'))<br />

printf ( "Introduzca un número :" ) ;<br />

scanf ("%d", &numero);;<br />

suma += numero;<br />

printf("¿Exist<strong>en</strong> más nÚmeros?(S pdra Si, N para No): ");<br />

scan€ ("%d", hresp) ;<br />

Este método es muy tedioso para listas gran<strong>de</strong>s <strong>de</strong> números. Cuando se lea una lista larga es<br />

preferible incluir una Única señal <strong>de</strong> parada, como se incluye <strong>en</strong> el método sigui<strong>en</strong>te.<br />

Valor c<strong>en</strong>tinela<br />

El método más práctico y efici<strong>en</strong>te para terminar un bucle que lee una lista <strong>de</strong> valores <strong>de</strong>l teclado es<br />

con un valor c<strong>en</strong>tinela. Un valor c<strong>en</strong>tinela es aquel que es totalm<strong>en</strong>te distinto <strong>de</strong> todos los valores<br />

posibles <strong>de</strong> la lista que se está ley<strong>en</strong>do y <strong>de</strong> este modo indica el final <strong>de</strong> la lista. Un ejemplo típico se<br />

pres<strong>en</strong>ta cuando se lee una lista <strong>de</strong> números positivos; un número negativo se pue<strong>de</strong> utilizar como un<br />

valor c<strong>en</strong>tinela para indicar el final <strong>de</strong> la lista.<br />

/* ejemplo <strong>de</strong> valor c<strong>en</strong>tinela (número negativo) */<br />

puts("1ntroduzca una lista <strong>de</strong> <strong>en</strong>teros positivos");<br />

puts ("Termine la lista con un número negativo') ;<br />

suma = O;<br />

scanf ("%d", &numero);<br />

while (numero >= O)<br />

t<br />

suma += numero;<br />

scanf ("%d",&numero);<br />

I<br />

printf ("La suma es: %d\n", suma) ;<br />

Si al ejecutar el segm<strong>en</strong>to <strong>de</strong> programa anterior se introduce la lista<br />

4 8 15 -99<br />

el valor <strong>de</strong> la suma será 27. Es <strong>de</strong>cir, -99, Último número <strong>de</strong> la <strong>en</strong>trada <strong>de</strong> <strong>datos</strong> no se aña<strong>de</strong> a suma.<br />

-99 es el último dato <strong>de</strong> la lista que actúa como c<strong>en</strong>tinela y no forma parte <strong>de</strong> la lista <strong>de</strong> <strong>en</strong>trada <strong>de</strong><br />

números.<br />

Agotami<strong>en</strong>to <strong>de</strong> la <strong>en</strong>trada<br />

Cuando se le<strong>en</strong> <strong>en</strong>tradas <strong>de</strong> un archivo, se pue<strong>de</strong> utilizar un valor c<strong>en</strong>tinela, aunque el método más<br />

frecu<strong>en</strong>te es comprobar simplem<strong>en</strong>te si todas las <strong>en</strong>tradas <strong>de</strong>l archivo han sido procesadas y se alcanza<br />

el final <strong>de</strong>l bucle cuando no hay más <strong>en</strong>tradas a leer. Éste es el método usual <strong>en</strong> la lectura <strong>de</strong> archivos,


196 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

que se suele utilizar una marca al final <strong>de</strong> archivo, eof. En el capítulo <strong>de</strong> archivos se <strong>de</strong>dicará una<br />

at<strong>en</strong>ción especial a la lectura <strong>de</strong> archivos con una marca <strong>de</strong> final <strong>de</strong> archivo.<br />

6.6.3. Otras técnicas <strong>de</strong> terminación <strong>de</strong> bucle<br />

Las técnicas más usuales para la terminación <strong>de</strong> bucles <strong>de</strong> cualquier tipo son:<br />

1. Bucles controlados por contador.<br />

2. Preguntar antes <strong>de</strong> iterar.<br />

3. Salir con una condición ban<strong>de</strong>ra.<br />

Un bucle controlado por contador es cualquier bucle que <strong>de</strong>termina el número <strong>de</strong> iteraciones antes<br />

<strong>de</strong> que el bucle comi<strong>en</strong>ce y a continuación repite (itera) el cuerpo <strong>de</strong>l bucle esas iteraciones. La técnica<br />

<strong>de</strong> la secu<strong>en</strong>cia <strong>de</strong> <strong>en</strong>trada precedida por su tamaño es un ejemplo <strong>de</strong> un bucle controlado por contador.<br />

La técnica <strong>de</strong> preguntar antes <strong>de</strong> iterar se pue<strong>de</strong> utilizar para bucles distintos <strong>de</strong> los bucles <strong>de</strong><br />

<strong>en</strong>trada, pero el uso más común <strong>de</strong> esta técnica es para procesar la <strong>en</strong>trada. La técnica <strong>de</strong>l valor c<strong>en</strong>tinela<br />

es una técnica conocida también como salida con una condición ban<strong>de</strong>ra o señalizadora. Una variable<br />

que cambia su valor para indicar que algún suceso o ev<strong>en</strong>to ha t<strong>en</strong>ido lugar, se d<strong>en</strong>omina normalm<strong>en</strong>te<br />

ban<strong>de</strong>ra o indicador. En el ejemplo anterior <strong>de</strong> suma <strong>de</strong> números, la variable ban<strong>de</strong>ra es numero <strong>de</strong><br />

modo que cuando toma un valor negativo significa que indica que la lista <strong>de</strong> <strong>en</strong>trada ha terminado.<br />

6.6.4. Bucles for vacíos<br />

La s<strong>en</strong>t<strong>en</strong>cia nula ( ;)<br />

es una s<strong>en</strong>t<strong>en</strong>cia que está <strong>en</strong> el cuerpo <strong>de</strong>l bucle y no hace nada. Un bucle for<br />

se consi<strong>de</strong>ra vacío si consta <strong>de</strong> la cabecera y <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia nula ( ; ) .<br />

Ejemplo<br />

Muestra los valores <strong>de</strong>l contador, <strong>de</strong> O a 4.<br />

1:<br />

2:<br />

3:<br />

4:<br />

5:<br />

6:<br />

7:<br />

8:<br />

9:<br />

10:<br />

Sulida<br />

i: O<br />

i: 1<br />

i: 2<br />

i: 3<br />

i: 4<br />

/*<br />

Ejemplo <strong>de</strong> Id s<strong>en</strong>t<strong>en</strong>cia nula <strong>en</strong> for.<br />

*/<br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

i<br />

int i;<br />

for (i = O; i < 5; printf("i: %d\n",i++));<br />

return O;<br />

Análisis<br />

El bucle for <strong>de</strong> la línea 8 incluye tres s<strong>en</strong>t<strong>en</strong>cias: la s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> inicial i zaci ón establece el valor<br />

inicial <strong>de</strong>l contador i a O. La s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> condición comprueba i < 5, y la s<strong>en</strong>t<strong>en</strong>cia acción<br />

imprime el valor <strong>de</strong> i y lo increm<strong>en</strong>ta.


Estructuras <strong>de</strong> control: bucles 197<br />

Ejercicio 6.3<br />

Escribir un programa que visualice e1,factorial <strong>de</strong> un <strong>en</strong>tero compr<strong>en</strong>dido <strong>en</strong>tre 2 y 20.<br />

El factorial <strong>de</strong> un <strong>en</strong>tero n se calcula con un bucle for <strong>de</strong>s<strong>de</strong> 2 hasta n, t<strong>en</strong>i<strong>en</strong>do <strong>en</strong> cu<strong>en</strong>ta que factorial<br />

<strong>de</strong> 1 es 1 (l! = 1) y que n! = n*(n-l)! . Así, por ejemplo,<br />

4! = 4*3! = 4*3 2! = 4*3*2*1! = 4*3*2*1 = 24<br />

En el programa se escribe un bucle do-whi 1 e para validar la <strong>en</strong>trada <strong>de</strong> n, <strong>en</strong>tre 2 y 20. Otro bucle<br />

for para calcular el factorial. El bucle for va a ser vacío, <strong>en</strong> la expresión <strong>de</strong> increm<strong>en</strong>to se va a<br />

calcular los n productos, para ello se utiliza el operador *=junto al <strong>de</strong> <strong>de</strong>crem<strong>en</strong>to (- -).<br />

#inclu<strong>de</strong> <br />

int main0<br />

long int n,m,fact;<br />

do<br />

printf ("\nFactorial <strong>de</strong> número n, <strong>en</strong>tre 2 y 20: ");<br />

scanf ("%ld", &n);<br />

}while ((n 20));<br />

for (m=n,fact=l; n>l; fact *= n--) ;<br />

printf ("%ld! = %ld",m, fact) ;<br />

return 0;<br />

6.7. BUCLES ANIDADOS<br />

Es posible anidar bucles. Los bucles anidados constan <strong>de</strong> un bucle externo con uno o más bucles<br />

internos. Cada vez que se repite el bucle externo, los bucles internos se repit<strong>en</strong>, se vuelv<strong>en</strong> a evaluar los<br />

compon<strong>en</strong>tes <strong>de</strong> control y se ejecutan todas las iteraciones requeridas.<br />

Ejemplo 6.10<br />

El segm<strong>en</strong>to <strong>de</strong> programa sigui<strong>en</strong>te visualiza una tabla <strong>de</strong> multiplicación por cálculo y visualización<br />

<strong>de</strong> productos <strong>de</strong> la forma x * y para cada x <strong>en</strong> el rango <strong>de</strong> 1 ci xu1 timo y <strong>de</strong>s<strong>de</strong> cada y <strong>en</strong> el<br />

rango 1 a ~ utimo i (don<strong>de</strong> xu1 t imo, y Yul t imo son <strong>en</strong>teros prefijados). La Tabla que .se <strong>de</strong>sea<br />

obt<strong>en</strong>er es<br />

1*1=1<br />

1*2=2<br />

1*3=3<br />

1*4=4<br />

1*5=5<br />

2*1=2<br />

2*2=4<br />

2*3=6<br />

2*4=8<br />

2*5=10


L<br />

198 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

for (x = 1; x


Estructuras <strong>de</strong> control: bucles 199<br />

Ejercicio 6.4<br />

Escribir un programa que visualice un triángulo isósceles.<br />

*<br />

* * *<br />

* * * * *<br />

* * * * * * *<br />

* * * * * * * * *<br />

El triángulo isósceles se realiza mediante un bucle externo y dos bucles internos. Cada vez que se<br />

repite el bucle externo se ejecutan los dos bucles internos. El bucle externo se repite cinco veces (cinco<br />

filas); el número <strong>de</strong> repeticiones realizadas por los bucles internos se basan <strong>en</strong> el valor <strong>de</strong> la variable<br />

fila . El primer bucle interno visualiza los espacios <strong>en</strong> blanco no significativos; el segundo bucle<br />

interno visualiza uno o más asteriscos.<br />

#inclu<strong>de</strong> <br />

/* constantes globales */<br />

const int num-lineas = 5;<br />

const char blanco = ";<br />

const char asterisco = '*';<br />

void main()<br />

{<br />

1<br />

int fila, blancos, cu<strong>en</strong>ta-as;<br />

puts(" " ); /* Deja una línea <strong>de</strong> separación */<br />

/* bucle externo: dibuja cada línea */<br />

for (fila = 1; fila O; blancos--)<br />

putchar (blanco) ;<br />

for (cu<strong>en</strong>ta-as = 1; cu<strong>en</strong>ta-as i 2 * tila; cu<strong>en</strong>td-as++)<br />

putchar(asterisc0);<br />

/* terminar línea */<br />

puts i" ") ;<br />

} /* fin <strong>de</strong>l bucle externo */<br />

El bucle externo se repite cinco veces, uno por línea o fila; el número <strong>de</strong> repeticiones ejecutadas<br />

por los bucles internos se basa <strong>en</strong> el valor <strong>de</strong> fila. La primera fila consta <strong>de</strong> un asterisco y cuatro<br />

blancos, la fila 2 consta <strong>de</strong> tres blancos y tres asteriscos, y así sucesivam<strong>en</strong>te; la fila 5 t<strong>en</strong>drá 9 asteriscos<br />

(2 x 5 - 1). En este ejercicio se ha utilizado para salida <strong>de</strong> un carácter la función putchar ( ) . Esta<br />

función escribe un argum<strong>en</strong>to <strong>de</strong> tipo carácter <strong>en</strong> la pantalla.<br />

I<br />

Ejercicio 6.5<br />

Ejecutar el programa sigui<strong>en</strong>te que imprime una tabla <strong>de</strong> mJilas por n columnas y un carácter <strong>de</strong> <strong>en</strong>trada.<br />

1: #inclu<strong>de</strong>


200 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

5:<br />

6:<br />

7:<br />

8:<br />

9:<br />

10:<br />

11 :<br />

12 :<br />

13 :<br />

14 :<br />

15:<br />

16:<br />

17:<br />

1.8:<br />

19 :<br />

20:<br />

21 :<br />

int tilas, columnas;<br />

int i, j;<br />

char elCar;<br />

printf ("¿Cuántas tilas?: ") ;<br />

scanf ("&d',&filas);<br />

printf ("¿Cuántas columnas?: 'I);<br />

scanf ( "%d" ,&col umnas) ;<br />

print€ ("¿Qué carácter?: ") ;<br />

scanf ("\n&c',&elCar);<br />

tor (i = 0; i < filas; i++)<br />

i<br />

for (j = O; j < columncls; it+)<br />

putchar (elcar);<br />

putchar('\n');<br />

i<br />

return O;<br />

i<br />

Análisis<br />

El usuario solicita el número <strong>de</strong> filas y <strong>de</strong> columnas y un carácter a imprimir. Merece la p<strong>en</strong>a com<strong>en</strong>tar, que<br />

para leer el carácter a escribir es necesario saltarse el carácter fin <strong>de</strong> línea (scanf ("\n&c", &elcar)) que se<br />

<strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el buffer <strong>de</strong> <strong>en</strong>trada, <strong>de</strong>bido a la petición anterior <strong>de</strong>l número <strong>de</strong> columnas. El primer bucle for<br />

<strong>de</strong> la línea 14 inicializa un contador (i) a O y a continuación se ejecuta el cuerpo <strong>de</strong>l bucle for externo.<br />

En la línea 16 se inicializa otro bucle for y un segundo contador j se inicializa a O y se ejecuta el<br />

cuerpo <strong>de</strong>l bucle interno. En la línea 17 se imprime el carácter elcar (*). Se evalúa la condición (j <<br />

columnas) y si se evalúa a true (verda<strong>de</strong>ro), j se increm<strong>en</strong>ta y se imprime el sigui<strong>en</strong>te carácter. Esta<br />

acción se repite hasta que j sea igual al número <strong>de</strong> columnas.<br />

El bucle interno imprime doce caracteres asterisco <strong>en</strong> una misma fila y el bucle externo repite cuatro<br />

veces (número <strong>de</strong> filas) la fila <strong>de</strong> caracteres.<br />

Ejercicio 6.6<br />

Escribir <strong>en</strong> pantalla el factorial <strong>de</strong> n, <strong>en</strong>tre los vulores 1 u IO.<br />

Con dos bucles for se solucion el problema. El bucle externo <strong>de</strong>termina el número y1 cuyo factorial se<br />

calcula <strong>en</strong> el bucle interno.<br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine S 10<br />

int main0<br />

i<br />

long int n,m,fact;<br />

for (n = 1, n l; m--)<br />

fact *= m;<br />

printf ("\t %Id! = &Id \n",n, fact) ;<br />

1<br />

return O;<br />

i


Estructuras <strong>de</strong> control: bucles 201<br />

8. RE<br />

era <strong>de</strong>l bucle whi -<br />

que se repite pue<strong>de</strong><br />

también comprueban<br />

I<br />

I<br />

I<br />

6.9. EJERCICIOS<br />

c<br />

1<br />

B double n = 2;<br />

for (; n > O; n<br />

I,<br />

> O; n = n-2)<br />

,


-<br />

202 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

6.3.<br />

visualice todas<br />

6.4.<br />

int i = 1;<br />

while (i = 1; m--)<br />

printf ("%dveces %d= %d \n", n,<br />

m, n*m);<br />

don<strong>de</strong> n e8 un valor <strong>de</strong> un dato.<br />

a que calcule y visualice<br />

suma = O;<br />

while (suma c 1001<br />

sum 5;<br />

printf C n %d \n", suma) ;<br />

1<br />

B for (i = n; i > O; i--1<br />

c<br />

for (j = m; j > O; j--)<br />

putchar('*');<br />

putchar('\n');<br />

1<br />

6.12. ¿Cuál es La salida <strong>de</strong> los sigui<strong>en</strong>tes bucles?<br />

A for (i = O; i < 10; it+)<br />

printf (" 2* %d \nu, i,<br />

2 * i);<br />

B for (i = O; i


Estructuras <strong>de</strong> control: bucles 203<br />

s, los salarios<br />

aum<strong>en</strong>tar según su<br />

Aum<strong>en</strong>to % 6.2.<br />

20<br />

10<br />

5<br />

O<br />

La constante pi (3.14<br />

<strong>en</strong> matemáticas. Un m<br />

lar su valor es:<br />

pi = 4 * (+) * (+) * ($) * (+) . . .


204 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

6.9. El matemátic<br />

I.<br />

I,<br />

8 1 6<br />

3 5 7<br />

4 9 2<br />

Un método <strong>de</strong> constsucción <strong>de</strong>l cuadrado<br />

consiste <strong>en</strong> situar el número 1 <strong>en</strong> el c<strong>en</strong>tro <strong>de</strong><br />

la primera línea, el número sigui<strong>en</strong>te <strong>en</strong> ía<br />

casilla situada <strong>en</strong>cima y a la <strong>de</strong>recha, y así<br />

sucesivam<strong>en</strong>te. Es preciso consi<strong>de</strong>rar que el<br />

cuadrado se cierra sobre sí mismo: la línea<br />

6.10. Para <strong>en</strong>contrar el m<br />

<strong>de</strong> dos números s<br />

Eucli<strong>de</strong>s, que se pu<br />

<strong>en</strong>teros a y b (a > b), se divi<strong>de</strong> a por b,<br />

obt<strong>en</strong>i<strong>en</strong>do el coci<strong>en</strong>te ql y el resto rl.<br />

Si r 1 < > O, se divi<strong>de</strong> r por b 1, Obt<strong>en</strong>i<strong>en</strong>do el<br />

coci<strong>en</strong>te (12 y el resto r-2. Si r2 < > O, se divi<strong>de</strong> rl<br />

por r2, para obt<strong>en</strong>er q3 y r3, y así sucesivam<strong>en</strong>te.<br />

Se continúa el p eso hasta que se<br />

obti<strong>en</strong>e un resto O. El resto anterior es <strong>en</strong>tonces<br />

el mcd <strong>de</strong> los números a y b. Escribir un<br />

programa que calcule el mcd <strong>de</strong> dos números.<br />

6.11. Escribir un programa que <strong>en</strong>cu<strong>en</strong>tre el primer<br />

s pares y los tres pri-<br />

pegecto es un <strong>en</strong>tero positivo, que<br />

. . . + l/Ndon<strong>de</strong><br />

introduce por teclado.<br />

6.13. Calcular la suma <strong>de</strong> los<br />

1/2 + 2/2’ P 3/23 + . -. + n/2”<br />

6.14. Un número aquel número que es<br />

sus divisiones excepto<br />

perfecto es 6, ya


Estructuras <strong>de</strong> control: bucles 205<br />

e 6.24. Calcular 1<br />

6.15. 625. Contar el número<br />

cidos <strong>en</strong> una línea.<br />

6.16.<br />

6.17. Caicular el factorial <strong>de</strong> un número <strong>en</strong>tero leido<br />

<strong>de</strong>s<strong>de</strong> el teclado utilizando las s<strong>en</strong>t<strong>en</strong>cias<br />

while, repeat y for.<br />

6.18. Encontrar el número mayor <strong>de</strong> una serie <strong>de</strong><br />

números.<br />

6.19. Calcular la media <strong>de</strong> las notas introducidas por<br />

teclado con un áiáio nteractivo semejante al<br />

sigui<strong>en</strong>te:<br />

Nota 2: 6.40<br />

Nota 3: 4.20<br />

Nota 4: 8.50<br />

...<br />

Nota 20: 9.50<br />

Media <strong>de</strong> estas 20: 7.475<br />

6.20. Determinar si un número dado leído <strong>de</strong>l<br />

teclado es primo o no.<br />

cular la suma <strong>de</strong> la serie 1 /1 i 1 /2 i<br />

1 /N don<strong>de</strong> N es un número <strong>en</strong>tero que se<br />

<strong>de</strong>termina condición que 1/N sea<br />

m<strong>en</strong>or que #on prefijado (por ejemplo<br />

1. IO6).<br />

6.22. Escribir un programa que calcule Ia suma <strong>de</strong><br />

los 50 primeros números <strong>en</strong>teros.<br />

6.23. Calcular la suma <strong>de</strong> una serie <strong>de</strong> números<br />

leídos <strong>de</strong>l teclado.<br />

6.26. Visualizar <strong>en</strong> pan<br />

sigui<strong>en</strong>te<br />

***<br />

Y***<br />

*****<br />

si<strong>en</strong>do variable el número <strong>de</strong> líneas que se<br />

pued<strong>en</strong> introducir.<br />

6.27. Escribir un programa para mostrar, mediante<br />

bucles, los código ascii <strong>de</strong> la letras mayúsculas<br />

y minúscula.<br />

6.2S* EhCOn&x el &mero natural N más Pequeño<br />

que la suma <strong>de</strong> los N primeros números exceda<br />

<strong>de</strong> una cantidad introducida por el teclado.<br />

6.29. Diseñar un programa que produzca la sigui<strong>en</strong>te<br />

salida:<br />

ZYXWVTSRQPONMLHJIHGFEDCBA<br />

YXWVTSRQPONMLKJIHGFEDCBA<br />

XWVTSRQPONMLKJIHGFEDCBA<br />

WVTSRQPONMLKJIHGFEDCBA<br />

VTSRQPONMLKJIHGFEDCBA<br />

TSRQPONMLKJIHGFEDCBA<br />

SRQPONMLKJIHGFEDCBA<br />

RQPONMLKJIHGFEDCBA<br />

QPONMLKJIHGFEDCBA<br />

PONMLKJIHGFEDCBA<br />

ONMLKJIHGFEDCBA<br />

NMLKJIHGFEDCBA<br />

MLKJIHGFEDCBA<br />

LKJIHGFEDCBA<br />

KJIHGFEDCBA<br />

JIHGFEDCBA<br />

IHGFEDCBA<br />

HGFEDCBA<br />

GFEDCBA


206 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

6.11. PR DE P MACI~N<br />

\<br />

Y<br />

6.1.<br />

6.5,<br />

1<br />

6.2.<br />

6.6.<br />

6.3.<br />

5.7.<br />

6.4.<br />

6.8.


Estructuras <strong>de</strong> control: bucles 207<br />

6.9. Escribir un programa que lea el radio <strong>de</strong> una<br />

a y su volum<strong>en</strong><br />

6.10. Escribir<br />

mitximo común divi


CONTENIDO<br />

7.1. Concepto <strong>de</strong> función. 7.10. Funciones numéricas.<br />

7,s. Estructura <strong>de</strong> una función.<br />

7.11. Funciones <strong>de</strong> fecha y hora.<br />

7.1s. Funciones<br />

7.5. Prototipos <strong>de</strong> las funciones.<br />

7.13. Visibilidad <strong>de</strong> una función.<br />

7.4. Parámetros <strong>de</strong> una función.<br />

7.14. ión separada.<br />

7.s. Funciones <strong>en</strong> Enea: macros.<br />

7.16. Vaxiables redstPo<br />

7.6. Ambit0 (alcance). (register).<br />

7.7. Clases <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to. 7.16. Recursividad.<br />

7.17.<br />

7.8. Concepto y uso <strong>de</strong> funciones<br />

Resum<strong>en</strong>.<br />

<strong>de</strong> biblioteca. 7.18. Ejerciciosr.<br />

7.9. Funciones <strong>de</strong> carácter. 7.19. Problemas.<br />

208


INTRODUCCIÓN<br />

<strong>de</strong>l manual <strong>de</strong> biblioteca <strong>de</strong><br />

ionadas por la bib<br />

iiadores <strong>de</strong> C.<br />

CON<br />

CUVE<br />

0 Biblioteca <strong>de</strong> funciones, Pasarpar 019 por valor.<br />

CoxnpilacirSn in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te. Paso por refer<strong>en</strong>cia.<br />

hción.<br />

Recursividad.<br />

* Mod~ización. S<strong>en</strong>t<strong>en</strong>cia return.<br />

Pm4xnetros <strong>de</strong> una mción. Subpmpama.<br />

209


21 0 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

7.1. CONCEPTO DE FUNCIÓN<br />

C fue diseñado como un l<strong>en</strong>guaje <strong>de</strong> programación <strong>estructura</strong>do, también llamado programación<br />

modular. Por esta razón, para escribir un programa se divi<strong>de</strong> éste <strong>en</strong> varios módulos, <strong>en</strong> lugar <strong>de</strong> uno solo<br />

largo. El programa se divi<strong>de</strong> <strong>en</strong> muchos módulos (rutinas pequeñas d<strong>en</strong>ominadas funciones), que<br />

produc<strong>en</strong> muchos b<strong>en</strong>eficios: aislar mejor los problemas, escribir programas correctos más rápido y<br />

producir programas que son mucho más fáciles <strong>de</strong> mant<strong>en</strong>er.<br />

Así pues, un programa C se compone <strong>de</strong> varias funciones, cada una <strong>de</strong> las cuales realiza una tarea<br />

principal. Por ejemplo, si está escribi<strong>en</strong>do un programa que obt<strong>en</strong>ga una lista <strong>de</strong> caracteres <strong>de</strong>l teclado,<br />

los ord<strong>en</strong>e alfabéticam<strong>en</strong>te y los visualice a continuación <strong>en</strong> la pantalla, se pued<strong>en</strong> escribir todas estas<br />

tareas <strong>en</strong> un único gran programa (función main ( ) ).<br />

int main()<br />

{<br />

1<br />

/* Código C para obt<strong>en</strong>er una lista <strong>de</strong> caracteres */<br />

...<br />

/* Código C para alfabetizar los caracteres */<br />

...<br />

/* Código C para visualizar la lista por ord<strong>en</strong> alfabético */<br />

...<br />

return O<br />

Sin embargo, este método no es correcto. El mejor medio para escribir un programa es escribir<br />

funciones in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes para cada tarea que haga el programa. El mejor medio para escribir el citado<br />

programa sería el sigui<strong>en</strong>te:<br />

int main0<br />

{<br />

obt<strong>en</strong>ercaractereso; /* Llamada a una función que obti<strong>en</strong>e los<br />

números */<br />

alfabetizar ();<br />

/* Llamada a la función que ord<strong>en</strong>a<br />

alfabéticam<strong>en</strong>te las letras */<br />

verletras ();<br />

/* Llamada a la función que visualiza<br />

letras <strong>en</strong> la pantalla */<br />

return O; /* retorno al sistema */<br />

1<br />

int obt<strong>en</strong>ercaractereso<br />

I<br />

/*<br />

Código <strong>de</strong> C para obt<strong>en</strong>er una lista <strong>de</strong> caracteres<br />

*/<br />

return (O) ; /* Retorno a main ( ) */<br />

1<br />

int alfabetizar0<br />

I<br />

/*. . .<br />

Código <strong>de</strong> C para alfabetizar los caracteres<br />

*/<br />

return(0) ; /* Retorno a main() */<br />

1<br />

.<br />

void verletras ( )<br />

/*. . .


~ <strong>de</strong>vyeito<br />

'. i.<br />

Funciones 21 1<br />

*/<br />

Código <strong>de</strong> C para visualizar lista alfabetizada<br />

1<br />

return /* Retorno a main0 */<br />

Cada función realiza una <strong>de</strong>terminada tarea y cuando se ejecuta return se retorna al punto <strong>en</strong> que<br />

fue llamada por el programa o función principal.<br />

Una bu<strong>en</strong>a regía para <strong>de</strong>t<strong>en</strong>ninar la longitud <strong>de</strong> una función (número <strong>de</strong> líneas que conti<strong>en</strong>e) es que<br />

no ocupe más longitud que el equival<strong>en</strong>te a una pantalla.<br />

7.2. ESTRUCTURA DE UNA FUNCIÓN<br />

Una función es, s<strong>en</strong>cillam<strong>en</strong>te, un conjunto <strong>de</strong> sht<strong>en</strong>cias que se pued<strong>en</strong> llamar <strong>de</strong>s<strong>de</strong> cualquier parte <strong>de</strong><br />

un programa. Las funciones permit<strong>en</strong> al programador un grado <strong>de</strong> abstracción <strong>en</strong> la resolución <strong>de</strong> un<br />

problema.<br />

Las funciones <strong>en</strong> C no se pued<strong>en</strong> anidar. Esto significa que una función no se pue<strong>de</strong> <strong>de</strong>clarar d<strong>en</strong>tro<br />

<strong>de</strong> otra función. La razón para esto es permitir un acceso muy efici<strong>en</strong>te a los <strong>datos</strong>. En C todas las<br />

funciones son externas o globales, es <strong>de</strong>cir, pued<strong>en</strong> ser llamadas <strong>de</strong>s<strong>de</strong> cualquier punto <strong>de</strong>l programa.<br />

La <strong>estructura</strong> <strong>de</strong> una función <strong>en</strong> C se muestra <strong>en</strong> la Figura 7.1.<br />

tipo-<strong>de</strong>-re torno nombreFunci Ón ( 1 is taDeParáme tros)<br />

{<br />

cuerpo <strong>de</strong> la función<br />

return expresión<br />

I<br />

tipo-<strong>de</strong>-retorno Tipo <strong>de</strong> valor <strong>de</strong>vuelto por la función o la palabra<br />

reservada void si la función no <strong>de</strong>vuelve ningún vi.3r.<br />

nombreFunciÓn Id<strong>en</strong>tificador o nombre <strong>de</strong> la función.<br />

1 i s t aDeParáme tros Lista <strong>de</strong> <strong>de</strong>claraciones <strong>de</strong> los parámetros <strong>de</strong> la función separados por comas.<br />

expresión<br />

valor que <strong>de</strong>vuelve la función.<br />

'I<br />

Tipo <strong>de</strong> resultado<br />

1<br />

7 Lista <strong>de</strong> parámetros<br />

Declaración<br />

<strong>de</strong> variables<br />

I<br />

float resp;<br />

resp 7 riiirnl + riii1ri2 ;<br />

return rpc;p);<br />

Valor<br />

Figura 7.1. Estructura <strong>de</strong> una función.


212 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Los aspectos más sobresali<strong>en</strong>tes <strong>en</strong> el diseño <strong>de</strong> una función son:<br />

o Tipo <strong>de</strong> resultado. Es el tipo <strong>de</strong> dato que <strong>de</strong>vuelve la función C y aparece antes <strong>de</strong>l nombre <strong>de</strong> la<br />

función.<br />

Lista <strong>de</strong> parúmetros. Es una lista <strong>de</strong> parámetros tipificados (con tipos) que utilizan el formato<br />

sigui<strong>en</strong>te:<br />

tipo1 parámetrol, tipo2 parámetro2, . . .<br />

Cuerpo <strong>de</strong> lafunción. Se <strong>en</strong>cierra <strong>en</strong>tre llaves <strong>de</strong> apertura ({) y cierre ()). No hay punto y coma<br />

<strong>de</strong>spués <strong>de</strong> la llave <strong>de</strong> cierre.<br />

o Paso <strong>de</strong> parámetros. Posteriorm<strong>en</strong>te se verá que el paso <strong>de</strong> parámetros <strong>en</strong> C se hace siempre por<br />

valor.<br />

o No se pued<strong>en</strong> <strong>de</strong>clararfunciones anidadas.<br />

o Declaración local. Las constantes, tipos <strong>de</strong> <strong>datos</strong> y variables <strong>de</strong>claradas d<strong>en</strong>tro <strong>de</strong> la función son<br />

locales a la misma y no perduran fuera <strong>de</strong> ella.<br />

Valor <strong>de</strong>vuelto por lafunción. Mediante la palabra reservada return se <strong>de</strong>vuelve el valor <strong>de</strong> la<br />

función.<br />

Una llamada a la función produce la ejecución <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l cuerpo <strong>de</strong> la función y un<br />

retorno a la unidad <strong>de</strong> programa llamadora <strong>de</strong>spués que la ejecución <strong>de</strong> la función se ha terminado,<br />

normalm<strong>en</strong>te cuando se <strong>en</strong>cu<strong>en</strong>tra una s<strong>en</strong>t<strong>en</strong>cia return.<br />

Ejemplo 7.1<br />

Las funciones cuadrado () y suma () muestran dos ejemplos típicos <strong>de</strong> ellas.<br />

/* función que calcule los cuadrados <strong>de</strong> números <strong>en</strong>teros<br />

sucesivos a partir <strong>de</strong> un número dado (n), parámetro<br />

<strong>de</strong> la función y hasta obt<strong>en</strong>er un cuadrado que sea<br />

mayor <strong>de</strong> 1000<br />

*/<br />

void cuadrado(int n)<br />

{<br />

int cuadrado=O, q=O;<br />

while (q


v<br />

Funciones 2 13<br />

}<br />

total += x;<br />

1<br />

return total;<br />

7.2.1. Nombre <strong>de</strong> una función<br />

Un nombre <strong>de</strong> una función comi<strong>en</strong>za con una letra o un subrayado (-) y pue<strong>de</strong> cont<strong>en</strong>er tantas letras,<br />

números o subrayados como <strong>de</strong>see. El compilador ignora, sin embargo, a partir <strong>de</strong> una cantidad dada (32<br />

<strong>en</strong> BorlandInprise C, 248 <strong>en</strong> Microsoft). C es s<strong>en</strong>sible a mayúsculas, lo que significa que las letras<br />

mayúsculas y minúsculas son distintas a efectos <strong>de</strong>l nombre <strong>de</strong> la función.<br />

int max (int x, int y) ; /* nombre <strong>de</strong> la función max */<br />

double media (double xl, double x2); /* nombre <strong>de</strong> la función media */<br />

double MAX (int* m, int n);<br />

/* nombre <strong>de</strong> función MAX,<br />

distinta <strong>de</strong> max */<br />

7.2.2. Tipo <strong>de</strong> dato <strong>de</strong> retorno<br />

Si la función no <strong>de</strong>vuelve un valor int, se <strong>de</strong>be especificar el tipo <strong>de</strong> dato <strong>de</strong>vuelto (<strong>de</strong> retorno) por la<br />

función; cuando <strong>de</strong>vuelve un valor int, se pue<strong>de</strong> omitir ya que por <strong>de</strong>fecto el C asume que todas las<br />

funciones son <strong>en</strong>teras, a pesar <strong>de</strong> ello siempre convi<strong>en</strong>e especificar el tipo aun si<strong>en</strong>do <strong>de</strong> tipo int, para<br />

mejor legibilidad. El tipo <strong>de</strong>be ser uno <strong>de</strong> los tipos simples <strong>de</strong> C, tales como int, char o float, o un<br />

puntero a cualquier tipo C, o un tipo s t ruc t.<br />

int max(int x, int y) /* <strong>de</strong>vuelve un tipo int */<br />

double media(doub1e xl, double x2) /* <strong>de</strong>vuelve un tipo double */<br />

float func00 {...I /* <strong>de</strong>vuelve un float */<br />

char funcl() {...} /* <strong>de</strong>vuelve un dato char */<br />

int *func3 () {. . .} /* <strong>de</strong>vuelve un puntero a int */<br />

char *func4() {. . . I /* <strong>de</strong>vuelve un puntero a char */<br />

int func5 ( 1 I.. .}<br />

/* <strong>de</strong>vuelve un int (es opcional)*/<br />

Si una función no <strong>de</strong>vuelve un resultado, se pue<strong>de</strong> utilizar el tipo void, que se consi<strong>de</strong>ra como un<br />

tipo <strong>de</strong> dato especial. Algunas <strong>de</strong>claraciones <strong>de</strong> funciones que <strong>de</strong>vuelv<strong>en</strong> distintos tipos <strong>de</strong> resultados<br />

son:<br />

int calculo-kilometraje(int litros, int kilometros);<br />

char mayusculas(char car);<br />

float DesvEst (void) ;<br />

struct Infopersona BuscarRegistro(int num-registro);<br />

Muchas funciones no <strong>de</strong>vuelv<strong>en</strong> resultados. La razón es que se utilizan como subrutinas para<br />

realizar una tarea concreta. Una función que no <strong>de</strong>vuelve un resultado, a veces se d<strong>en</strong>omina<br />

procedimi<strong>en</strong>to. Para indicar al compilador que una función no <strong>de</strong>vuelve resultado, se utiliza el tipo <strong>de</strong><br />

retorno void, como <strong>en</strong> este ejemplo:<br />

void VisualizarResultados(f1oat Total, int num-elem<strong>en</strong>tos);<br />

Si se omite un tipo <strong>de</strong> retorno para una función, como <strong>en</strong><br />

VerResultados(f1oat Total, int longitud);<br />

el compilador supone que el tipo <strong>de</strong> dato <strong>de</strong>vuelto es int. Aunque el uso <strong>de</strong> int es opcional, por razones<br />

<strong>de</strong> claridad y consist<strong>en</strong>cia se recomi<strong>en</strong>da su uso. Así, la función anterior se pue<strong>de</strong> <strong>de</strong>clarar también:<br />

int VerResultados(f1oat Total, int longitud);


214 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

7.2.3. Resultados <strong>de</strong> una función<br />

Una función pue<strong>de</strong> <strong>de</strong>volver un Único valor. El resultado se muestra con una s<strong>en</strong>t<strong>en</strong>cia return cuya<br />

sintaxis es:<br />

return (expresión)<br />

return;<br />

El valor <strong>de</strong>vuelto (expresión) pue<strong>de</strong> ser cualquier tipo <strong>de</strong> dato excepto una función o un array. Se<br />

pued<strong>en</strong> <strong>de</strong>volver valores múltiples <strong>de</strong>volvi<strong>en</strong>do un puntero o una <strong>estructura</strong>. El valor <strong>de</strong> retorno <strong>de</strong>be<br />

seguir las mismas reglas que se aplican a un operador <strong>de</strong> asignación. Por ejemplo, no se pue<strong>de</strong> <strong>de</strong>volver<br />

un valor int, si el tipo <strong>de</strong> retorno es un puntero. Sin embargo, si se <strong>de</strong>vuelve un int y el tipo <strong>de</strong> retorno<br />

es un float, se realiza la conversión automáticam<strong>en</strong>te.<br />

Una función pue<strong>de</strong> t<strong>en</strong>er cualquier número <strong>de</strong> s<strong>en</strong>t<strong>en</strong>cias return. Tan pronto como el programa<br />

<strong>en</strong>cu<strong>en</strong>tra cualquiera <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias return, <strong>de</strong>vuelve control a la s<strong>en</strong>t<strong>en</strong>cia llamadora. La ejecución<br />

<strong>de</strong> la función termina si no se <strong>en</strong>cu<strong>en</strong>tra ninguna s<strong>en</strong>t<strong>en</strong>cia return; <strong>en</strong> este caso, la ejecución continúa<br />

hasta la llave final <strong>de</strong>l cuerpo <strong>de</strong> la función.<br />

Si el tipo <strong>de</strong> retorno es void, la s<strong>en</strong>t<strong>en</strong>cia return se pue<strong>de</strong> escribir como return; sin ninguna<br />

expresión <strong>de</strong> retorno, o bi<strong>en</strong>, <strong>de</strong> modo alternativo se pue<strong>de</strong> omitir la s<strong>en</strong>t<strong>en</strong>cia return.<br />

void funcl(void)<br />

{<br />

1<br />

puts ("Esta función no <strong>de</strong>vuelve valores") ;<br />

El valor <strong>de</strong>vuelto se suele <strong>en</strong>cerrar <strong>en</strong>tre paréntesis, pero su uso es opcional. En algunos sistemas<br />

operativos, como DOS, se pue<strong>de</strong> <strong>de</strong>volver un resultado al <strong>en</strong>torno llamador. Normalm<strong>en</strong>te el valor O, se<br />

suele <strong>de</strong>volver <strong>en</strong> estos casos.<br />

int main ( )<br />

{<br />

1<br />

puts('Prueba <strong>de</strong> un programa C, <strong>de</strong>vuelve O al sistema " );<br />

return O;<br />

Consejo<br />

Aunque no es obligatorio el uso <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia return <strong>en</strong> la Última Linea,<br />

ya que ayuda a recordar el retorno <strong>en</strong> ese punto a la función llamadora.<br />

omi<strong>en</strong>da su uso,<br />

Precaución<br />

Un error típico <strong>de</strong> programación es olvidar incluk la s<strong>en</strong>t<strong>en</strong>cia ret urn<br />

sección <strong>de</strong> código que no se ejecute. Si ninguna s<strong>en</strong>t<strong>en</strong>cia return se ejecuta,<br />

que <strong>de</strong>vuelve la función es impre<strong>de</strong>cible y pue<strong>de</strong> originar que su prog<br />

resultados incorrectos. Por ejemplo, suponga que se sitúa la s<strong>en</strong>t<strong>en</strong>cia return d<strong>en</strong>tro <strong>de</strong> una sección<br />

<strong>de</strong> código que se ejecuta condicionalm<strong>en</strong>te, tal como:


Funciones 21 5<br />

if (Total >= 0.0)<br />

return Total;<br />

Si Total es m<strong>en</strong>or que cero, no se ejecuta la s<strong>en</strong>t<strong>en</strong>cia return y el resultado <strong>de</strong> la función es<br />

un valor aleatorio (C pue<strong>de</strong> g<strong>en</strong>erar el m<strong>en</strong>saje <strong>de</strong> advert<strong>en</strong>cia "Function should return a<br />

value", que le ayudará a <strong>de</strong>tectar este posible error).<br />

/<br />

I<br />

7.2.4. Llamada a una función<br />

I<br />

Las funciones, para po<strong>de</strong>r ser ejecutadas, han <strong>de</strong> ser llamadas o invocadas. Cualquier expresión pue<strong>de</strong><br />

cont<strong>en</strong>er una llamada a una función que redirigirá el control <strong>de</strong>l programa a la función nombrada.<br />

Normalm<strong>en</strong>te la llamada a una función se realizará <strong>de</strong>s<strong>de</strong> la función principal main ( ), aunque<br />

naturalm<strong>en</strong>te también podrá ser <strong>de</strong>s<strong>de</strong> otra función.<br />

Nota<br />

La función que llama a otra función se d<strong>en</strong>ominafuncidn llamadora y la funci6n controlada se<br />

d<strong>en</strong>omina futtcidn Elurnada.<br />

La función llamada que recibe el control <strong>de</strong>l programa se ejecuta <strong>de</strong>s<strong>de</strong> el principio y cuando<br />

termina (se alcanza la s<strong>en</strong>t<strong>en</strong>cia return, o la llave <strong>de</strong> cierre (1) si se omite return) el control <strong>de</strong>l<br />

programa vuelve y retorna a la función main ( ) o a la función llamadora si no es main.<br />

1<br />

funcl O;<br />

func2 O;<br />

...<br />

ret.rirn O;<br />

void funcl 0<br />

void tunc2 O 4-<br />

i<br />

...<br />

re turn :<br />

I<br />

Figura 7.2. Traza <strong>de</strong> llamadas <strong>de</strong> funciones.


216 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En el sigui<strong>en</strong>te ejemplo se <strong>de</strong>claran dos funciones y se llaman <strong>de</strong>s<strong>de</strong> la función m ain ( ) .<br />

#inclu<strong>de</strong> <br />

void funcl (void)<br />

i<br />

puts ("Segunda función") ;<br />

return;<br />

1<br />

void func2 (void)<br />

i<br />

puts ("Tercera función") ;<br />

re turn ;<br />

1<br />

int main()<br />

{<br />

1<br />

puts("Primera función llamada main()");<br />

funcl ( 1 ; /* Segunda función llamada */<br />

func2 ( ) ; /* Tercera función llamada */<br />

puts ("main se termina") ;<br />

return O; /* Devuelve control al sistema */<br />

La salida <strong>de</strong> este programa es:<br />

Primera función llamada main ( )<br />

Segunda función<br />

Tercera función<br />

main se termina<br />

Se pue<strong>de</strong> llamar a una función y no utilizar el valor que se <strong>de</strong>vuelve. En esta llamada a función:<br />

func ( ) ; el valor <strong>de</strong> retorno no se consi<strong>de</strong>ra. El formato f unc ( ) sin argum<strong>en</strong>tos es el más simple. Para<br />

indicar que la llamada a una función no ti<strong>en</strong>e argum<strong>en</strong>tos se sitúa una palabra reservada void <strong>en</strong>tre<br />

paréntesis <strong>en</strong> la <strong>de</strong>claración <strong>de</strong> la función y posteriorm<strong>en</strong>te <strong>en</strong> lo que se d<strong>en</strong>ominará prototipo; también,<br />

con paréntesis vacíos.<br />

int main()<br />

1<br />

func ( ) ;<br />

...<br />

/* Llamada a la función */<br />

void func (void)<br />

/* Declaración <strong>de</strong> la función */<br />

t<br />

printf ("Función sin argum<strong>en</strong>tos \n");<br />

i<br />

d<strong>en</strong>tro <strong>de</strong> otra. Todo código


Funciones 21 7<br />

Ejemplo 7.2<br />

La función max <strong>de</strong>vuelve el número mayor <strong>de</strong> dos <strong>en</strong>teros.<br />

#inclu<strong>de</strong> <br />

int max(int x, int y)<br />

t<br />

if (x < y)<br />

return y;<br />

else<br />

return x;<br />

i<br />

int main()<br />

{<br />

int m, n;<br />

do I<br />

scanf ("%d%d",&m,&n);<br />

printf('Maximo <strong>de</strong> %d,%d es %d\n",max(m, n)); /*llamada a max*/<br />

}while(m ! = O) ;<br />

return O;<br />

Ejemplo 7.3<br />

Calcular la media aritmética <strong>de</strong> dos números reales.<br />

#inclu<strong>de</strong> <br />

double media(doub1e xl, double x2)<br />

{<br />

1<br />

return(x1 + x2)/2;<br />

int main()<br />

i<br />

double numl, num2, med;<br />

printf("1ntroducir dos números reales:");<br />

scanf ("%lf %1f",&numl,&num2);<br />

med = media(num1, num2);<br />

printf ("El valor medio es %.41f \n", med) ;<br />

return O;<br />

1<br />

7.3. PROTOTIPOS DE LAS FUNCIONES<br />

La <strong>de</strong>claración <strong>de</strong> una función se d<strong>en</strong>omina prototipo. Los prototipos <strong>de</strong> una función conti<strong>en</strong><strong>en</strong> la<br />

cabecera <strong>de</strong> la función, con la difer<strong>en</strong>cia <strong>de</strong> que los prototipos terminan con un punto y coma.<br />

Específicam<strong>en</strong>te un prototipo consta <strong>de</strong> los sigui<strong>en</strong>tes elem<strong>en</strong>tos: nombre <strong>de</strong> la función, una lista <strong>de</strong><br />

argum<strong>en</strong>tos <strong>en</strong>cerrados <strong>en</strong>tre paréntesis y un punto y coma. En C no es estrictam<strong>en</strong>te necesario que una<br />

función se <strong>de</strong>clare o <strong>de</strong>fina antes <strong>de</strong> su uso, no es necesario incluir el prototipo aunque si es<br />

recom<strong>en</strong>dable para que el compilador pueda hacer chequeos <strong>en</strong> las llamadas a las funciones. Los<br />

prototipos <strong>de</strong> las funciones llamadas <strong>en</strong> un programa se incluy<strong>en</strong> <strong>en</strong> la cabecera <strong>de</strong>l programa para que<br />

así sean reconocidas <strong>en</strong> todo el programa.


218 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

C recomi<strong>en</strong>da que se <strong>de</strong>clare una función si se llama a la función antes <strong>de</strong> que se <strong>de</strong>fina. *<br />

1<br />

Sintaxis<br />

t ipo-re t orno nombre- f unci Ón ( 1 i s t a-<strong>de</strong>-<strong>de</strong>cl ara ci Ónsaráme t ros );<br />

t ipo-re torno<br />

nombre- f un ci Ón<br />

1 ista-<strong>de</strong>cl aración_parárne tros<br />

Tipo <strong>de</strong>l valor <strong>de</strong>vuelto por la función o palabra reservada void<br />

si no <strong>de</strong>vuelve un valor.<br />

Nombre <strong>de</strong> la función.<br />

Lista <strong>de</strong> <strong>de</strong>claración <strong>de</strong> los parámetros <strong>de</strong> la función, separados<br />

por comas (los nombres <strong>de</strong> los párametros son opcionales, pero<br />

es bu<strong>en</strong>a práctica incluirlos para indicar lo que repres<strong>en</strong>tan).<br />

Un prototipo <strong>de</strong>clara una función y proporciona una información sufici<strong>en</strong>te al compilador para<br />

verificar que la función está si<strong>en</strong>do llamada correctam<strong>en</strong>te, con respecto al número y tipo <strong>de</strong> los<br />

parámetros y el tipo <strong>de</strong>vuelto por la función. Es obligatorio poner un punto y coma al final <strong>de</strong>l prototipo<br />

<strong>de</strong> la función con el objeto <strong>de</strong> convertirlo <strong>en</strong> una s<strong>en</strong>t<strong>en</strong>cia.<br />

double FahrACelsius(doub1e tempFahr);<br />

int max(int x, int y);<br />

int longitud(int h, int a);<br />

struct persona <strong>en</strong>trad(void);<br />

char* concat<strong>en</strong>ar(char* cl, char* c2);<br />

double int<strong>en</strong>sidad(double, double);<br />

Los prototipos se sitúan normalm<strong>en</strong>te al principio <strong>de</strong> un programa, antes <strong>de</strong> la <strong>de</strong>finición <strong>de</strong> la función<br />

main ( ) . El compilador utiliza los prototipos para validar que el número y los tipos <strong>de</strong> <strong>datos</strong> <strong>de</strong><br />

los argum<strong>en</strong>tos reales <strong>de</strong> la llamada a la función son los mismos que el número y tipo <strong>de</strong> argum<strong>en</strong>tos formales<br />

<strong>en</strong> la función llamada. Si se <strong>de</strong>tecta una inconsist<strong>en</strong>cia, se visualiza un m<strong>en</strong>saje <strong>de</strong> error. Sin prototipos,<br />

un error pue<strong>de</strong> ocurrir si un argum<strong>en</strong>to con un tipo <strong>de</strong> dato incorrecto se pasa a una función. En<br />

programas complejos, este tipo <strong>de</strong> errores son difíciles <strong>de</strong> <strong>de</strong>tectar.<br />

En C, la difer<strong>en</strong>cia <strong>en</strong>tre los conceptos <strong>de</strong>claración y <strong>de</strong>jnición es preciso t<strong>en</strong>erla clara. Cuando<br />

una <strong>en</strong>tidad se <strong>de</strong>clara, se proporciona un nombre y se listan sus características. Una <strong>de</strong>finición<br />

proporciona un nombre <strong>de</strong> <strong>en</strong>tidad y reserva espacio <strong>de</strong> memoria para esa <strong>en</strong>tidad. Una <strong>de</strong>finición indica<br />

que existe un lugar <strong>en</strong> un programa don<strong>de</strong> «existe» realm<strong>en</strong>te la <strong>en</strong>tidad <strong>de</strong>finida, mi<strong>en</strong>tras que una<br />

<strong>de</strong>claración es sólo una indicación <strong>de</strong> que algo existe <strong>en</strong> alguna posición.<br />

Una <strong>de</strong>claración <strong>de</strong> la función conti<strong>en</strong>e sólo la cabecera <strong>de</strong> la función y una vez <strong>de</strong>clarada la<br />

función, la <strong>de</strong>finición completa <strong>de</strong> la función <strong>de</strong>be existir <strong>en</strong> algún lugar <strong>de</strong>l programa, antes o <strong>de</strong>spués<br />

<strong>de</strong> main ( ) .<br />

En el sigui<strong>en</strong>te ejemplo se escribe una función area ( ) <strong>de</strong> rectángulo. En la función main ( ) se<br />

llama a <strong>en</strong>trada ( ) para pedir la base y la altura; a continuación se llama a la función area ( ) .<br />

#inclu<strong>de</strong> <br />

/* prototipos válidos */<br />

float area-rectangulo(f1oat b, float a);/* <strong>de</strong>claración */<br />

float <strong>en</strong>trada(); /* prototipo o <strong>de</strong>claración */<br />

int main ( )<br />

{<br />

float b, h;<br />

printf ("\n Base <strong>de</strong>l rectangulo: ") ;<br />

b = <strong>en</strong>trada() ;<br />

0<br />

printf ("\n Altura <strong>de</strong>l rectangulo: ") ;<br />

h = <strong>en</strong>trada() ;<br />

printf("\n Area <strong>de</strong>l rectangulo: %.2f',area_rectangulo(b,h));


Funciones 219<br />

i<br />

return O;<br />

/* <strong>de</strong>vuelve número positivo */<br />

float <strong>en</strong>trada ( )<br />

i<br />

float m;<br />

do i<br />

scan f ( I' / % f I' , &m) ;<br />

j while (m ~~0.0);<br />

return m;<br />

1<br />

/* calcula el area <strong>de</strong> un rectángulo */<br />

float area-rectangulo(f1oat b, float a)<br />

{<br />

1<br />

En este otro ejemplo se <strong>de</strong>clara la función media<br />

#inclu<strong>de</strong> <br />

double media (duble xl, double x2);<br />

int main()<br />

{<br />

1<br />

return (b*a) ;<br />

...<br />

med = media(num1, num2);<br />

...<br />

/*<strong>de</strong>claración <strong>de</strong> media*/<br />

double media(doub1e xl, double x2) /* <strong>de</strong>finición */<br />

i<br />

return (xl + x2)/2;<br />

1<br />

.<br />

o Declaraciones <strong>de</strong> una función<br />

o Antes <strong>de</strong> que una función pueda ser invocada, <strong>de</strong>be ser <strong>de</strong>clarada.<br />

6n conti<strong>en</strong>e s610 la cabecera <strong>de</strong> la función (llamado también<br />

char* copiar (char * buffer, int n);<br />

La comprobación <strong>de</strong> tipos es una acción realizada por el compilador. El compilador conoce cuales<br />

son los tipos <strong>de</strong> argum<strong>en</strong>tos que se han pasado una vez que se ha procesado un prototipo. Cuando se<br />

<strong>en</strong>cu<strong>en</strong>tra una s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> llamada a una función, el compilador confirma que el tipo <strong>de</strong> argum<strong>en</strong>to <strong>en</strong><br />

la llamada a la función es el mismo tipo que el <strong>de</strong>l argum<strong>en</strong>to correspondi<strong>en</strong>te <strong>de</strong>l prototipo. Si no son<br />

los mismos, el compilador g<strong>en</strong>era un m<strong>en</strong>saje <strong>de</strong> error. Un ejemplo <strong>de</strong> prototipo:<br />

int procesar(int a, char b, float c, double d, char *e);<br />

El compilador utiliza sólo la información <strong>de</strong> los tipos <strong>de</strong> <strong>datos</strong>. Los nombres <strong>de</strong> los argum<strong>en</strong>tos,<br />

aunque se aconsejan, no ti<strong>en</strong><strong>en</strong> significado; el propósito <strong>de</strong> los nombres es hacer la <strong>de</strong>claración <strong>de</strong> tipo3<br />

más fácil para leer y escribir. La s<strong>en</strong>t<strong>en</strong>cia preced<strong>en</strong>te se pue<strong>de</strong> escribir también así:


1<br />

220 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

int procesar(int, char, float, double, char *);<br />

Si una función no ti<strong>en</strong>e argum<strong>en</strong>tos, se ha <strong>de</strong> utilizar la palabra reservada void como lista <strong>de</strong><br />

argum<strong>en</strong>tos <strong>de</strong>l prototipo (también se pue<strong>de</strong> escribir paréntesis vacíos).<br />

int muestra(void);<br />

0<br />

Ejemplos<br />

l./* prototipo <strong>de</strong> la función cuadrado *I<br />

double cuadrado(doub1e);<br />

int main()<br />

{<br />

1<br />

double x=11.5;<br />

printf("%6.2lf al cuadrado = %8.41f \n",x,cuadrado(x));<br />

return O;<br />

double cuadrado(doub1e n)<br />

I<br />

return n*n;<br />

1<br />

2. /* prototipo <strong>de</strong> visualizar-nombre */<br />

void visualizar-nombre(char*);<br />

void main()<br />

i<br />

visualizar-nombre ("Lucas El Fuerte") ;<br />

1<br />

void visualizar-nombre(char* nom)<br />

{<br />

1<br />

printf ("Hola %s \n",nom);<br />

7.3.1. Prototipos con un número no especificado <strong>de</strong> parametros<br />

Un formato especial <strong>de</strong> prototipo es aquel que ti<strong>en</strong>e un número no especificado <strong>de</strong> argum<strong>en</strong>tos, que se<br />

repres<strong>en</strong>ta por puntos susp<strong>en</strong>sivos (...). Por ejemplo,<br />

int muestras(int a, . . . ) ;<br />

int printf(conct char *formato, ... );<br />

int scanf(const char *formato, ... );<br />

Para implem<strong>en</strong>tar una función con lista variable <strong>de</strong> parámetros es necesario utilizar unas macros<br />

(especie <strong>de</strong> funciones <strong>en</strong> línea) que están <strong>de</strong>finidas <strong>en</strong> el archivo <strong>de</strong> cabecera ctdarg.h, por<br />

consigui<strong>en</strong>te lo primero que hay que hacer es incluir dicho archivo.<br />

#inclu<strong>de</strong> <br />

En el archivo está <strong>de</strong>clarado el tipo va-1 is t , un puntero para manejar la lista <strong>de</strong> <strong>datos</strong> pasada a la<br />

función.<br />

val-list puntero;


Funciones 221<br />

La función va-start ( ) inicializa puntero, <strong>de</strong> tal forma que refer<strong>en</strong>cia al primer parámetro<br />

variable. El prototipo que ti<strong>en</strong>e:<br />

void va-start (va-list puntero, u1 timofijo) ;<br />

El segundo argum<strong>en</strong>to es el Último argum<strong>en</strong>to fijo <strong>de</strong> la función que se está implem<strong>en</strong>tando. Así<br />

paralafunciónmuestras(int a, .. .);<br />

va-start (puntero, a) ;<br />

w<br />

Con la función va-arg ( ) se obti<strong>en</strong><strong>en</strong>, consecutivam<strong>en</strong>te, los sucesivos argum<strong>en</strong>tos <strong>de</strong> la lista<br />

variable. El prototipo que ti<strong>en</strong>e<br />

tipo va-arg(va-list puntero, tipo);<br />

Don<strong>de</strong> tipo es el tipo <strong>de</strong>l argum<strong>en</strong>to variable que es captado <strong>en</strong> ese mom<strong>en</strong>to, a su vez es el tipo<br />

<strong>de</strong> dato que <strong>de</strong>vuelve va-arg ( ) . Para la función muestras ( ) si los argum<strong>en</strong>tos variables son <strong>de</strong> tipo<br />

int:<br />

int m;<br />

m = va-arg(punter0,int);<br />

La Última llamada que hay que hacer <strong>en</strong> la implem<strong>en</strong>tación <strong>de</strong> estas funciones es a va-<strong>en</strong>d ( ) . De<br />

esta forma se queda el puntero preparado para sigui<strong>en</strong>tes llamadas. El prototipo que ti<strong>en</strong>e va-<strong>en</strong>d ( ) :<br />

void va-<strong>en</strong>d(va-list puntero).<br />

Ejercicio 7.1<br />

. Una aplicación completa <strong>de</strong> una función con lista <strong>de</strong> argum<strong>en</strong>tos variables es maximo(int, ...), que<br />

calcula el máximo <strong>de</strong> n argum<strong>en</strong>tos <strong>de</strong> tipo double, don<strong>de</strong> n es el argum<strong>en</strong>to fijo que se utiliza.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> istdarg.h><br />

void maximo(int n, ... );<br />

int main (void)<br />

{<br />

1<br />

puts("\t\tPRIMERA BUSQUEDA DEL MAXIMO\n');<br />

maximo(6,3.0,4.0,-12.5,1.2,4.5,6.4);<br />

puts ("\n\t\tNUEVA BUSQUEDA DEL MAXIMO\n") ;<br />

maximo(4,5.4,17.8,5.9,-17.99) ;<br />

return O;<br />

void maximo(int n, ... )<br />

{<br />

double mx,actual;<br />

va-list puntero;<br />

int i;<br />

va-start(punter0,n);<br />

mx = actual = va-arg(punter0,double);<br />

printf("\t\tArgum<strong>en</strong>to actual: %.2lf\n",actual);<br />

for (i=2; i mx)<br />

t


222 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

mx = actual;<br />

1<br />

1<br />

printf("\t\tMáximo <strong>de</strong> la lista <strong>de</strong> %d números es %.2lf\n",n,mx);<br />

va-<strong>en</strong>d(punter0);<br />

7.4. PARÁMETROS DE UNA FUNCIÓN<br />

C siempre utiliza el método <strong>de</strong> parámetros por valor para pasar variables a funciones. Para que una<br />

función <strong>de</strong>vuelva un valor a través <strong>de</strong> un argum<strong>en</strong>to hay que pasar la dirección <strong>de</strong> la variable, y que el<br />

argum<strong>en</strong>to correspondi<strong>en</strong>te <strong>de</strong> la función sea un puntero, es la forma <strong>de</strong> conseguir <strong>en</strong> C un paso <strong>de</strong><br />

parámetro por refer<strong>en</strong>cia. Esta sección examina el mecanismo que C utiliza para pasar parámetros a<br />

funciones y cómo optimizar el paso <strong>de</strong> parámetros, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l tipo <strong>de</strong> dato que se utiliza.<br />

Suponi<strong>en</strong>do que se t<strong>en</strong>ga la <strong>de</strong>claración <strong>de</strong> una función circulo con tres argum<strong>en</strong>tos<br />

void circulo(int x, int y, int didmetro);<br />

Cuando se llama a circulo se <strong>de</strong>b<strong>en</strong> pasar tres parámetros a esta función. En el punto <strong>de</strong> llamada<br />

cada parámetro pue<strong>de</strong> ser una constante, una variable o una expresión, como <strong>en</strong> el sigui<strong>en</strong>te ejemplo:<br />

circulo(25, 40, vueltas*4);<br />

I<br />

7.4.1. Paso <strong>de</strong> parámetros por valor<br />

Pasopor valor (también llamadopaso por copia) significa que cuando C compila la función y el código<br />

que llama a la función, la función recibe una copia <strong>de</strong> los valores <strong>de</strong> los parámetros. Si se cambia el<br />

valor <strong>de</strong> un parámetro variable local, el cambio sólo afecta a la función y no ti<strong>en</strong>e efecto fuera <strong>de</strong> ella.<br />

La Figura 7.3 muestra la acción <strong>de</strong> pasar un argum<strong>en</strong>to por valor. La variable real i no se pasa, pero<br />

el valor <strong>de</strong> i, 6, se pasa a la función receptora.<br />

En la técnica <strong>de</strong> paso <strong>de</strong> parámetro por valor, la modificación <strong>de</strong> la variable (parámetro pasado) <strong>en</strong><br />

la función receptora no afecta al parámetro argum<strong>en</strong>to <strong>en</strong> la función llamadora.<br />

main 0<br />

i<br />

irit i 7 6;<br />

tunc (i); ~<br />

return O;<br />

1<br />

6<br />

*6 I<br />

printf ("8d" , ii ,<br />

lit;<br />

Figura 7.3. Paso <strong>de</strong> la variable i por valor.


Funciones 223<br />

Nota<br />

El método por <strong>de</strong>fecto <strong>de</strong> pasar parámetros es por valor, a m<strong>en</strong>os que se pas<strong>en</strong> arrays. Los arrays<br />

se pasan siempre por dirección.<br />

El sigui<strong>en</strong>te programa muestra el mecanismo <strong>de</strong> paso <strong>de</strong> parámetros por valor.<br />

/*<br />

Muestra el paso <strong>de</strong> parámetros por valor<br />

Se pue<strong>de</strong> cambiar la variable <strong>de</strong>l parámetro <strong>en</strong> la función<br />

pero su modificación no pue<strong>de</strong> salir al exterior<br />

*/<br />

#inclu<strong>de</strong> istdio.h><br />

void DemoLocal(int valor);<br />

void main (void)<br />

i<br />

int n = 10;<br />

printf("Antes <strong>de</strong> llamar a DemoLocal, n = %d\n",n);<br />

DemoLocal(n);<br />

printf('Despu6s <strong>de</strong> llamada a DemoLocal, n = %d\n",n);<br />

1<br />

void DemoLocal(int valor)<br />

{<br />

printf ( "D<strong>en</strong>tro <strong>de</strong> DemoLocal, valor = %d\n" , valor) ;<br />

valor = 999;<br />

printf ("D<strong>en</strong>tro <strong>de</strong> DemoLocal, valor = %d\n" , valor) ;<br />

1<br />

Al ejecutar este programa se visualiza la salida:<br />

Antes <strong>de</strong> llamar a DemoLocal, n = 10<br />

D<strong>en</strong>tro <strong>de</strong> DemoLocal, valor = 10<br />

D<strong>en</strong>tro <strong>de</strong> DemoLocal, valor = 999<br />

Después <strong>de</strong> llamar a DemoLocal, n = 10<br />

7.4.2. Paso <strong>de</strong> parámetros por refer<strong>en</strong>cia<br />

Cuando una función <strong>de</strong>be modificar el valor <strong>de</strong>l parámetro pasado y <strong>de</strong>volver este valor modificado a la<br />

función llamadora, se ha <strong>de</strong> utilizar el método <strong>de</strong> paso <strong>de</strong> parámetro por refer<strong>en</strong>cia o dirección.<br />

En este método el compilador pasa la dirección <strong>de</strong> memoria <strong>de</strong>l valor <strong>de</strong>l parámetro a la función.<br />

Cuando se modifica el valor <strong>de</strong>l parámetro (la variable local), este valor queda almac<strong>en</strong>ado <strong>en</strong> la misma<br />

dirección <strong>de</strong> memoria, por lo que al retornar a la función llamadora la dirección <strong>de</strong> la memoria don<strong>de</strong><br />

se almac<strong>en</strong>ó el parámetro cont<strong>en</strong>drá el valor modificado. Para pasar una variable por refer<strong>en</strong>cia, el<br />

símbolo & <strong>de</strong>be prece<strong>de</strong>r al nombre <strong>de</strong> la variable y el parámetro variable correspondi<strong>en</strong>te <strong>de</strong> la función<br />

<strong>de</strong>be <strong>de</strong>clararse como puntero.<br />

float x;<br />

int y;<br />

<strong>en</strong>trada(&x,&y);<br />

...<br />

void <strong>en</strong>trada(float* x, int* y)<br />

C permite utilizar punteros para implem<strong>en</strong>tar parámetros por refer<strong>en</strong>cia, ya que por <strong>de</strong>fecto <strong>en</strong> C el<br />

paso <strong>de</strong> parámetros es por valor.


224 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

/* método <strong>de</strong> paso por refer<strong>en</strong>cia, mediante punteros */<br />

void intercambio(int* a, int* b)<br />

i<br />

int aux = *a;<br />

*a = *b;<br />

*b = aux;<br />

1<br />

En la llamada sigui<strong>en</strong>te, la función intercambio ( ) utiliza las expresiones *a y *b para acce<strong>de</strong>r<br />

a los <strong>en</strong>teros refer<strong>en</strong>ciados por las direcciones <strong>de</strong> las variables i y j :<br />

int i = 3, j = 50;<br />

printf("i = %d y j = %d \n", i,j);<br />

intercambio (&i, & j ) ;<br />

printf ("i = %d y j = %d \n", i,j) ;<br />

La llamada a la función intercambio ( ) <strong>de</strong>be pasar las direcciones <strong>de</strong> las variables intercambiadas.<br />

El operador & <strong>de</strong>lante <strong>de</strong> una variable significa «dame la direccion <strong>de</strong> la variable».<br />

double x;<br />

&x ; /* dirección <strong>en</strong> memoria <strong>de</strong> x */<br />

Una variable, o parámetro puntero se <strong>de</strong>clara poni<strong>en</strong>do el asterisco ( * ) antes <strong>de</strong>l nombre <strong>de</strong> la<br />

variable. Las variables p, r y q son punteros a distintos tipos.<br />

char* p; /* variable puntero a char */<br />

int * r; /* variable puntero a int */<br />

double* q; /* variable puntero a double */<br />

7.4.3. Difer<strong>en</strong>cias <strong>en</strong>tre paso <strong>de</strong> variables por valor y por refer<strong>en</strong>cia<br />

Las reglas que se han <strong>de</strong> seguir cuando se transmit<strong>en</strong> variables por valor y por refer<strong>en</strong>cia son las<br />

sigui<strong>en</strong>tes:<br />

O los parámetros valor recib<strong>en</strong> copias <strong>de</strong> los valores <strong>de</strong> los argum<strong>en</strong>tos que se les pasan;<br />

O la asignación a parámetros valor <strong>de</strong> una función nunca cambian el valor <strong>de</strong>l argum<strong>en</strong>to original<br />

pasado a los parámetros;<br />

O los parámetros para el paso por refer<strong>en</strong>cia (<strong>de</strong>clarados con * , punteros) recib<strong>en</strong> la dirección <strong>de</strong><br />

los argum<strong>en</strong>tos pasados; a estos les <strong>de</strong>be <strong>de</strong> prece<strong>de</strong>r <strong>de</strong>l operador &, excepto los arrays;<br />

O <strong>en</strong> una función, las asignaciones a parámetros refer<strong>en</strong>cia (punteros) cambian los valores <strong>de</strong> los<br />

argum<strong>en</strong>tos originales.<br />

Por ejemplo, la escritura <strong>de</strong> una función potrat ( ) para cambiar los cont<strong>en</strong>idos <strong>de</strong> dos variables,<br />

requiere que los <strong>datos</strong> puedan ser modificados.<br />

Paso por valor<br />

Paso par refer<strong>en</strong>cia<br />

float a, b; float a, b;<br />

potratl(f1oat x,float y) potrat2(float* x,float* y)<br />

I {<br />

1 I<br />

Sólo <strong>en</strong> el caso <strong>de</strong> potrat2 los valores <strong>de</strong> a y b se cambiarán. Veamos una aplicación completa <strong>de</strong><br />

ambas funciones:<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong>


Funciones 225<br />

void potratl(float, float);<br />

void potrat2(float*, float*)<br />

void main()<br />

{<br />

float a, b;<br />

a = 5.0; b = 1.0e2;<br />

potratl (a, b) ;<br />

printf("\n a = %.1f b = %.lt",a,b);<br />

potrat2 (a, b) ;<br />

Printf("\n a = %.lf b = %.lf',a,b);<br />

i<br />

void potratl(f1oat x, float y)<br />

t<br />

x = x*x;<br />

Y = sqrt(y);<br />

I<br />

void potrat2(float* x, float* y)<br />

1<br />

*X = (*x)*(*x);<br />

*y = sqrt (*y);<br />

}<br />

La ejecución <strong>de</strong>l programa producirá:<br />

a = 5.0 b = 100.0<br />

a = 25.0 b = 10.0<br />

Nota<br />

Todos los parámetros <strong>en</strong> G se pasan por valor. C no ti<strong>en</strong>e parhetros por refer<strong>en</strong>cia, hay que<br />

hacerlo con punteros y el operador &.<br />

Se pue<strong>de</strong> observar <strong>en</strong> el programa cómo se acce<strong>de</strong> a los punteros, el operador * precedi<strong>en</strong>do al<br />

parámetro puntero <strong>de</strong>vuelve el cont<strong>en</strong>ido.<br />

7.4.4. Parámetros const <strong>de</strong> una función<br />

Con el objeto <strong>de</strong> añadir seguridad adicional a las funciones, se pue<strong>de</strong> añadir a una <strong>de</strong>scripción <strong>de</strong> un<br />

parámetro el especificador const, que indica al compilador que sólo es <strong>de</strong> lectura <strong>en</strong> el interior <strong>de</strong> la<br />

función. Si se int<strong>en</strong>ta escribir <strong>en</strong> este parámetro se producirá un m<strong>en</strong>saje <strong>de</strong> error <strong>de</strong> compilación.<br />

void fl(const int, const int*);<br />

void f2(int, int const");<br />

void fl(const int x, const i.nt* y)<br />

t<br />

x = 10; /* error por cambiar un objeto constante*/<br />

*y = 11; /* error por cambiar un objeto constante*/<br />

y = &x; /* correcto */<br />

1<br />

void f2(int x, int const* y)<br />

J<br />

x = 10; /* correcto */<br />

*y = 11; /* error */<br />

y = &x; /* correcto */<br />

i<br />

i<br />

I# ,I


226 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

-1<br />

La Tabla 7.1 muestra un resum<strong>en</strong> <strong>de</strong>l comportami<strong>en</strong>to <strong>de</strong> los difer<strong>en</strong>tes tipos <strong>de</strong> parámetros.<br />

Tabla 7.1. Paso <strong>de</strong> parámetros <strong>en</strong> C.<br />

Parámetro especificado como: Item pasado por Cambia item d<strong>en</strong>tro Modifica parámetros<br />

<strong>de</strong> la función al exterior<br />

int item valor Si NO<br />

const int item valor NO No<br />

int* item por dirección Si Si<br />

const int* item por direccirín No su cont<strong>en</strong>ido NO<br />

7.5. FUNCIONES EN LINEA, MACROS CON ARGUMENTOS<br />

Una función normal es un bloque <strong>de</strong> código que se llama <strong>de</strong>s<strong>de</strong> otra función. El compilador g<strong>en</strong>era<br />

código para situar la dirección <strong>de</strong> retorno <strong>en</strong> la pila. La dirección <strong>de</strong> retorno es la dirección <strong>de</strong> la<br />

s<strong>en</strong>t<strong>en</strong>cia que sigue a la instrucción que llama a la función. A continuación, el compilador g<strong>en</strong>era código<br />

que sitúa cualquier argum<strong>en</strong>to <strong>de</strong> la función <strong>en</strong> la pila a medida que se requiera. Por último, el<br />

compilador g<strong>en</strong>era una instrucción <strong>de</strong> llamada que transfiere el control a la función.<br />

float fesp(f1oat x)<br />

i<br />

return (X*X + 2*~ -1);<br />

1<br />

Las funciones <strong>en</strong> línea sirv<strong>en</strong> para aum<strong>en</strong>tar la velocidad <strong>de</strong> su programa. Su uso es conv<strong>en</strong>i<strong>en</strong>te<br />

cuando la función es una expresión, su código es pequeño y se utiliza muchas veces <strong>en</strong> el programa.<br />

Realm<strong>en</strong>te no son funciones, el preprocesador expan<strong>de</strong> o sustituye la expresión cada vez que es llamada.<br />

Así la anterior función pue<strong>de</strong> sustituirse:<br />

#<strong>de</strong>fine fesp(x) (x*x + 2*x -1)<br />

En este programa se realizan cálculos <strong>de</strong> la función para valores <strong>de</strong> x <strong>en</strong> un intervalo.<br />

#inclu<strong>de</strong> istdio.h><br />

#<strong>de</strong>fine fesp(x) (x*x + 2*x -1)<br />

void main()<br />

i<br />

float x;<br />

for (x = 0.0; x


Funciones 227<br />

argum<strong>en</strong>tos (función<br />

<strong>en</strong> línea) se inserta<br />

directam<strong>en</strong>te<br />

S<strong>en</strong>t<strong>en</strong>cias<br />

-7<br />

Funciones<br />

comunes se llaman<br />

normalm<strong>en</strong>te<br />

...<br />

S<strong>en</strong>t<strong>en</strong>cias<br />

Figura 7.4. Código g<strong>en</strong>erado por una función fuera <strong>de</strong> línea.<br />

con una función normal, y el código <strong>de</strong> llamada suplem<strong>en</strong>tario es 25 bytes por cada llamada, el tamaño<br />

se increm<strong>en</strong>ta <strong>en</strong> una cantidad insignificante.<br />

La Figura 7.5 ilustra la sintaxis g<strong>en</strong>eral <strong>de</strong> una macro con argum<strong>en</strong>tos.<br />

#<strong>de</strong>fine NombreMacso(pardmetros sin tipos) expresión-texto<br />

REGLA: La <strong>de</strong>finición <strong>de</strong> una macro sólo pue<strong>de</strong> ocupar una línea. Se pue<strong>de</strong> prolongar la línea<br />

con el caracter \ ai find <strong>de</strong> la línea.<br />

Figura 7.5. Código <strong>de</strong> una macro con argum<strong>en</strong>tos.<br />

La Tabla 7.2 resume las v<strong>en</strong>tajas y <strong>de</strong>sv<strong>en</strong>tajas <strong>de</strong> situar un código <strong>de</strong> una función <strong>en</strong> una macro o<br />

fuera <strong>de</strong> línea (función normal):<br />

Tabla 7.2. V<strong>en</strong>tajas y <strong>de</strong>sv<strong>en</strong>tajas <strong>de</strong> macros.<br />

Macros (funciones <strong>en</strong> línea)<br />

Funciones fuera <strong>de</strong> línea<br />

V<strong>en</strong>tajas<br />

Rápida <strong>de</strong> ejecutar.<br />

Pequeño tamaño <strong>de</strong> código.<br />

Desv<strong>en</strong>tajas<br />

Tamaño <strong>de</strong> código gran<strong>de</strong>.<br />

L<strong>en</strong>ta <strong>de</strong> ejecución.<br />

7.5.1. Creación <strong>de</strong> macros con argum<strong>en</strong>tos<br />

Para crear una macro con argum<strong>en</strong>tos utilizar la sintaxis:<br />

#<strong>de</strong>fine NombreMacro(par2hetro.s sin tipos) expresión-texto<br />

La <strong>de</strong>finición ocupará sólo una línea, aunque si se necesitan más texto, situar una barra invertida ( \ ) al<br />

final <strong>de</strong> la primera línea y continuar <strong>en</strong> la sigui<strong>en</strong>te, <strong>en</strong> caso <strong>de</strong> ser necesarias más líneas proce<strong>de</strong>r <strong>de</strong><br />

igual forma; <strong>de</strong> esa forma se pue<strong>de</strong> formar una expresión más compleja. Entre el nombre <strong>de</strong> la macro y<br />

los paréntesis <strong>de</strong> la lista <strong>de</strong> argum<strong>en</strong>tos no pue<strong>de</strong> haber espacios <strong>en</strong> blanco. Por ejemplo, la función<br />

media <strong>de</strong> tres valores se pue<strong>de</strong> escribir:<br />

#<strong>de</strong>fine MEDIA3(x,y,z) ((x) + (y) + (z))/3.0


228 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En este segm<strong>en</strong>to <strong>de</strong> código se invoca a MEDIA3<br />

double a = 2.9;<br />

printf("\t %If 'I, MEDIA3(a,4.5,7));<br />

En esta llamada a MEDIA3 se pasan argum<strong>en</strong>tos <strong>de</strong> tipo distinto. Es importante t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta que<br />

<strong>en</strong> las macros con argum<strong>en</strong>tos no hay comprobación <strong>de</strong> tipos. Para evitar problemas <strong>de</strong> prioridad <strong>de</strong><br />

operadores, es conv<strong>en</strong>i<strong>en</strong>te <strong>en</strong>cerrar <strong>en</strong>tre paréntesis cada argum<strong>en</strong>to <strong>en</strong> la expresión <strong>de</strong> <strong>de</strong>finición e<br />

incluso <strong>en</strong>cerrar <strong>en</strong>tre paréntesis toda la expresión.<br />

En la sigui<strong>en</strong>te macro, la <strong>de</strong>finición <strong>de</strong> la expresión ocupa más <strong>de</strong> una línea.<br />

#<strong>de</strong>fine FUNCION3 (x) { \<br />

if í (x) e1.0 ) \<br />

(- (x)* (x) +3) ; \<br />

else if ((x)


T<br />

Funciones 229<br />

Normalm<strong>en</strong>te la posición <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia <strong>en</strong> el programa <strong>de</strong>termina el ámbito. Los especificadores<br />

<strong>de</strong> clases <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to, static, extern, auto y register, pued<strong>en</strong> afectar al ámbito. El<br />

sigui<strong>en</strong>te fragm<strong>en</strong>to <strong>de</strong> progranla ilustra cada tipo <strong>de</strong> ámbito:<br />

int i; /* Ámbito <strong>de</strong> programa */<br />

static int j; /* Ámbito <strong>de</strong> archivo */<br />

float func(int k) /* k, ámbito <strong>de</strong> función */<br />

i<br />

int m; /* Ámbito <strong>de</strong> bloque */<br />

7.6.1. Ámbito <strong>de</strong>l programa<br />

Las variables que ti<strong>en</strong><strong>en</strong> ámbito <strong>de</strong> progrumu pued<strong>en</strong> ser refer<strong>en</strong>ciadas por cualquier función <strong>en</strong> el<br />

programa completo; tales variables se llaman variahles globules. Para hacer una variable global,<br />

<strong>de</strong>clárela simplem<strong>en</strong>te al principio <strong>de</strong> un programa, fuera <strong>de</strong> cualquier función.<br />

int g, h; /* variables globales */<br />

main ( )<br />

i<br />

...<br />

I<br />

Una variable global es visible («se conoc<strong>en</strong>) <strong>de</strong>s<strong>de</strong> su punto <strong>de</strong> <strong>de</strong>finición <strong>en</strong> el archivo fu<strong>en</strong>te. Es<br />

<strong>de</strong>cir, si se <strong>de</strong>fine una variable global, cualquier línea <strong>de</strong>l resto <strong>de</strong>l programa, no importa cuantas<br />

funciones y líneas <strong>de</strong> código le sigan, podrá utilizar esa variable.<br />

#inclu<strong>de</strong> cstdio.h><br />

#inclu<strong>de</strong> cmath.h><br />

float v<strong>en</strong>tas, b<strong>en</strong>eficios; /* variables globales */<br />

void f3 (void)<br />

1<br />

void f 1 (void)<br />

i<br />

...<br />

1<br />

void main()<br />

{<br />

Consejo<br />

Declare todas las variables <strong>en</strong> la parte superior <strong>de</strong> su programa. Aunque se pued<strong>en</strong> <strong>de</strong>finir tales<br />

variables <strong>en</strong>tre dos funciones, podría realizar cualquier cambio <strong>en</strong> su programa <strong>de</strong> modo más<br />

rápido, si sitúa las variables globales al principio <strong>de</strong>l programa.


230 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

7.6.2. Ámbito <strong>de</strong>l archivo fu<strong>en</strong>te<br />

Una variable que se <strong>de</strong>clara fuera <strong>de</strong> cualquier función y cuya <strong>de</strong>claración conti<strong>en</strong>e la palabra reservada<br />

static ti<strong>en</strong>e ámbito <strong>de</strong> archivofu<strong>en</strong>te. Las variables con este ámbito se pued<strong>en</strong> refer<strong>en</strong>cia <strong>de</strong>s<strong>de</strong> el<br />

punto <strong>de</strong>l programa <strong>en</strong> que están <strong>de</strong>claradas hasta el final <strong>de</strong>l archivo fu<strong>en</strong>te. Si un archivo fu<strong>en</strong>te ti<strong>en</strong>e<br />

más <strong>de</strong> una función, todas las funciones que sigu<strong>en</strong> a la <strong>de</strong>claración <strong>de</strong> la variable pued<strong>en</strong> refer<strong>en</strong>ciarla.<br />

En el ejemplo sigui<strong>en</strong>te, i ti<strong>en</strong>e ámbito <strong>de</strong> archivo fu<strong>en</strong>te:<br />

static int i;<br />

void func (void)<br />

{<br />

7.6.3. Ámbito <strong>de</strong> una función<br />

I<br />

I<br />

Una variable que ti<strong>en</strong>e ámbito <strong>de</strong> una función se pue<strong>de</strong> refer<strong>en</strong>ciar <strong>de</strong>s<strong>de</strong> cualquier parte <strong>de</strong> la función.<br />

Las variables <strong>de</strong>claradas d<strong>en</strong>tro <strong>de</strong>l cuerpo <strong>de</strong> la función se dice que son locales a la función. Las<br />

variables locales no se pued<strong>en</strong> utilizar fuera <strong>de</strong>l ámbito <strong>de</strong> la función <strong>en</strong> que están <strong>de</strong>finidas.<br />

void calculo(void)<br />

{<br />

1<br />

double x, r, t ; /* Ámbito <strong>de</strong> la función */<br />

...<br />

7.6.4. Ámbito <strong>de</strong> bloque<br />

Una variable <strong>de</strong>clarada <strong>en</strong> un bloque ti<strong>en</strong>e ámbito <strong>de</strong> bloque y pue<strong>de</strong> ser refer<strong>en</strong>ciada <strong>en</strong> cualquier parte<br />

<strong>de</strong>l bloque, <strong>de</strong>s<strong>de</strong> el punto <strong>en</strong> que está <strong>de</strong>clarada hasta el final <strong>de</strong>l bloque. Las variables locales<br />

<strong>de</strong>claradas d<strong>en</strong>tro <strong>de</strong> una función ti<strong>en</strong><strong>en</strong> ámbito <strong>de</strong> bloque <strong>de</strong> la función; no son visibles fuera <strong>de</strong>l bloque.<br />

En el sigui<strong>en</strong>te ejemplo, i es una variable local:<br />

void funcl (int x)<br />

t<br />

int i;<br />

for (i = x; i < x+10; it+)<br />

printf ("i = %d \n",i*i);<br />

1<br />

Una variable local <strong>de</strong>clarada <strong>en</strong> un bloque anidado sólo es visible <strong>en</strong> el interior <strong>de</strong> ese bloque.<br />

float func (int j )<br />

i<br />

if (j > 3)<br />

I<br />

int i;<br />

for (i = O; i < 20; i++)<br />

funcl (i) ;<br />

1<br />

/* aquí ya no es visible i */<br />

1;


7.6.5. Variables locales<br />

A<strong>de</strong>más <strong>de</strong> t<strong>en</strong>er un ámbito restringido, las variables locales son especiales por otra razón: exist<strong>en</strong> <strong>en</strong><br />

memoria sólo cuando la función está activa (es <strong>de</strong>cir, mi<strong>en</strong>tras se ejecutan las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> la función).<br />

Cuando la función no se está ejecutando, sus variables locales no ocupan espacio <strong>en</strong> memoria, ya que<br />

no exist<strong>en</strong>. Algunas reglas que sigu<strong>en</strong> las variables locales son:<br />

Los nombres <strong>de</strong> las variables locales no son únicos. Dos o más funciones pued<strong>en</strong> <strong>de</strong>finir la misma<br />

variable test. Cada variable es distinta y pert<strong>en</strong>ece a su función específica.<br />

o Las variables locales <strong>de</strong> las funciones no exist<strong>en</strong> <strong>en</strong> tnemoria hasta que se ejecute la función. Por<br />

esta razón, múltiples funciones pued<strong>en</strong> compartir la misma memoria para sus variables locales<br />

(pero no al mismo tiempo).<br />

7.7. CLASES DE ALMACENAMIENTO<br />

Los especificadores <strong>de</strong> clases (tipos) <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to permit<strong>en</strong> modificar el ámbito <strong>de</strong> una variable.<br />

Los especificadores pued<strong>en</strong> ser uno <strong>de</strong> los sigui<strong>en</strong>tes: auto, extern, register, static y<br />

type<strong>de</strong>f.<br />

7.7.1. Variables automáticas<br />

Las variables que se <strong>de</strong>claran d<strong>en</strong>tro <strong>de</strong> una función se dice que son automáticas (auto), significando<br />

que se les asigna espacio <strong>en</strong> memoria automáticam<strong>en</strong>te a la <strong>en</strong>trada <strong>de</strong> la función y se les libera el<br />

espacio tan pronto se sale <strong>de</strong> dicha función. La palabra reservada auto es opcional.<br />

auto int Total; es igual que int Total;<br />

Normalm<strong>en</strong>te no se especifica la palabra auto.<br />

7.7.2. Variables externas<br />

A veces se pres<strong>en</strong>ta el problema <strong>de</strong> que una función necesita utilizar una variable que otra función<br />

inicializa. Como las variables locales sólo exist<strong>en</strong> temporalm<strong>en</strong>te mi<strong>en</strong>tras se está ejecutando su función,<br />

no pued<strong>en</strong> resolver el problema. ¿Cómo se pue<strong>de</strong> resolver <strong>en</strong>tonces el problema? En es<strong>en</strong>cia, <strong>de</strong> lo que<br />

se trata es <strong>de</strong> que una función <strong>de</strong> un archivo <strong>de</strong> código fu<strong>en</strong>te utilice una variable <strong>de</strong>finida <strong>en</strong> otro<br />

archivo. Una solución es <strong>de</strong>clarar la variable local con la palabra reservada extern. Cuando una variable<br />

se <strong>de</strong>clara externa, se indica al compilador que el espacio <strong>de</strong> la variable está <strong>de</strong>finida <strong>en</strong> otro lugar.<br />

/* variables externas: parte 1 */<br />

/* archivo fu<strong>en</strong>te exter1.c */<br />

#inclu<strong>de</strong> <br />

extern void leerReal(void);/* función <strong>de</strong>finida <strong>en</strong> otro archivo; <strong>en</strong> este<br />

caso no es necesario extern */<br />

float f;<br />

int main()<br />

i<br />

leerReal ( ) ;<br />

printf ("Valor <strong>de</strong> f = %f", f) ;<br />

return O;<br />

i


232 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

/*variables externas: parte 2 */<br />

/* archivo fu<strong>en</strong>te exter2.c */<br />

#inclu<strong>de</strong> istdio.h><br />

void leerReal(void)<br />

i<br />

extern float f ;<br />

1<br />

printf("1ntroduzca valor <strong>en</strong> coma flotante: " );<br />

scanf ("%f",&f);<br />

En el archivo EXTERS . c la <strong>de</strong>claración externa <strong>de</strong> ,f indica al compilador que ,f se ha <strong>de</strong>finido <strong>en</strong><br />

otra parte (archivo). Posteriorm<strong>en</strong>te, cuando estos archivos se <strong>en</strong>lac<strong>en</strong>, las <strong>de</strong>claraciones se combinan <strong>de</strong><br />

modo que se referirán a las mismas posiciones <strong>de</strong> memoria.<br />

7.7.3. Variables registro<br />

Otro tipo <strong>de</strong> variable C es la variable registro. Precedi<strong>en</strong>do a la <strong>de</strong>claración <strong>de</strong> una variable con la<br />

palabra reservada register, se sugiere al compilador que la variable se almac<strong>en</strong>e <strong>en</strong> uno <strong>de</strong> los<br />

registros hardware <strong>de</strong>l microprocesador. La palabra register es una suger<strong>en</strong>cia al compilador y no una<br />

ord<strong>en</strong>. La familia <strong>de</strong> microprocesadores 80x86 no ti<strong>en</strong>e muchos registros hardware <strong>de</strong> reserva, por lo que<br />

el compilador pue<strong>de</strong> <strong>de</strong>cidir ignorar sus suger<strong>en</strong>cias. Para <strong>de</strong>clarar una variable registro, utilice una<br />

<strong>de</strong>claración similar a:<br />

register int k;<br />

Una variable registro <strong>de</strong>be ser local a una función, nunca pue<strong>de</strong> ser global al programa completo.<br />

El uso <strong>de</strong> la variable register no garantiza que un valor se almac<strong>en</strong>e <strong>en</strong> un registro. Esto sólo<br />

suce<strong>de</strong>rá si existe un registro disponible. Si no exist<strong>en</strong> registros sufici<strong>en</strong>tes, C ignora la palabra reservada<br />

register y crea la variable localm<strong>en</strong>te como ya se conoce.<br />

Una aplicación típica <strong>de</strong> una variable registro es como variable <strong>de</strong> control <strong>de</strong> un bucle. Guardando<br />

la variable <strong>de</strong> control <strong>de</strong> un bucle <strong>en</strong> un registro, se reduce el tiempo que la CPU requiere para buscar<br />

el valor <strong>de</strong> la variable <strong>de</strong> la memoria. Por ejemplo,<br />

register int indice;<br />

for (indice = O; indice < 1000; indice++) ...<br />

7.7.4. Variables estáticas<br />

Las variables estáticas son opuestas, <strong>en</strong> su significado, a las variables automáticas. Las variables<br />

estáticas no se borran (no se pier<strong>de</strong> su valor) cuando la función termina y, <strong>en</strong> consecu<strong>en</strong>cia, reti<strong>en</strong><strong>en</strong><br />

sus valores <strong>en</strong>tre llamadas a una función. Al contrario que las variables locales normales, una variable<br />

static se inicializa sólo una vez. Se <strong>de</strong>claran precedi<strong>en</strong>do a la <strong>de</strong>claración <strong>de</strong> la variable con la palabra<br />

reservada static.<br />

f unc-uno ( )<br />

{<br />

1<br />

int i;<br />

static int j = 25; /*j, k variables estjticas */<br />

static int k = 100;<br />

...<br />

Las variables estáticas se utilizan normalm<strong>en</strong>te para mant<strong>en</strong>er valores <strong>en</strong>tre llamadas a funciones.


Funciones 233<br />

float ResultadosTotales(f1oat valor)<br />

i<br />

static float suma;<br />

I<br />

suma = suma + valor;<br />

return suma;<br />

En la función anterior se utiliza suma para acumular sumas a través <strong>de</strong> sucesivas llamadas a<br />

Res u 1 t ado s So t a 1 e s .<br />

Ejercicio 7.3<br />

Una aplicación <strong>de</strong> una variable static <strong>en</strong> una,funrión es la que nos permite obt<strong>en</strong>er la serie <strong>de</strong> niimeros<br />

<strong>de</strong>.fibonacci. El ejercicio lo plantearnos: dado un <strong>en</strong>tero n, obt<strong>en</strong>er los n primeros números <strong>de</strong> la serie<br />

<strong>de</strong> jhonacci.<br />

Análisis<br />

La secu<strong>en</strong>cia <strong>de</strong> números <strong>de</strong> fibonacci: O, I, I, 2, 3,5, 8, 13.. . , se obti<strong>en</strong>e parti<strong>en</strong>do <strong>de</strong> los números O,<br />

1 y a partir <strong>de</strong> ellos cada número se obti<strong>en</strong>e sumando los dos anteriores:<br />

a,, = % I + ( 4 2<br />

La función fibonacci ti<strong>en</strong>e dos variables estáticas, x e y . Se inicializan x a O e y a 1 ; a partir <strong>de</strong> esos<br />

valores se calcula el valor actual, y, se <strong>de</strong>ja preparado x para la sigui<strong>en</strong>te llamada. Al ser variables<br />

estáticas manti<strong>en</strong><strong>en</strong> el valor <strong>en</strong>tre llamada y llamada.<br />

#inclu<strong>de</strong> <br />

long int fibonaccio;<br />

int main()<br />

{<br />

int n,i;<br />

printf ("\nCuantos numeros <strong>de</strong> fibonacci ?: ") ;<br />

scanf ("%d", &n);<br />

printf ("\nSecu<strong>en</strong>cia <strong>de</strong> f ibonacci : O, 1") ;<br />

for (i=2; i


234 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

7.8. CONCEPTO Y USO DE FUNCIONES DE BIBLIOTECA<br />

Todas las versiones <strong>de</strong>l l<strong>en</strong>guaje C ofrec<strong>en</strong> con una biblioteca estándar <strong>de</strong> funciones <strong>en</strong> tiempo <strong>de</strong><br />

ejecución que proporcionan soporte para operaciones utilizadas con más frecu<strong>en</strong>cia. Estas funciones<br />

permit<strong>en</strong> realizar una operación con sólo una llamada a la función (sin necesidad <strong>de</strong> escribir su código<br />

fu<strong>en</strong>te).<br />

Las funciones estúnúur o preúefinidus, como así se d<strong>en</strong>ominan las funciones pert<strong>en</strong>eci<strong>en</strong>tes a la<br />

biblioteca estándar, se divid<strong>en</strong> <strong>en</strong> grupos; todas las funciones que pert<strong>en</strong>ec<strong>en</strong> al mismo grupo se <strong>de</strong>claran<br />

<strong>en</strong> el mismo archivo <strong>de</strong> cabecera.<br />

Los nombres <strong>de</strong> los archivos <strong>de</strong> cabecera estándar utilizados <strong>en</strong> nuestro programa se muestran a<br />

continuación <strong>en</strong>cerrados <strong>en</strong>tre corchetes tipo ángulo:<br />

<br />

<br />

<br />

<br />

e r rno . h<br />

-..setjmp.h><br />

<br />

<br />

<br />

<br />

En los módulos <strong>de</strong> programa se pued<strong>en</strong> incluir líneas #inclu<strong>de</strong> con los archivos <strong>de</strong> cabecera<br />

correspondi<strong>en</strong>tes <strong>en</strong> cualquier ord<strong>en</strong>, y estas líneas pued<strong>en</strong> aparecer más <strong>de</strong> una vez.<br />

Para utilizar una función o un macro, se <strong>de</strong>be conocer su número <strong>de</strong> argum<strong>en</strong>tos, sus tipos y el tipo<br />

<strong>de</strong> sus valores <strong>de</strong> retorno. Esta información se proporcionará <strong>en</strong> los prototipos <strong>de</strong> la función. La<br />

s<strong>en</strong>t<strong>en</strong>cia #inclu<strong>de</strong> mezcla el archivo <strong>de</strong> cabecera <strong>en</strong> su programa.<br />

Algunos <strong>de</strong> los grupos <strong>de</strong> funciones <strong>de</strong> biblioteca más usuales son:<br />

O E/S estándar (para operaciones <strong>de</strong> EntraddSalida);<br />

O matemáticas (para operaciones matemáticas);<br />

O rutinas estándar (para operaciones estándar <strong>de</strong> programas);<br />

O visualizar v<strong>en</strong>tana <strong>de</strong> texto;<br />

O <strong>de</strong> conversión (rutinas <strong>de</strong> conversión <strong>de</strong> caracteres y cad<strong>en</strong>as);<br />

O <strong>de</strong> diagnóstico (proporcionan rutinas <strong>de</strong> <strong>de</strong>puración incorporada);<br />

O <strong>de</strong> manipulación <strong>de</strong> memoria;<br />

O control <strong>de</strong>l proceso;<br />

O clasificación (ord<strong>en</strong>ación);<br />

O directorios;<br />

O fecha y hora;<br />

O <strong>de</strong> interfaz;<br />

O diversas;<br />

O búsqueda;<br />

O manipulación <strong>de</strong> cad<strong>en</strong>as;<br />

O gráficos.<br />

Se pued<strong>en</strong> incluir tantos archivos <strong>de</strong> cabecera como sean necesarios <strong>en</strong> sus archivos <strong>de</strong> programa,<br />

incluy<strong>en</strong>do sus propios archivos <strong>de</strong> cabecera que <strong>de</strong>fin<strong>en</strong> sus propias funciones.<br />

En este capítulo se estudiarán las funciones más sobresali<strong>en</strong>tes y más utilizadas <strong>en</strong> programación.<br />

7.9. FUNCIONES DE CARÁCTER<br />

El archivo <strong>de</strong> cabecera <strong>de</strong>fine un grupo <strong>de</strong> funciones/macros <strong>de</strong> manipulación <strong>de</strong> caracteres.<br />

Todas las funciones <strong>de</strong>vuelv<strong>en</strong> un resultado <strong>de</strong> valor verda<strong>de</strong>ro (distinto <strong>de</strong> cero) o falso (cero).<br />

Para utilizar cualquiera <strong>de</strong> las funciones (Tabla 7.3) no se olvi<strong>de</strong> incluir el archivo <strong>de</strong> cabecera<br />

CTYPE . H <strong>en</strong> la parte superior <strong>de</strong> cualquier programa que haga uso <strong>de</strong> esas funciones.


Funciones 235<br />

Tabla 7.3. Funciones <strong>de</strong> caracteres.<br />

Función<br />

Prueba (test) <strong>de</strong><br />

int isalpha(int c) Letra mayúscula o minúscula.<br />

int isdigit (int c) Dígito <strong>de</strong>cimal.<br />

int isupper(int c) Letra mayúscula (A- Z).<br />

int islower (int c) Letra minúscula (a- z).<br />

int isalnum(int c) letra o dígito; isalpha(c) I lisdigit(c)<br />

int iscntrl(int c) Carácter <strong>de</strong> control.<br />

int isxdigit(int c) Dígito hexa<strong>de</strong>cimal.<br />

int isprint(int c) Carácter imprimible incluy<strong>en</strong>do ESPACIO.<br />

int isgraph(int c) Carácter imprimible excepto ESPACIO.<br />

int isspace(int c) ESPACIO, AVANCE DE PÁGINA, NUEVA LINEA, RETORNO DE<br />

CARRO, TAB ULAC IÓN , TABULACI~N VERTICAL.<br />

int ispunct(int c) Carácter imprimible no espacio, dígito o letra.<br />

int toupper(int c) Convierte a letras mayúsculas.<br />

int tolower(int c) Convierte a letras minúsculas.<br />

7.9.1. Comprobación alfabética y <strong>de</strong> dígitos<br />

Exist<strong>en</strong> varias funciones que sirv<strong>en</strong> para comprobar condiciones alfabéticas:<br />

0 isalpha(c)<br />

Devuelve verda<strong>de</strong>ro (distinto <strong>de</strong> cero) si c es una letra mayúscula o minúscula. Se <strong>de</strong>vuelve un<br />

valor falso si se pasa un carácter distinto <strong>de</strong> letra a esta función.<br />

0 islower(c)<br />

Devuelve verda<strong>de</strong>ro (distinto <strong>de</strong> cero) si c es una letra minúscula. Se <strong>de</strong>vuelve un valor falso (O),<br />

si se pasa un carácter distinto <strong>de</strong> una minúscula.<br />

0 isupper (c)<br />

Devuelve verda<strong>de</strong>ro (distinto <strong>de</strong> cero) si c es una letra mayúscula, falso con cualquier otro<br />

carácter.<br />

Las sigui<strong>en</strong>tes funciones comprueban caracteres numéricos:<br />

o isdigit(c)<br />

Comprueba si c es un dígito <strong>de</strong> O a 9, <strong>de</strong>volvi<strong>en</strong>do verda<strong>de</strong>ro (distinto <strong>de</strong> cero) <strong>en</strong> ese caso, y<br />

falso <strong>en</strong> caso contrario.<br />

0 isxdigit (c)<br />

Devuelve verda<strong>de</strong>ro si c es cualquier dígito hexa<strong>de</strong>cimal (O a 9, A a F, o bi<strong>en</strong> a a f ) y falso <strong>en</strong><br />

cualquier otro caso.<br />

Las sigui<strong>en</strong>tes funciones comprueban argum<strong>en</strong>tos numéricos o alfabéticos:<br />

0 isalnum(c)<br />

Devuelve un valor verda<strong>de</strong>ro, si c es un dígito <strong>de</strong> O a 9 o un carácter alfabético (bi<strong>en</strong> mayúscula<br />

o minúscula) y falso <strong>en</strong> cualquier otro caso.


236 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

~~<br />

Ejemplo 7.4<br />

Leer un carácter <strong>de</strong>l teclado y comprobar si es una letra.<br />

/*<br />

Solicita iniciales y comprueba que es alfabética<br />

*/<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main0<br />

t<br />

char inicial;<br />

printf("¿Cuál es su primer carácter inicial?: ");<br />

scanf ("%c",&inicial);<br />

while (!isalpha(inicial))<br />

t<br />

puts ("Carácter no alfabético 'I) ;<br />

I<br />

printf ("¿Cuál es su sigui<strong>en</strong>te inicial?: ") ;<br />

scanf ("%c", &inicial);<br />

1<br />

puts (";Terminado!") ;<br />

return O;<br />

7.9.2. Funciones <strong>de</strong> prueba <strong>de</strong> caracteres especiales<br />

Algunas funciones incorporadas a la biblioteca <strong>de</strong> funciones comprueban caracteres especiales,<br />

principalm<strong>en</strong>te a efectos <strong>de</strong> legibilidad. Estas funciones son las sigui<strong>en</strong>tes:<br />

iscntrl(c)<br />

Devuelve verda<strong>de</strong>ro si c es un carácter <strong>de</strong> control (códigos ASCII O a 31) y falso <strong>en</strong> caso<br />

contrario.<br />

isgraph(c)<br />

Devuelve verda<strong>de</strong>ro si c es un carácter imprimible (no <strong>de</strong> control) excepto espacio; <strong>en</strong> caso<br />

contrario, se <strong>de</strong>vuelve falso.<br />

isprint(c)<br />

Devuelve verda<strong>de</strong>ro si c es un carácter imprimible (código ASCII 32 a 127) incluy<strong>en</strong>do un<br />

espacio; <strong>en</strong> caso contrario, se <strong>de</strong>vuelve falso.<br />

0 ispunct(c)<br />

Devuelve verda<strong>de</strong>ro si c es cualquier carácter <strong>de</strong> puntuación (un carácter imprimible distinto <strong>de</strong><br />

espacio, letra o dígito); falso, <strong>en</strong> caso contrario.<br />

isspace(c)<br />

Devuelve verda<strong>de</strong>ro si c es carácter un espacio, nueva línea (\n), retorno <strong>de</strong> carro (\r),<br />

tabulación ( \ t) o tabulación vertical ( \ v).<br />

7.9.3. Funciones <strong>de</strong> conversión <strong>de</strong> caracteres<br />

Exist<strong>en</strong> funciones que sirv<strong>en</strong> para cambiar caracteres mayúsculas a minúsculas o viceversa.<br />

tolower (c)<br />

Convierte el carácter c a minúscula, si ya no lo es.<br />

toupper(c)<br />

Convierte el carácter c a mayúscula, si ya no lo es.


Funciones 237<br />

Ejemplo 7.5<br />

El programa MAYMINl . c comprueba si la <strong>en</strong>trudu es una v o una H.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main()<br />

i<br />

char resp; /* respuesta <strong>de</strong>l usuario */<br />

char c;<br />

1<br />

printf ("¿Es un varón o una hembra (V/H)?: ") ;<br />

scanf ("%c",&resp);<br />

resp=toupper(resp);<br />

switch (resp)<br />

i<br />

case 'VI:<br />

puts ("Es un <strong>en</strong>fermero") ;<br />

break;<br />

case 'HI:<br />

puts ("Es una maestra") ;<br />

break;<br />

<strong>de</strong>fault:<br />

puts("No es ni <strong>en</strong>fermero ni maestra") ;<br />

break;<br />

1<br />

return O;<br />

7.10. FUNCIONES NUMÉRICAS<br />

Virtualm<strong>en</strong>te cualquier operación aritmética es posible <strong>en</strong> un programa C. Las funciones matemáticas<br />

disponibles son las sigui<strong>en</strong>tes:<br />

O matemáticas;<br />

O trigonométricas;<br />

O logm'tmicas;<br />

O expon<strong>en</strong>ciales;<br />

O aleatorias.<br />

La mayoría <strong>de</strong> las funciones numéricas están <strong>en</strong> el archivo <strong>de</strong> cabecera MATH. H; las funciones abs<br />

y labs están <strong>de</strong>finidas <strong>en</strong> MATH. H y STDLIB. H, y las rutinas div y ldiv <strong>en</strong> STDLIB. H.<br />

7.10.1. Funciones matemáticas<br />

Las funciones matemáticas usuales <strong>en</strong> la biblioteca estándar son:<br />

O ceil(x)<br />

Redon<strong>de</strong>a al <strong>en</strong>tero más cercano.<br />

O fabs(x)<br />

Devuelve el valor absoluto <strong>de</strong> x (un valor positivo).<br />

O floor(x)<br />

Redon<strong>de</strong>a por <strong>de</strong>fecto al <strong>en</strong>tero más próximo.


238 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

0 fmod(x, y)<br />

Calcula el restof <strong>en</strong> coma flotante para la división x/v, <strong>de</strong> modo que x = i*y+f, don<strong>de</strong> i es un<br />

<strong>en</strong>tero,fti<strong>en</strong>e el mismo signo que x y el valor absoluto <strong>de</strong> f es m<strong>en</strong>or que el valor absoluto <strong>de</strong> y.<br />

0 POW(X, Y)<br />

Calcula x elevado a la pot<strong>en</strong>cia y (x’). Si x es m<strong>en</strong>or que o igual a cero, y <strong>de</strong>be ser un <strong>en</strong>tero. Si x<br />

es igual a cero, y no pue<strong>de</strong> ser negativo.<br />

0 powlO(x)<br />

Calcula 10 elevado a la pot<strong>en</strong>cia x (IO); x <strong>de</strong>be ser <strong>de</strong> tipo <strong>en</strong>tero.<br />

0 sqrt(x)<br />

Devuelve la raíz cuadrada <strong>de</strong> x; x <strong>de</strong>be ser mayor o igual a cero.<br />

7.10.2. Funciones trigonométricas<br />

La biblioteca <strong>de</strong> C incluye una serie <strong>de</strong> funciones que sirv<strong>en</strong> para realizar cálculos trigonométricos. Es<br />

necesario incluir <strong>en</strong> su programa el archivo <strong>de</strong> cabecera MATH. II para utilizar cualquier función.<br />

0 acos(x)<br />

Calcula el arco cos<strong>en</strong>o <strong>de</strong>l argum<strong>en</strong>to x. El argum<strong>en</strong>to x <strong>de</strong>be estar <strong>en</strong>tre -1 y 1.<br />

0 asin(x)<br />

Calcula el arco s<strong>en</strong>o <strong>de</strong>l argum<strong>en</strong>to x. El argum<strong>en</strong>to x <strong>de</strong>be estar <strong>en</strong>tre -1 y 1.<br />

0 atan(x)<br />

Calcula el arco tang<strong>en</strong>te <strong>de</strong>l argum<strong>en</strong>to x.<br />

o atan2(x,y)<br />

Calcula el arco tang<strong>en</strong>te <strong>de</strong> x dividido por y.<br />

0 cos(x)<br />

Calcula el cos<strong>en</strong>o <strong>de</strong>l ángulo x ; x se expresa <strong>en</strong> radianes.<br />

0 sin(x)<br />

Calcula el s<strong>en</strong>o <strong>de</strong>l ángulo x; x se expresa <strong>en</strong> radianes.<br />

o tan(x)<br />

Devuelve la tang<strong>en</strong>te <strong>de</strong>l ángulo x ; x se expresa <strong>en</strong> radianes.<br />

Regla<br />

Si necesita pasar un ángulo expresado <strong>en</strong> grados a radianes, para po<strong>de</strong>r utilizarlo con las funciones<br />

trigonom&ieas, multiplique los grados por pi/180, don<strong>de</strong> pi = 3.14159.<br />

7.10.3. Funciones logarítmicas y expon<strong>en</strong>ciales<br />

/<br />

Las funciones logarítmicas y expon<strong>en</strong>ciales suel<strong>en</strong> ser utilizadas con frecu<strong>en</strong>cia no sólo <strong>en</strong> matemáticas,<br />

sino también <strong>en</strong> el mundo <strong>de</strong> la empresa y los negocios. Estas funciones requier<strong>en</strong> también el archivo<br />

<strong>de</strong> inclusión MATH. H.<br />

exp(x1, expl(x)<br />

Calcula el expon<strong>en</strong>cial e , don<strong>de</strong> e es la base <strong>de</strong> logaritmos naturales <strong>de</strong> valor 2.7 18282.<br />

valor = exp (5. O) ;<br />

Una variante <strong>de</strong> esta función es expl, que calcula e utilizando un valor long double (largo<br />

doble).


7<br />

Funciones 239<br />

log(x), logl(x)<br />

La función log calcula el logaritmo natural <strong>de</strong>l argum<strong>en</strong>to x y log1 (x) calcula el citado<br />

logaritmo natural <strong>de</strong>l argum<strong>en</strong>to x <strong>de</strong> valor lonq double (largo doble).<br />

0 loglO(x), loglOl(x)<br />

Calcula el logaritmo <strong>de</strong>cimal <strong>de</strong>l argum<strong>en</strong>to x, <strong>de</strong> valor real double <strong>en</strong> 1 ogl 0 ( XI y <strong>de</strong> valor real<br />

long double <strong>en</strong> log] O1 (x); x ha <strong>de</strong> ser positivo.<br />

7.10.4. Funciones aleatorias<br />

Los números aleatorios son <strong>de</strong> gran utilidad <strong>en</strong> numerosas aplicaciones y requier<strong>en</strong> un trato especial <strong>en</strong><br />

cualquier l<strong>en</strong>guaje <strong>de</strong> programación. C no es una excepción y la mayoría <strong>de</strong> los compiladores incorporan<br />

funciones que g<strong>en</strong>eran números aleatorios. Las funciones usuales <strong>de</strong> la biblioteca estándar <strong>de</strong> C son:<br />

rand, random, randomize y srand. Estas funciones se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el archivo STULTH. ti.<br />

0 rand(void)<br />

La función rand g<strong>en</strong>era un número aleatorio. El número calculado por rand varía <strong>en</strong> el rango<br />

<strong>en</strong>tero <strong>de</strong> O a RAND-MAX. La constante RAND-MAX se <strong>de</strong>fine <strong>en</strong> el archivo STDL,IB. 11 <strong>en</strong> forma<br />

hexa<strong>de</strong>cimal (por ejemplo, 7FFF). En consecu<strong>en</strong>cia, asegúrese incluir dicho archivo <strong>en</strong> la parte superior<br />

<strong>de</strong> su programa.<br />

Cada vez que se llama a rand ( ) <strong>en</strong> el mismo programa, se obti<strong>en</strong>e un número <strong>en</strong>tero difer<strong>en</strong>te. Sin<br />

embargo, si el programa se ejecuta una y otra vez, se <strong>de</strong>vuelv<strong>en</strong> el mismo conjunto <strong>de</strong> números<br />

aleatorios. Un método para obt<strong>en</strong>er un conjunto difer<strong>en</strong>te <strong>de</strong> números aleatorios es llamar a la función<br />

srand( ) o a la macro randomize.<br />

La llamada a la función rand( ) se pue<strong>de</strong> asignar a una variable o situar <strong>en</strong> la función <strong>de</strong> salida<br />

printf ( ).<br />

test = rand() ;<br />

printf ("Este es un número dl edtor io %d\n", rand() ) ;<br />

0 randomize(void)<br />

La macro random i ze inicializa el g<strong>en</strong>erador <strong>de</strong> números aleatorios con una semilla aleatoria<br />

obt<strong>en</strong>ida a partir <strong>de</strong> una llamada a la función time. Dado que esta macro llama a la función time, el<br />

archivo <strong>de</strong> cabecera TIME. H se incluirá <strong>en</strong> el programa. No <strong>de</strong>vuelve ningún valor.<br />

/* progrma para g<strong>en</strong>erar 10 números aledtorioc */<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

fnt main (void)<br />

i<br />

int i;<br />

}<br />

clrscro; /* limpia la pantalld */<br />

randomize ( ) ;<br />

for (i=l; i


240 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Si el valor <strong>de</strong> semi 11 a es 1, se reinicializa el g<strong>en</strong>erador <strong>de</strong> números aleatorios. Cuando se llama<br />

a la función rand antes <strong>de</strong> hacer una llamada a la función srand, se g<strong>en</strong>era la misma secu<strong>en</strong>cia<br />

que si se hubiese llamado a la función srand con el argum<strong>en</strong>to semi 1 la tomando el valor 1.<br />

random (num)<br />

La macro random g<strong>en</strong>era un número aleatorio d<strong>en</strong>tro <strong>de</strong> un rango especificado (O y el límite<br />

superior especificado por el argum<strong>en</strong>to num). Devuelve un número <strong>en</strong>tero <strong>en</strong>tre O y num-1.<br />

/*<br />

programa para g<strong>en</strong>erar <strong>en</strong>contrar el mayor <strong>de</strong> 10 números aleatorios<br />

<strong>en</strong>tre O y 1000<br />

"/<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine TOPE 1000<br />

#<strong>de</strong>fine MAX(x,y) ( (x)>(y)?(x): (y )<br />

int main (void)<br />

i<br />

int mx,i;<br />

1<br />

clrscr ( ) ;<br />

randomize ( ) ;<br />

mx = random(T0PE) ;<br />

for (i=2; i


7<br />

Funciones 241<br />

Las funciones time, clock, -strdate y -strtime <strong>de</strong>vuelv<strong>en</strong> la hora actual como el número <strong>de</strong><br />

segundos transcurridos <strong>de</strong>s<strong>de</strong> la medianoche <strong>de</strong>l 1 <strong>de</strong> <strong>en</strong>ero <strong>de</strong> 1970 (hora universal, GMT), el tiempo<br />

<strong>de</strong> CPU empleado por el proceso invocante, la fecha y hora actual, respectivam<strong>en</strong>te.<br />

La <strong>estructura</strong> <strong>de</strong> tiempo utilizada incluye los miembros sigui<strong>en</strong>tes:<br />

struct tm<br />

I<br />

int tm-sec;<br />

int tm-min;<br />

int tm-hour;<br />

int tm-mday;<br />

int tmmon;<br />

int tmjear;<br />

int tm-wday;<br />

int tmjday;<br />

int tm-isdt;<br />

I;<br />

/* segundos */<br />

/* minutos */<br />

/* horas */<br />

/* día <strong>de</strong>l mes 1 a 31 */<br />

/* mes, 0 para Ene, 1 para Feb, ... */<br />

/* año <strong>de</strong>s<strong>de</strong> 1900 */<br />

/* días <strong>de</strong> la semana <strong>de</strong>s<strong>de</strong> domingo (0-6) */<br />

/* día <strong>de</strong>l año <strong>de</strong>s<strong>de</strong> el 1 <strong>de</strong> Ene(0-365) */<br />

/* siempre O para gmtime */<br />

0 clock (void)<br />

La función clock <strong>de</strong>termina el tiempo <strong>de</strong> procesador, <strong>en</strong> unida<strong>de</strong>s <strong>de</strong> click, transcurrido <strong>de</strong>s<strong>de</strong> el<br />

principio <strong>de</strong> la ejecución <strong>de</strong>l programa. Si no se pue<strong>de</strong> <strong>de</strong>volver el tiempo <strong>de</strong> procesador se<br />

<strong>de</strong>vuelve -I.<br />

inicio = clock();<br />

fin = clock();<br />

0 time (hora )<br />

La función time obti<strong>en</strong>e la hora actual; <strong>de</strong>vuelve el número <strong>de</strong> segundos transcurridos <strong>de</strong>s<strong>de</strong> la<br />

medianoche (0O:OO:OO) <strong>de</strong>l 1 <strong>de</strong> <strong>en</strong>ero <strong>de</strong> 1970. Este valor <strong>de</strong> tiempo se almac<strong>en</strong>a <strong>en</strong>tonces <strong>en</strong> la<br />

posición apuntada por el argum<strong>en</strong>to hora. Si hora es un puntero nulo, el valor no se almac<strong>en</strong>a. El<br />

prototipo <strong>de</strong> la función es:<br />

time-t time (time-t *hora) ;<br />

El tipo t ime-h está <strong>de</strong>finido como tipo long <strong>en</strong> t ime . h.<br />

o localtime(hora)<br />

Convierte la fecha y hora <strong>en</strong> una <strong>estructura</strong> <strong>de</strong> tipo tm . Su prototipo es<br />

struct tm *localtime(const time-t "tptr);<br />

0 mktime(t)<br />

Convierte la fecha <strong>en</strong> formato <strong>de</strong> cal<strong>en</strong>dario. Toma la información <strong>de</strong>l argum<strong>en</strong>to y <strong>de</strong>termina<br />

los valores <strong>de</strong>l día <strong>de</strong> la semana (tm-wday ) y <strong>de</strong>l día respecto al inicio <strong>de</strong>l año, también conocido<br />

como fecha juliana (tmjday). Su prototipo es<br />

time-t mktime(struct tm *tptr)<br />

La función <strong>de</strong>vuelve -1 <strong>en</strong> caso <strong>de</strong> producirse un error.<br />

En este ejercicio se pi<strong>de</strong> el año, mes y día; escribe el día <strong>de</strong> la semana y los días pasados<br />

<strong>de</strong>s<strong>de</strong> el 1 <strong>de</strong> <strong>en</strong>ero <strong>de</strong>l año leído. Es utilizado un array <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres, su estudio se hace<br />

<strong>en</strong> capítulos posteriores.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

char *dias [I = { 'I 'I, "Lunes", 'I Mart es 'I , "Mi e L' c o 1 es I' ,<br />

"Jueves", "Viernes", "Cabado", "Domingo"} ;<br />

int main (void)<br />

1<br />

Y


242 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

struct tm fecha;<br />

int anyo, mes, dia;<br />

/* Entrada: año, mes y dia */<br />

printf ("Año: ") ;<br />

scanf ("%d",&anyo);<br />

print f ( "Mes: I' ) ;<br />

scanf ("%d", &mes);<br />

print f ("Dia: 'I 1;<br />

scanf ("%d",&dia);<br />

/* Asigna fecha a la <strong>estructura</strong> fecha, <strong>en</strong> formato establecido */<br />

fecha.tm_year = anyo - 1900;<br />

fecha.tm-mon = mes - 1;<br />

fecha.tm-mday = dia;<br />

fecha.tm-hour = O;<br />

fecha.tm-min = O;<br />

fecha.tm-sec = 1;<br />

fecha.tm-isdst = -1;<br />

/* mktime <strong>en</strong>cu<strong>en</strong>tra el día <strong>de</strong> la semana y el día <strong>de</strong>l año.<br />

Devuelve -1 si error.<br />

*/<br />

if (mktime(&fecha) == -1)<br />

i<br />

puts (" Error <strong>en</strong> la fecha. ") ;<br />

exit (-1);<br />

1<br />

/* El domingo, la función le consi<strong>de</strong>ra dia O */<br />

if (fecha.tmwday == O)<br />

fecha.tm-wday = 7;<br />

printf('\nDia <strong>de</strong> la semana: %d; dia <strong>de</strong>l año: %d",<br />

fecha.tm-wday, fecha.Lm_yday+l) ;<br />

/* Escribe el día <strong>de</strong> la semana */<br />

printf("\nEs el dia <strong>de</strong> la semana, &s\n", dias[€echa.tmwday]);<br />

return O;<br />

I<br />

Ejercicio 7.4<br />

Una aplicación <strong>de</strong> clock ( ) para <strong>de</strong>terminar el tiempo <strong>de</strong> proceso <strong>de</strong> un programa que calcula el<br />

factorial <strong>de</strong> un número.<br />

El factorial <strong>de</strong> n! = n*(n-I)*(n-2) ... 2*1. La variable que vaya a calcular el factorhi, se <strong>de</strong>fine <strong>de</strong><br />

tipo long para po<strong>de</strong>r cont<strong>en</strong>er un valor elevado. El número, arbitrariam<strong>en</strong>te, va a estar compr<strong>en</strong>dido<br />

<strong>en</strong>tre 3 y 15. El tiempo <strong>de</strong> proceso va a incluir el tiempo <strong>de</strong> <strong>en</strong>trada <strong>de</strong> <strong>datos</strong>. La función clock ( )<br />

<strong>de</strong>vuelve el tiempo <strong>en</strong> unida<strong>de</strong>s <strong>de</strong> click, cada CLK-TCK es un segundo. El programa escribe el tiempo<br />

<strong>en</strong> ambas unida<strong>de</strong>s.<br />

/*<br />

*/<br />

En este ejercicio se <strong>de</strong>termind el tiempo <strong>de</strong>l procesddor para<br />

calcular el factorial <strong>de</strong> un número requerido, <strong>en</strong>tre 3 y 15.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong>


Funciones 243<br />

int main (void)<br />

i<br />

float inicio, fin;<br />

int n, x;<br />

long int fact;<br />

1<br />

inicio = clock() ;<br />

do i<br />

printf (" Factorial <strong>de</strong> (3


244 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

ldiv (nun, d<strong>en</strong>om)<br />

Calcula el coci<strong>en</strong>te y resto <strong>de</strong> num dividido por d<strong>en</strong>om, y almac<strong>en</strong>a los resultados <strong>de</strong> quot y rem,<br />

miembros long <strong>de</strong> la <strong>estructura</strong> ldiv-t.<br />

type<strong>de</strong>f struct<br />

i<br />

long int quot; /* coci<strong>en</strong>te */<br />

long int rem; /* resto */<br />

} ldiv-t;<br />

resultado = ldiv(1600L, 40L);<br />

7.13. VISIBILIDAD DE UNA FUNCIÓN<br />

El ámbito <strong>de</strong> un elem<strong>en</strong>to es su visibilidad <strong>de</strong>s<strong>de</strong> otras partes <strong>de</strong>l programa y la duración <strong>de</strong> un elem<strong>en</strong>to<br />

es su tiempo <strong>de</strong> vida, lo que implica no sólo cuánto tiempo existe la variable, sino cuando se crea y<br />

cuando se hace disponible. El ámbito <strong>de</strong> un elem<strong>en</strong>to <strong>en</strong> C <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong> don<strong>de</strong> se sitúe la <strong>de</strong>finición y <strong>de</strong><br />

los modificadores que le acompañan. En resum<strong>en</strong>, se pue<strong>de</strong> <strong>de</strong>cir que un elem<strong>en</strong>to <strong>de</strong>finido d<strong>en</strong>tro <strong>de</strong><br />

una función ti<strong>en</strong>e ámbito local (alcance local), o si se <strong>de</strong>fine fuera <strong>de</strong> cualquier función, se dice que<br />

ti<strong>en</strong>e un ámbito global. La Figura 7.6 resume el modo <strong>en</strong> que se ve afectado el ámbito por la posición<br />

<strong>en</strong> el archivo fu<strong>en</strong>te.<br />

Exist<strong>en</strong> dos tipos <strong>de</strong> clases <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> C: auto y static. Una variable auto es aquella<br />

que ti<strong>en</strong>e una duración automática. No existe cuando el programa comi<strong>en</strong>za la ejecución, se crea <strong>en</strong><br />

algún punto durante la ejecución y <strong>de</strong>saparece <strong>en</strong> algún punto antes <strong>de</strong> que el programa termine la<br />

ejecución. Una variable static es aquella que ti<strong>en</strong>e una duración$ja. El espacio para el elem<strong>en</strong>to <strong>de</strong><br />

programación se establece <strong>en</strong> tiempo <strong>de</strong> compilación; existe <strong>en</strong> tiempo <strong>de</strong> ejecución y se elimina sólo<br />

cuando el programa <strong>de</strong>saparece <strong>de</strong> memoria <strong>en</strong> tiempo <strong>de</strong> ejecución.<br />

Las variables con ámbito global se d<strong>en</strong>ominan variables globales y son las <strong>de</strong>finidas externam<strong>en</strong>te<br />

a la función (<strong>de</strong>claración externa). Las variables globales ti<strong>en</strong><strong>en</strong> el sigui<strong>en</strong>te comportami<strong>en</strong>to y atributos:<br />

prog-<strong>de</strong>mo. c<br />

Las variables globales <strong>de</strong>claradas<br />

<strong>en</strong> este nivel ti<strong>en</strong><strong>en</strong> ámbito global.<br />

Son válidas para todas las funciones<br />

<strong>de</strong> este archivo fu<strong>en</strong>te. Disponible<br />

<strong>en</strong> otros archivos fu<strong>en</strong>te a<br />

m<strong>en</strong>os que se utilice la palabra<br />

reservada static.<br />

Function-a í 1<br />

Las variables <strong>de</strong>claradas <strong>en</strong> este<br />

nivel son locales y ti<strong>en</strong><strong>en</strong> clase <strong>de</strong><br />

almac<strong>en</strong>ami<strong>en</strong>to auto al salir <strong>de</strong> la<br />

función, a m<strong>en</strong>os que se utilice la<br />

palabla reservada static. Visible<br />

sólo a esta función.<br />

Ámbito<br />

Ámbito<br />

global<br />

I<br />

Figura 7.6. Ámbito <strong>de</strong> variable local y global.


Funciones 245<br />

Las variables globales ti<strong>en</strong><strong>en</strong> duración estática por <strong>de</strong>fecto. El almac<strong>en</strong>ami<strong>en</strong>to se realiza <strong>en</strong><br />

tiempo <strong>de</strong> compilación y nunca <strong>de</strong>saparece. Por <strong>de</strong>finición, una variable global no pue<strong>de</strong> ser una<br />

variable auto.<br />

e Las variables globales son visibles globalm<strong>en</strong>te <strong>en</strong> el archivo fu<strong>en</strong>te. Se pued<strong>en</strong> refer<strong>en</strong>ciar por<br />

cualquier función, a continuación <strong>de</strong>l punto <strong>de</strong> <strong>de</strong>finición.<br />

Las variables globules están disponibles, por <strong>de</strong>fecto, a otros archivos&<strong>en</strong>te. Esta operación se<br />

d<strong>en</strong>omina <strong>en</strong>lace externo.<br />

7.13.1. Variables locales fr<strong>en</strong>te a variables globales<br />

A<strong>de</strong>más <strong>de</strong> las variables globales, es preciso consi<strong>de</strong>rar las variables locales. Una variable local está<br />

<strong>de</strong>finida solam<strong>en</strong>te d<strong>en</strong>tro <strong>de</strong>l bloque o cuerpo <strong>de</strong> la función y no ti<strong>en</strong>e significado (vida) fuera <strong>de</strong> la<br />

función respectiva. Por consigui<strong>en</strong>te, si una función <strong>de</strong>fine una variable como local, el ámbito <strong>de</strong> la<br />

variable está protegido. La variable no se pue<strong>de</strong> utilizar, cambiar o borrar <strong>de</strong>s<strong>de</strong> cualquier otra función<br />

sin una programación específica mediante el paso <strong>de</strong> valores (parámetros).<br />

Una variable locd es una variable que se <strong>de</strong>fine d<strong>en</strong>tro <strong>de</strong> una función.<br />

es una variable que pue<strong>de</strong> ser uti<br />

s funciones <strong>de</strong> un<br />

Para construir variables globales <strong>en</strong> C, se <strong>de</strong>b<strong>en</strong> <strong>de</strong>finir fuera <strong>de</strong> la función main ( ) . Para ilustrar el<br />

uso <strong>de</strong> variables locales y globales, examine la <strong>estructura</strong> <strong>de</strong> bloques <strong>de</strong> la Figura 7.7. Aquí la variable<br />

global es xo y la variable local es xi. La función pue<strong>de</strong> realizar operaciones sobre xo y XI. Sin embargo,<br />

main() sólo pue<strong>de</strong> operar con xo, ya que xl no está <strong>de</strong>finida fuera <strong>de</strong>l bloque <strong>de</strong> la función<br />

funcionl( 1. Cualquier int<strong>en</strong>to <strong>de</strong> utilizar XI fuera <strong>de</strong> funcionl( ) producirá un error.<br />

int x0 ; /* v


246 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Exainine ahora la Figura 7.8. Esta vez exist<strong>en</strong> dos funciones, ambas <strong>de</strong>fin<strong>en</strong> XI como variable local.<br />

Nuevam<strong>en</strong>te xo es una variable global. La variable XI sólo se pue<strong>de</strong> utilizar d<strong>en</strong>tro <strong>de</strong> las dos funciones.<br />

Sin embargo, cualquier operación sobre XI d<strong>en</strong>tro <strong>de</strong> funcioni ( ) no afecta al valor <strong>de</strong> xl <strong>en</strong><br />

funcion2( y viceversa. En otras palabras, la variable xl <strong>de</strong> funcioni ( ) se consi<strong>de</strong>ra una variable<br />

in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te <strong>de</strong> xl <strong>en</strong> funciona ( ).<br />

Al contrario que las variables, las .funciones son externas por <strong>de</strong>fecto. Es preciso consi<strong>de</strong>rar la<br />

difer<strong>en</strong>cia <strong>en</strong>tre <strong>de</strong>finición <strong>de</strong> una función y <strong>de</strong>claracih. Si una <strong>de</strong>claración <strong>de</strong> variable comi<strong>en</strong>za con<br />

la palabra reservada extern, no se consi<strong>de</strong>ra <strong>de</strong>finición <strong>de</strong> variable. Sin esta palabra reservada es una<br />

<strong>de</strong>finición. Cada <strong>de</strong>finición <strong>de</strong> variable es al mismo tiempo una <strong>de</strong>claración <strong>de</strong> variable. Se pue<strong>de</strong> utilizar<br />

una variable sólo <strong>de</strong>spués <strong>de</strong> que ha sido <strong>de</strong>clarada (<strong>en</strong> el mismo archivo). Únicam<strong>en</strong>te las <strong>de</strong>finiciones<br />

<strong>de</strong> variables asignan memoria y pued<strong>en</strong>, por consigui<strong>en</strong>te, cont<strong>en</strong>er inicializaciones. Una variable sólo<br />

se <strong>de</strong>fine una vez, pero se pue<strong>de</strong> <strong>de</strong>clarur tantas veces como se <strong>de</strong>see. Una <strong>de</strong>claración <strong>de</strong> variable al<br />

nivel global (externa a las funciones) es válida <strong>de</strong>s<strong>de</strong> esa <strong>de</strong>claración hasta el final <strong>de</strong>l archivo; una<br />

<strong>de</strong>claración <strong>en</strong> el interior <strong>de</strong> una función es válida sólo <strong>en</strong> esa función. En este punto, considérese que<br />

las <strong>de</strong>finiciones y <strong>de</strong>claraciones <strong>de</strong> variables globales son similares a las funciones; la difer<strong>en</strong>cia<br />

principal es que se pue<strong>de</strong> escribir la palabra reservada extern <strong>en</strong> <strong>de</strong>claraciones <strong>de</strong> función.<br />

I<br />

int x0 ;<br />

float. f uncion 1 ( ) ; /* prototipo fiiricioril *il<br />

float. tuncionl() ; /* prototipo funciun2 */<br />

int main0<br />

i<br />

...<br />

float funcionl ( )<br />

int xl ;<br />

...<br />

-<br />

float funciorii ( 1<br />

i<br />

irit xl ; / * vCj r i


7”<br />

Funciones 247<br />

/* archivo con la funcion main(): pr0grama.c */<br />

int total ;<br />

extern int suma;<br />

extern void f (void) ;<br />

void main (void)<br />

/*<br />

archivo con la <strong>de</strong>finición <strong>de</strong> funciones y variable: modu1o.c<br />

*/<br />

int suma;<br />

void f(void)<br />

...<br />

Utilizando la palabra reservada extern se pue<strong>de</strong> acce<strong>de</strong>r a símbolos externos <strong>de</strong>finidos <strong>en</strong> otros<br />

módulos. suma y la función f ( 1 se <strong>de</strong>claran externas.<br />

Las funciones son externas por <strong>de</strong>fecto, ai contrario que las variables.<br />

7.13.2. Variables estáticas y automáticas<br />

Los valores asignados a las variables locales <strong>de</strong> una función se <strong>de</strong>struy<strong>en</strong> cuando se termina la ejecución<br />

<strong>de</strong> la función y no se pue<strong>de</strong> recuperar su valor para ejecuciones posteriores <strong>de</strong> la función. Las variables<br />

locales se d<strong>en</strong>ominan variables automáticas, significando que se pierd<strong>en</strong> cuando termina la función.<br />

Se pue<strong>de</strong> utilizar auto para <strong>de</strong>clarar una variable<br />

auto int v<strong>en</strong>tas;<br />

aunque las variables locales se <strong>de</strong>claran automáticas por <strong>de</strong>fecto y, por consigui<strong>en</strong>te, el uso <strong>de</strong> auto es<br />

opcional y, <strong>de</strong> hecho, no se utiliza.<br />

Las variables estáticas (static), por otra parte, manti<strong>en</strong><strong>en</strong> su valor <strong>de</strong>spués que una función se ha<br />

terminado. Una variable <strong>de</strong> una función, <strong>de</strong>clarada como estática, manti<strong>en</strong>e un valor a través <strong>de</strong><br />

ejecuciones posteriores <strong>de</strong> la misma función. Haci<strong>en</strong>do una variable local estática, su valor se reti<strong>en</strong>e <strong>de</strong><br />

una llamada a la sigui<strong>en</strong>te <strong>de</strong> la función <strong>en</strong> que está <strong>de</strong>finida. Se <strong>de</strong>claran las variables estáticas situando<br />

la palabra reservada static <strong>de</strong>lante <strong>de</strong> la variable. Por ejemplo,<br />

static int v<strong>en</strong>tas = 10000;<br />

static int dias = 500;<br />

Este valor se almac<strong>en</strong>a <strong>en</strong> la variable estática, sólo la primera vez que se ejecuta la función. Si su<br />

valor no está <strong>de</strong>finido, el compilador almac<strong>en</strong>a un cero <strong>en</strong> una variable estática por <strong>de</strong>fecto.<br />

El sigui<strong>en</strong>te programa ilustra el concepto estático <strong>de</strong> una variable:<br />

#inclu<strong>de</strong> <br />

/* prototipo <strong>de</strong> la función */<br />

void Ejemplo-estatica(int);<br />

void main()<br />

{<br />

}<br />

Ejemplo-estatica(1) ;<br />

E j emplo-es t at ica ( 2 ) ;<br />

Ejemplo-estatica(3);<br />

/* Ejemplo <strong>de</strong>l uso <strong>de</strong> una variable estática */


248 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

void Ejemplo-estatica(int Llamada)<br />

i<br />

static int Cu<strong>en</strong>ta;<br />

if (Llamada =: 1)<br />

Cu<strong>en</strong>ta = 1;<br />

printf("\n El valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamada n? %d es: %d",<br />

Llamada,Cu<strong>en</strong>ta);<br />

++Cu<strong>en</strong>ta ;<br />

1<br />

Al ejecutar el programa se visualiza:<br />

El valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamada ne 1 es: 1<br />

El valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamada ne 2 es: 2<br />

El valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamada ne 3 es: 3<br />

Si quita la palabra reservada static <strong>de</strong> la <strong>de</strong>claración <strong>de</strong> Cu<strong>en</strong>ta, el resultado será:<br />

El valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamada nG 1 es: 1<br />

El valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamada nQ 2 es: 1046<br />

no se pue<strong>de</strong> pre<strong>de</strong>cir cuál es el valor <strong>de</strong> Cu<strong>en</strong>ta <strong>en</strong> llamadas posteriores a la primera.<br />

Las variables globales se pued<strong>en</strong> ocultar <strong>de</strong> otros urchivos ,fu<strong>en</strong>te utilizando el especijicador <strong>de</strong><br />

almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> clase stat i c.<br />

Para hacer una variable global privada al archivo fu<strong>en</strong>te (y, por consigui<strong>en</strong>te, no Útil a otros módulos<br />

<strong>de</strong> código) se le hace prece<strong>de</strong>r por la palabra s tat i c. Por ejemplo, las sigui<strong>en</strong>tes variables se <strong>de</strong>claran<br />

fuera <strong>de</strong> las funciones <strong>de</strong> un archivo fu<strong>en</strong>te:<br />

static int m = 25;<br />

static char linea_texto[80] ;<br />

static int indice-linea;<br />

static char bufer[MAXLOGBUFl;<br />

static char *pBuffer;<br />

Las variables anteriores son privadas al archivo fu<strong>en</strong>te. Observe este ejemplo:<br />

#<strong>de</strong>fine OFF O<br />

#<strong>de</strong>fine ON 1<br />

...<br />

static unsigned char maestro = OFF;<br />

...<br />

main ( )<br />

...<br />

1<br />

funcion-a ( )<br />

I<br />

...<br />

1<br />

maestro se pue<strong>de</strong> utilizar tanto <strong>en</strong> funcion-a ( ) como <strong>en</strong> main ( ) , <strong>en</strong> este archivo fu<strong>en</strong>te, pero no se<br />

pue<strong>de</strong> <strong>de</strong>clarar como extern a otro archivo fu<strong>en</strong>te.<br />

Se pue<strong>de</strong> hacer también una <strong>de</strong>claración <strong>de</strong> función static. Por <strong>de</strong>fecto, todas las funciones ti<strong>en</strong><strong>en</strong><br />

<strong>en</strong>lace externo y son visibles a otros módulos <strong>de</strong> programa. Cuando se sitúa la palabra reservada static<br />

<strong>de</strong>lante <strong>de</strong> la <strong>de</strong>claración <strong>de</strong> la función, el compilador hace privada la función al archivb fu<strong>en</strong>te. Se<br />

pue<strong>de</strong>, <strong>en</strong>tonces, reutilizar el nombre <strong>de</strong> la función <strong>en</strong> otros módulos fu<strong>en</strong>te <strong>de</strong>l programa.<br />

1<br />

i<br />

!


1<br />

Funciones 249<br />

I<br />

1<br />

1<br />

i<br />

I<br />

7.14. COMPILACIÓN SEPARADA<br />

Hasta este mom<strong>en</strong>to, casi todos los ejemplos que se han expuesto <strong>en</strong> el capítulo se <strong>en</strong>contraban <strong>en</strong> un<br />

sólo archivo fu<strong>en</strong>te. Los programas gran<strong>de</strong>s son más fáciles <strong>de</strong> gestionar si se divid<strong>en</strong> <strong>en</strong> varios archivos<br />

fu<strong>en</strong>te, también llamados módulos, cada uno <strong>de</strong> los cuales pue<strong>de</strong> cont<strong>en</strong>er una o más funciones. Estos<br />

módulos se compilan y <strong>en</strong>lazan por separado posteriorm<strong>en</strong>te con un <strong>en</strong>laador, o bi<strong>en</strong> con la herrami<strong>en</strong>ta<br />

correspondi<strong>en</strong>te <strong>de</strong>l <strong>en</strong>torno <strong>de</strong> programación. Cuando se divi<strong>de</strong> un programa gran<strong>de</strong> <strong>en</strong> pequeños, los<br />

únicos archivos que se recompilan son los que se han modificado. El tiempo <strong>de</strong> compilación se reduce,<br />

dado que pequeños archivos fu<strong>en</strong>te se compilan más rápido que los gran<strong>de</strong>s. Los archivos gran<strong>de</strong>s son<br />

difíciles <strong>de</strong> mant<strong>en</strong>er y editar, ya que su impresión es un proceso l<strong>en</strong>to que utilizará cantida<strong>de</strong>s excesivas<br />

<strong>de</strong> papel.<br />

La Figura 7.9 muestra cómo el <strong>en</strong>lazador pue<strong>de</strong> construir un programa ejecutable, utilizando<br />

módulos objetos, cada uno <strong>de</strong> los cuales se obti<strong>en</strong>e compilando un módulo fu<strong>en</strong>te.<br />

Compilador<br />

...<br />

+<br />

Enlazador<br />

Programa ejecutable<br />

Figura 7.9. Compilación separada.<br />

1<br />

i<br />

Cuando se ti<strong>en</strong>e más <strong>de</strong> un archivo fu<strong>en</strong>te, se pue<strong>de</strong> refer<strong>en</strong>ciar una función <strong>en</strong> un archivo fu<strong>en</strong>te<br />

<strong>de</strong>s<strong>de</strong> una función <strong>de</strong> otro archivo fu<strong>en</strong>te. Al contrario que las variables, las funciones son externas por<br />

<strong>de</strong>fecto. Si <strong>de</strong>sea, por razones <strong>de</strong> legibilidad -no recom<strong>en</strong>dable-, pue<strong>de</strong> utilizar la palabra reservada<br />

extern con un prototipo <strong>de</strong> función y <strong>en</strong> la cabecera.<br />

Se pue<strong>de</strong> <strong>de</strong>sear restringir la visibilidad <strong>de</strong> una función, haciéndola visible sólo a otras funciones <strong>en</strong><br />

un archivo fu<strong>en</strong>te. Una razón para hacer esto es t<strong>en</strong>er la posibilidad <strong>de</strong> t<strong>en</strong>er dos funciones con el mismo<br />

nombre <strong>en</strong> difer<strong>en</strong>tes archivos. Otra razón es reducir el número <strong>de</strong> refer<strong>en</strong>cias externas y aum<strong>en</strong>tar la<br />

velocidad <strong>de</strong>l proceso <strong>de</strong> <strong>en</strong>lace.<br />

Se pue<strong>de</strong> hacer una función no visible al exterior <strong>de</strong> un archivo fu<strong>en</strong>te utilizando la palabra reservada<br />

static con la cabecera <strong>de</strong> la función y la s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>l prototipo <strong>de</strong> función. Se escribe la palabra<br />

static antes <strong>de</strong>l tipo <strong>de</strong> valor <strong>de</strong>vuelto por la función. Tales funciones no serán públicas al <strong>en</strong>lazador,<br />

<strong>de</strong> modo que otros módulos no t<strong>en</strong>drán acceso a ellas. La palabra reservada static, tanto para variables<br />

globales como para funciones, es Útil para evitar conflictos <strong>de</strong> nombres y prev<strong>en</strong>ir el uso accid<strong>en</strong>tal <strong>de</strong><br />

ellos. Por ejemplo, imaginemos un programa muy gran<strong>de</strong> que consta <strong>de</strong> muchos módulos, <strong>en</strong> el que se<br />

busca un error producido poi una variable global; si la variable es estática, se pue<strong>de</strong> restringir su<br />

búsqueda al módulo <strong>en</strong> que está <strong>de</strong>finida; si no es así, se exti<strong>en</strong><strong>de</strong> nuestra investigación a los restantes<br />

módulos <strong>en</strong> que está <strong>de</strong>clarada (con la palabra reservada extern).<br />

4


250 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Como regla g<strong>en</strong>eral, son preferibles las variables locales a las globales. Si realm<strong>en</strong>te es necesario<br />

o <strong>de</strong>seable y e alguna variable sea global, es preferible hacerla estática, lo que significa que será<br />

<strong>en</strong> que está <strong>de</strong>finida.<br />

Ejemplo 7.6<br />

Supongamos dos módulos: MODULO1 y MODULOL. En el primero .se escribe la función main(), hace<br />

refer<strong>en</strong>cia a funciones y variables globales <strong>de</strong>finidas <strong>en</strong> el segundo módulo.<br />

/* MODULO1.C */<br />

#inclu<strong>de</strong> <br />

void main( )<br />

I<br />

void f(int i), g(void);<br />

extern int n; /* Declaración <strong>de</strong> n (no <strong>de</strong>finición) */<br />

f ( 8);<br />

nt+;<br />

90;<br />

puts ("Fin <strong>de</strong> programa. 'I;<br />

1<br />

/* MODUL02.C */<br />

#inclu<strong>de</strong> <br />

int n = 100;<br />

static int m = 7;<br />

void f (int i)<br />

I<br />

n += (i+m);<br />

1<br />

void g(void)<br />

/* Definición <strong>de</strong> n (también <strong>de</strong>claración) */<br />

I<br />

printf ("n = %d\n",n);<br />

1<br />

f y g se <strong>de</strong>fin<strong>en</strong> <strong>en</strong> el módulo 2 y se <strong>de</strong>claran <strong>en</strong> el módulo 1. Si se ejecuta el programa, se produce la<br />

salida<br />

n = 116<br />

Fin <strong>de</strong> programa.<br />

Se pue<strong>de</strong> hacer una función invisible fuera <strong>de</strong> un archivo fu<strong>en</strong>te utilizando la palabra reservada<br />

static con la cabecera y el prototipo <strong>de</strong> la función.<br />

7.15. VARIABLES REGISTRO (register)<br />

Una variable registro (register) es similar a una variable local, pero <strong>en</strong> lugar <strong>de</strong> ser almac<strong>en</strong>ada <strong>en</strong><br />

la pila, se almac<strong>en</strong>a directam<strong>en</strong>te <strong>en</strong> un registro <strong>de</strong>l procesador (tal como ay o bx). Dado que el número


Funciones 251<br />

<strong>de</strong> registros es limitado y a<strong>de</strong>tnás están limitados <strong>en</strong> tamaño, el número <strong>de</strong> variables registro que un<br />

programa pue<strong>de</strong> crear simultáneam<strong>en</strong>te es muy restringido.<br />

Para <strong>de</strong>clarar una variable registro, se hace prece<strong>de</strong>r a la misma con la palabra reservada<br />

register ;<br />

register int k;<br />

La v<strong>en</strong>taja <strong>de</strong> las variables registro es su mayor rapi<strong>de</strong>z <strong>de</strong> manipulación. Esto se <strong>de</strong>be a que las<br />

operaciones sobre valores situados <strong>en</strong> los registros son normalm<strong>en</strong>te más rápidas que cuando se realizan<br />

sobre valores almac<strong>en</strong>ados <strong>en</strong> memoria. Su uso se suele restringir a segm<strong>en</strong>tos <strong>de</strong> código mucha veces<br />

ejecutados. Las variables registro pued<strong>en</strong> ayudar a optimizar el r<strong>en</strong>dimi<strong>en</strong>to <strong>de</strong> un programa<br />

proporcionando acceso directo <strong>de</strong> la CPU a los valores claves <strong>de</strong>l programa.<br />

Una variable registro <strong>de</strong>be ser local a una función; nunca pue<strong>de</strong> ser global al programa completo. El<br />

uso <strong>de</strong> la palabra reservada register no garantiza que un valor sea almac<strong>en</strong>ado <strong>en</strong> un registro. Esto<br />

sólo suce<strong>de</strong>rá si un registro está disponible (libre). Si no exist<strong>en</strong> registros disponibles, C crea la variable<br />

como si fuera una variable local normal.<br />

Una aplicación usual <strong>de</strong> las variables registro es como variable <strong>de</strong> control <strong>de</strong> bucles for o <strong>en</strong> la<br />

expresión condicional <strong>de</strong> una s<strong>en</strong>t<strong>en</strong>cia while, que se <strong>de</strong>b<strong>en</strong> ejecutar a alta velocidad.<br />

void usoregistro(void)<br />

i<br />

register int k;<br />

puts("\n Contar con una variable registro.');<br />

for (k = 1; k


252 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

factorial (5) = 120<br />

5*4*3*2*1<br />

n -= 4<br />

I<br />

ret,orno ri<br />

t<br />

t<br />

t<br />

f<br />

i’ i,t or no ri<br />

retorno 11<br />

* factorial (4);<br />

* fdctorial (-3);<br />

* iact.oria1 (2);<br />

r-et.orno 11 * factorial (1);<br />

ri == 1<br />

retor~no I;<br />

_____I<br />

Figura 7.10. Llamadas a funciones recursivas para fac torial (5).<br />

c<br />

Ejemplo 7.7<br />

i<br />

Realizar el algoritmo <strong>de</strong> la fclnción factorial.<br />

La implem<strong>en</strong>tación <strong>de</strong> la función recursiva factorial es:<br />

double factorial(int numero)<br />

i<br />

if (numero > 1)<br />

return numero * factorial(numero-1);<br />

return 1;<br />

1<br />

Ejemplo 7.8<br />

Contar valores <strong>de</strong> 1 a 10 <strong>de</strong> modo recursivo.<br />

#inclu<strong>de</strong> <br />

void contar(int cima);<br />

int main0<br />

{<br />

contar(l0);<br />

return O;<br />

1<br />

void contar(int cima)<br />

i<br />

if (cima > 1)<br />

contar(cima-1);<br />

printf (“%d ‘I, cima) ;


Funciones 253<br />

Ejemplo 7.9<br />

Determinar si un número <strong>en</strong>tero positivo es par o impar; con dos funciones que se llaman mutuam<strong>en</strong>te:<br />

recursividad indirecta.<br />

#inclu<strong>de</strong> <br />

int par(int n) ;<br />

int impar(int n);<br />

int main (void)<br />

{<br />

int n;<br />

/* Entrada: <strong>en</strong>tero > O */<br />

do i<br />

printf ("\nEntero > O: ");<br />

scanf ("%d",&n);<br />

} while (n


254 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

7.17. RESUMEN<br />

Las funciones son la base <strong>de</strong> la construcción <strong>de</strong><br />

programas <strong>en</strong> C. Se utilizan funciones para subdividir<br />

problemas gran<strong>de</strong>s <strong>en</strong> tareas más pequeñas. El<br />

<strong>en</strong>capsuiami<strong>en</strong>to <strong>de</strong> las características <strong>en</strong> funciones,<br />

hace los programas más fáciles <strong>de</strong> mant<strong>en</strong>er. El uso <strong>de</strong><br />

funciones ayuda al programador a reducir el tamaño<br />

<strong>de</strong> su programa, ya que se pue<strong>de</strong> llamar repetidam<strong>en</strong>te<br />

y reutilizar el código d<strong>en</strong>tro <strong>de</strong> una función.<br />

En este capftulo habrá apr<strong>en</strong>dido lo sigui<strong>en</strong>te:<br />

0 el concepto, <strong>de</strong>claración, <strong>de</strong>finición y uso <strong>de</strong> una<br />

función;<br />

las funciones que <strong>de</strong>vuelv<strong>en</strong> un resultado lo<br />

hac<strong>en</strong> a través <strong>de</strong> la s<strong>en</strong>t<strong>en</strong>cia return;<br />

los parámetros d es se pasan por valor,<br />

para un paso por a se utilizan punteros;<br />

el modificador e utiliza cuando se<br />

<strong>de</strong>sea que los parámetros <strong>de</strong> la función sean<br />

valores <strong>de</strong> sólo lectura;<br />

el concepto y uso <strong>de</strong> prototipos, cuyo uso es<br />

recom<strong>en</strong>dable <strong>en</strong> C;<br />

la v<strong>en</strong>taja <strong>de</strong> utilizar macros con argum<strong>en</strong>tos,<br />

para aum<strong>en</strong>tar la velocidad <strong>de</strong> ejecución;<br />

el concepto <strong>de</strong> ámbito o alcance y visibilidad,<br />

junto con el <strong>de</strong> variable global y local;<br />

0 clases <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> variables <strong>en</strong><br />

memoria: auto, extern, register y<br />

static.<br />

La biblioteca estándar C <strong>de</strong> funciones <strong>en</strong> tiempo<br />

<strong>de</strong> ejecución incluye gran cantidad <strong>de</strong> funciones. Se<br />

agrupan por categorías, <strong>en</strong>tre las que <strong>de</strong>stacan:<br />

0 manipulación <strong>de</strong> caracteres;<br />

numéricas;<br />

tiempo y hora;<br />

conversión <strong>de</strong> <strong>datos</strong>;<br />

0 bcísqueda y o<strong>de</strong><br />

etc.<br />

T<strong>en</strong>ga cuidado <strong>de</strong> incluir el archivo <strong>de</strong> cabecera<br />

correspondi<strong>en</strong>te cuando <strong>de</strong>see incluir funciones <strong>de</strong><br />

biblioteca <strong>en</strong> sus programas.<br />

Una <strong>de</strong> las características más sobresali<strong>en</strong>tes <strong>de</strong> C<br />

que aum<strong>en</strong>tan consi<strong>de</strong>rablem<strong>en</strong>te la pot<strong>en</strong>cia <strong>de</strong> los<br />

programas es la posibilidad <strong>de</strong> manejar las funciones<br />

<strong>de</strong> modo efici<strong>en</strong>te, apoyándose <strong>en</strong> la propiedad que<br />

les perm.ite ser compiladas por separado.<br />

Otros temas tratados han sido:<br />

Ambit0 o las reglas <strong>de</strong> visibilidad <strong>de</strong> funciones<br />

y variables.<br />

En <strong>en</strong>torno <strong>de</strong> un programa ti<strong>en</strong>e cuatro tipos <strong>de</strong><br />

ámbito: <strong>de</strong> programa, archivo fu<strong>en</strong>te, función y<br />

bloque. Una variable está asociada a uno <strong>de</strong> esos<br />

ámbitos y es invisible (no accesible) <strong>de</strong>s<strong>de</strong> otros<br />

ámbitos.<br />

O Las variables globules se <strong>de</strong>claran fuera <strong>de</strong><br />

cualquier función y son visibles a todas las<br />

funciones. Las variables locales se <strong>de</strong>claran<br />

d<strong>en</strong>tro <strong>de</strong> una función y s610 pued<strong>en</strong> ser<br />

utilizadas por esa función.<br />

int i; /* variable global,<br />

ámbito <strong>de</strong> programa<br />

*/<br />

static int j /* ámbito <strong>de</strong> archivo<br />

*/<br />

main (<br />

t<br />

int d, e; /* variable local,<br />

ámbito <strong>de</strong> función */<br />

...<br />

1<br />

func (int j)<br />

t<br />

if (j z 3)<br />

{<br />

int i; /* ámbito<strong>de</strong> bloque */<br />

for (i = O; i < 20; i++)<br />

func2 íi) ;<br />

1<br />

/* i ya no es visible */<br />

1<br />

Variables automáticas son las variables, por<br />

<strong>de</strong>fecto, <strong>de</strong>claradas localm<strong>en</strong>te <strong>en</strong> una función.<br />

Variables estdticas manti<strong>en</strong><strong>en</strong> su información,<br />

incluso <strong>de</strong>spués que la función ha terminado.<br />

Cuando se llama <strong>de</strong> nuevo la función, la variable<br />

se pone al valor que t<strong>en</strong>ía cuando se llamó anteriorm<strong>en</strong>te.<br />

0 Funciones recursivas son aquellas que se pued<strong>en</strong><br />

llamar a sí mismas.<br />

Las variables registro se pued<strong>en</strong> utilizar cuando<br />

se <strong>de</strong>sea aum<strong>en</strong>tar la velocidad <strong>de</strong> procesami<strong>en</strong>to<br />

<strong>de</strong> ciertas variables.


Funciones 255<br />

7.19. PROBLEM<br />

7.2. Escribir una función que reciba una cad<strong>en</strong>a <strong>de</strong><br />

caracteres y la <strong>de</strong>vueiva <strong>en</strong> forma inversa (ñola’<br />

se convierte <strong>en</strong> ’doh’).<br />

7.3. Escribir una función que <strong>de</strong>termine si una<br />

cad<strong>en</strong>a <strong>de</strong> caracteres es un palíndromo (un<br />

palíndrorno es un texto que se lee igual <strong>en</strong><br />

s<strong>en</strong>tido directo y <strong>en</strong> inverso: radar).<br />

7.4. Escribir un programa m n que<br />

acepte un número <strong>de</strong> Y 10<br />

visuaiice <strong>en</strong> el formato<br />

dd/mm/aa<br />

Por ejemplo, los<br />

visualizan como<br />

8/10/46<br />

s 8, 10 y 1946 se<br />

7.5. Escribir un progr utilice una función<br />

para convertir coord<strong>en</strong>adas pokes a rectangulares.<br />

PfX, Y)<br />

x = r cos8<br />

y = r sine<br />

eje x


256 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong>


c<br />

Funciones 257


CONTENIDO<br />

8.1.<br />

8.8.<br />

8.3.<br />

8.4.<br />

8.5.<br />

Arrays.<br />

InicializaciÓn <strong>de</strong> un array.<br />

Arrays <strong>de</strong> caracteres<br />

y cad<strong>en</strong>as <strong>de</strong> texto.<br />

multidini<strong>en</strong>siona-<br />

ión <strong>de</strong> arrays como<br />

paráníetms.<br />

8.6. Ord<strong>en</strong>ación <strong>de</strong> listas.<br />

8.7. Búsqueda <strong>en</strong> listas.<br />

8.8. Resum<strong>en</strong>.<br />

8.9. Ejerciicios.<br />

8.10. Problemas.<br />

li 258


se han <strong>de</strong>scrito<br />

s elem<strong>en</strong>tos <strong>de</strong>l rriisrno tipo, tales como veinte<br />

un solo carhter;<br />

e cont<strong>en</strong>ga un gr<br />

CONCEPTOS CLAVE<br />

Declasación <strong>de</strong> un array.<br />

259<br />

Iyy


260 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

8.1. ARRAYS<br />

Un array (lista o tabla) es una secu<strong>en</strong>cia <strong>de</strong> <strong>datos</strong> <strong>de</strong>l mismo tipo. Los <strong>datos</strong> se llaman elem<strong>en</strong>tos <strong>de</strong>l<br />

array y se numeran consecutivam<strong>en</strong>te O, 1,2,3, etc. El tipo <strong>de</strong> elem<strong>en</strong>tos almac<strong>en</strong>ados <strong>en</strong> el array pue<strong>de</strong><br />

ser cualquier tipo <strong>de</strong> dato <strong>de</strong> C, incluy<strong>en</strong>do <strong>estructura</strong>s <strong>de</strong>finidas por el usuario, como se <strong>de</strong>scribirá más<br />

tarte. Normalm<strong>en</strong>te el array se utiliza para almac<strong>en</strong>ar tipos tales como char, int o float.<br />

Un array pue<strong>de</strong> cont<strong>en</strong>er, por ejemplo, la edad <strong>de</strong> los alumnos <strong>de</strong> una clase, las temperaturas <strong>de</strong><br />

cada día <strong>de</strong> un mes <strong>en</strong> una ciudad <strong>de</strong>terminada, o el número <strong>de</strong> personas que resid<strong>en</strong> <strong>en</strong> cada una <strong>de</strong> las<br />

diecisiete comunida<strong>de</strong>s autónomas españolas. Cada item <strong>de</strong>l array se d<strong>en</strong>omina elem<strong>en</strong>to.<br />

Los elem<strong>en</strong>tos <strong>de</strong> un array se numeran, como ya se ha com<strong>en</strong>tado, consecutivam<strong>en</strong>te O, I, 2, 3, ...<br />

Estos números se d<strong>en</strong>ominan valores índice o subindice <strong>de</strong>l array. El término «subíndice» se utiliza ya<br />

que se especifica igual que <strong>en</strong> matemáticas, como una secu<strong>en</strong>cia tal como ql, a,, a2... Estos números<br />

localizan la posición <strong>de</strong>l elem<strong>en</strong>to d<strong>en</strong>tro <strong>de</strong>l array, proporcionando acceso directo al array.<br />

Si el nombre <strong>de</strong>l array es a, <strong>en</strong>tonces a [ 01 es el nombre <strong>de</strong>l elem<strong>en</strong>to que está <strong>en</strong> la posición O,<br />

a [ 11 es el nombre <strong>de</strong>l elem<strong>en</strong>to que está <strong>en</strong> la posición 1, etc. En g<strong>en</strong>eral, el elem<strong>en</strong>to i-ésimo esta <strong>en</strong><br />

la posición i-l. De modo que si el array ti<strong>en</strong>e n elem<strong>en</strong>tos, sus nombres son a [ O I , a [ 1 I , ..., a [n- 1 I .<br />

Gráficam<strong>en</strong>te se repres<strong>en</strong>ta así el array a con seis elem<strong>en</strong>tos.<br />

a<br />

25.1 34.2 5.25 7.45 6.09<br />

7.54<br />

Figura 8.1. Array <strong>de</strong> seis elem<strong>en</strong>tos.<br />

El array a ti<strong>en</strong>e 6 elem<strong>en</strong>tos: a [O 1 conti<strong>en</strong>e 25.1. a [ 1 I conti<strong>en</strong>e 34.2, a [ 2 1 conti<strong>en</strong>e 5.25, a [ 3 1<br />

conti<strong>en</strong>e 7.45, a [ 4 ] conti<strong>en</strong>e 6.09 y a [ 5 1 conti<strong>en</strong>e 7.54. El diagrama <strong>de</strong> la Figura 8.1 repres<strong>en</strong>ta<br />

realm<strong>en</strong>te una región <strong>de</strong> la memoria <strong>de</strong> la computadora, ya que un array se almac<strong>en</strong>a siempre con sus<br />

elem<strong>en</strong>tos <strong>en</strong> una secu<strong>en</strong>cia <strong>de</strong> posiciones <strong>de</strong> memoria contigua.<br />

En C los índices <strong>de</strong> un array siempre ti<strong>en</strong><strong>en</strong> como límite inferior O, como índice superior el tamaño<br />

<strong>de</strong>l array m<strong>en</strong>os 1.<br />

8.1.1. Declaración <strong>de</strong> un array<br />

Al igual que con cualquier tipo <strong>de</strong> variable, se <strong>de</strong>be <strong>de</strong>clarar un array antes <strong>de</strong> utilizarlo. Un array se<br />

<strong>de</strong>clara <strong>de</strong> modo similar a otros tipos <strong>de</strong> <strong>datos</strong>, excepto que se <strong>de</strong>be indicar al compilador el tamaRo o<br />

longitud <strong>de</strong>l array. Para indicar al compilador el tamaño o longitud <strong>de</strong>l array se <strong>de</strong>be hacer seguir al<br />

nombre, el tamaño <strong>en</strong>cerrado <strong>en</strong>tre corchetes. La sintaxis para <strong>de</strong>clarar un array <strong>de</strong> una dim<strong>en</strong>sión<br />

<strong>de</strong>terminada es:<br />

tipo nornbreArray [numeroDeEl ern<strong>en</strong> t os 1 ;<br />

Por ejemplo, paracrear un array (lista) <strong>de</strong> diez variables <strong>en</strong>teras, se escribe:<br />

int numeros [ 10 I ;<br />

Esta <strong>de</strong>claración hace que el compilador reserve espacio sufici<strong>en</strong>te para cont<strong>en</strong>er diez valores<br />

<strong>en</strong>teros. En C los <strong>en</strong>teros ocupan, normalm<strong>en</strong>te, 2 bytes, <strong>de</strong> modo que un array <strong>de</strong> diez <strong>en</strong>teros ocupa 20<br />

bytes <strong>de</strong> memoria. La Figura 8.2 muestra el esquema <strong>de</strong> un array <strong>de</strong> diez elem<strong>en</strong>tos; cada elem<strong>en</strong>to<br />

pue<strong>de</strong> t<strong>en</strong>er su propio valor.


Arrays (listas y tablas) 261<br />

Array <strong>de</strong> <strong>datos</strong> <strong>en</strong>teros: a<br />

Un array <strong>de</strong> <strong>en</strong>teros se almac<strong>en</strong>a <strong>en</strong> bytes consecutivos <strong>de</strong> memoria. Cada elem<strong>en</strong>to<br />

utiliza dos bytes. Se acce<strong>de</strong> a cada elem<strong>en</strong>to <strong>de</strong> array mediante un índice que<br />

comi<strong>en</strong>za <strong>en</strong> cero. Así, el elem<strong>en</strong>to quinto (a [ 4 1 ) <strong>de</strong>l array ocupa los bytes 9" y 10".<br />

Figura 8.2. Almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> un array <strong>en</strong> memoria.<br />

Se pue<strong>de</strong> acce<strong>de</strong>r a cada elem<strong>en</strong>to <strong>de</strong>l array utilizando un índice <strong>en</strong> el nombre <strong>de</strong>l array. Por ejemplo,<br />

printf ("%d\n",numeros[4]) ;<br />

visualiza el valor <strong>de</strong>l elem<strong>en</strong>to 5 <strong>de</strong>l array. Los arrays siempre comi<strong>en</strong>zan <strong>en</strong> el elem<strong>en</strong>to O. Así pues, el<br />

array numeros conti<strong>en</strong>e los sigui<strong>en</strong>tes elem<strong>en</strong>tos individuales:<br />

numeros [O] numeros [ 1 ] numeros [ 2 ] numeros [ 3 ]<br />

numeros [ 4 ] numeros [ 5 1 numeros [ 6 ] numeros [ 7 ]<br />

numeros [ 8 ] numeros [ 9 1<br />

Si por ejemplo, se quiere crear un array <strong>de</strong> números reales y su tamaño es una constante repres<strong>en</strong>tada<br />

por un parámetro<br />

#<strong>de</strong>fine N 20<br />

float vector[N];<br />

Para acce<strong>de</strong>r al elem<strong>en</strong>to 3 y leer un valor <strong>de</strong> <strong>en</strong>trada:<br />

scanf ( "%f 'I, &vector [ 2 1 ) ;<br />

Precaución<br />

C no comprueba que los índices <strong>de</strong>l array están d<strong>en</strong>tro <strong>de</strong>l rango <strong>de</strong>finido. Así, por ejemplo, se<br />

pue<strong>de</strong> int<strong>en</strong>tar acce<strong>de</strong>r a numeros [ 12 1 y el compilador no producirá ningún error, lo que pue<strong>de</strong><br />

producir un fallo <strong>en</strong> su programa, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l contexto <strong>en</strong> que se <strong>en</strong>cu<strong>en</strong>tre el error.<br />

8.1.2. Subíndices <strong>de</strong> un array<br />

El índice <strong>de</strong> un array se d<strong>en</strong>omina, con frecu<strong>en</strong>cia, suhindice <strong>de</strong>l arruy. El término proce<strong>de</strong> <strong>de</strong> las<br />

matemáticas, <strong>en</strong> las que un subíndice se utiliza para repres<strong>en</strong>tar un elem<strong>en</strong>to <strong>de</strong>terminado.<br />

numero s<br />

numeros<br />

equivale a<br />

equivule a<br />

numeros [O I<br />

numeros I3 I<br />

El método <strong>de</strong> numeración <strong>de</strong>l elem<strong>en</strong>to i-ésimo con el índice o subíndice i-l se d<strong>en</strong>omina in<strong>de</strong>xución<br />

husada <strong>en</strong> cero. Su uso ti<strong>en</strong>e el efecto <strong>de</strong> que el índice <strong>de</strong> un elem<strong>en</strong>to <strong>de</strong>l array es siempre el mismo que<br />

el número <strong>de</strong> <strong>de</strong>s<strong>de</strong> el elem<strong>en</strong>to inicial a I O I a ese elem<strong>en</strong>to. Por ejemplo, a [ 3 I está a 3 pasos<br />

o posiciones <strong>de</strong>l elem<strong>en</strong>to a 1 O 1 . La v<strong>en</strong>taja <strong>de</strong> este método se verá <strong>de</strong> modo más evid<strong>en</strong>te al tratar las<br />

relaciones <strong>en</strong>tre arrays y punteros.


-<br />

262 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplos<br />

int edad[5] ;<br />

int pesos [25] , longitu<strong>de</strong>s [loo] ;<br />

float salarios [25] ;<br />

double temperaturas[501;<br />

char letras [15] ;<br />

#<strong>de</strong>fine MX 120<br />

charrbuffer[MX+l];<br />

Array edad conti<strong>en</strong>e 5 elem<strong>en</strong>tos: el primero, edad [ O 1 y<br />

el último, edad [ 4 1 .<br />

Declara 2 arrays <strong>de</strong> <strong>en</strong>teros.<br />

Declara un array <strong>de</strong> 25 elem<strong>en</strong>tos float.<br />

Declara un array <strong>de</strong> 50 elem<strong>en</strong>tos double.<br />

Declara un array <strong>de</strong> caracteres.<br />

Declara un array <strong>de</strong> caracteres <strong>de</strong> tamaño MX+l,<br />

el primer elem<strong>en</strong>to es buffer[O] y el último buffer[MX].<br />

En los programas se pued<strong>en</strong> refer<strong>en</strong>ciar elem<strong>en</strong>tos <strong>de</strong>l array utilizando fórmulas para los subíndices.<br />

Mi<strong>en</strong>tras que el subíndice pue<strong>de</strong> evaluar a un <strong>en</strong>tero, se pue<strong>de</strong> utilizar una constante, una variable o una<br />

expresión para el subíndice. Así, algunas refer<strong>en</strong>cias individuales a elem<strong>en</strong>tos son:<br />

edad [ 4 I<br />

v<strong>en</strong>tas [total+51<br />

bonos [mes 1<br />

salario [mes[il*5]<br />

8.1.3. Almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> memoria <strong>de</strong> los arrays<br />

Los elem<strong>en</strong>tos <strong>de</strong> los arrays se almac<strong>en</strong>an <strong>en</strong> bloques contiguos. Así, por<br />

int eda<strong>de</strong>s [ 5 1 ;<br />

char codigos [ 5 I ;<br />

se repres<strong>en</strong>tan gráficam<strong>en</strong>te <strong>en</strong> memoria <strong>en</strong> la Figura 8.3.<br />

I<br />

mplo, los arrays<br />

Eda<strong>de</strong>s<br />

Figura 8.3. Almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> memoria <strong>de</strong> arrays.


Arrays (listas y tablas) 263<br />

Nota<br />

Todos los subíndices <strong>de</strong> los arrays comi<strong>en</strong>zan con O.<br />

Precaución<br />

C permite asignar valores fuera <strong>de</strong> rango a los subíndices. Se <strong>de</strong>be t<strong>en</strong>er cuidado no hacer esta<br />

acción, <strong>de</strong>bido a que se sobreescribirían <strong>datos</strong> o código.<br />

Los arrays <strong>de</strong> caracteres funcionan <strong>de</strong> igual forma que los arrays numéricos, parti<strong>en</strong>do <strong>de</strong> la base <strong>de</strong><br />

que cada carácter ocupa normalm<strong>en</strong>te un byte. Así, por ejemplo, un array llamado nombre se pue<strong>de</strong><br />

repres<strong>en</strong>tar <strong>en</strong> la Figura 8.4.<br />

char nornhre [ ]<br />

= "('azor Id"<br />

F<br />

P-<br />

I<br />

Figura 8.4. Almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> un arrays <strong>de</strong> caracteres <strong>en</strong> memoria.<br />

A t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta, <strong>en</strong> las cad<strong>en</strong>as <strong>de</strong> caracteres el sistema siempre inserta un Último carácter (nulo)<br />

para indicar fin <strong>de</strong> cad<strong>en</strong>a.<br />

8.1.4. El tamaño <strong>de</strong> los arrays<br />

El operador sizeof <strong>de</strong>vuelve el número <strong>de</strong> bytes necesarios para cont<strong>en</strong>er su argum<strong>en</strong>to. Si se usa<br />

sizeof para solicitar el tamaño <strong>de</strong> un array, esta función <strong>de</strong>vuelve el número <strong>de</strong> bytes reservados para<br />

el array completo.<br />

Por ejemplo, supongamos que se <strong>de</strong>clara un array <strong>de</strong> <strong>en</strong>teros <strong>de</strong> 100 elem<strong>en</strong>tos d<strong>en</strong>ominado eda<strong>de</strong>s;<br />

si se <strong>de</strong>sea conocer el tamaño <strong>de</strong>l array, se pue<strong>de</strong> utilizar una s<strong>en</strong>t<strong>en</strong>cia similar a:<br />

n = sizeof(eda<strong>de</strong>s);


L<br />

264 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

don<strong>de</strong> n tomará el valor 200. Si se <strong>de</strong>sea solicitar el tamaño <strong>de</strong> un elem<strong>en</strong>to individual <strong>de</strong>l array, tal<br />

como<br />

n = sizeof(eda<strong>de</strong>s[6]);<br />

n almac<strong>en</strong>ará el valor 2 (número <strong>de</strong> bytes que conti<strong>en</strong><strong>en</strong> un <strong>en</strong>tero).<br />

8.1.5. Verificación <strong>de</strong>l rango <strong>de</strong>l índice <strong>de</strong> un array<br />

C, al contrario que otros l<strong>en</strong>guajes <strong>de</strong> programación -por ejemplo, Pascal-, no verifica el valor <strong>de</strong>l<br />

índice <strong>de</strong> la variable que repres<strong>en</strong>ta al array. Así, por ejemplo, <strong>en</strong> Pascal si se <strong>de</strong>fine un array a con<br />

índices O a 5, <strong>en</strong>tonces a [ 6 1 hará que el programa se «rompa» <strong>en</strong> tiempo <strong>de</strong> ejecución.<br />

Ejemplo 8.1<br />

Protección fr<strong>en</strong>te a errores <strong>en</strong> el intervalo (rango) <strong>de</strong> valores <strong>de</strong> una variable <strong>de</strong> índice que repres<strong>en</strong>ta<br />

un array.<br />

double suma(const double a[], const int n)<br />

t<br />

double S = 0.0;<br />

if (n * sizeof(doub1e) > sizeof(a))<br />

return 0;<br />

for (int i = 0; i < n; i++)<br />

S += a[il;<br />

return S;<br />

1<br />

8.2. INICIALIZACIÓN DE UN ARRAY<br />

Se <strong>de</strong>b<strong>en</strong> asignar valores a los elem<strong>en</strong>tos <strong>de</strong>l array antes <strong>de</strong> utilizarlos, tal como se asignan valores a<br />

variables. Para asignar valores a cada elem<strong>en</strong>to <strong>de</strong>l array <strong>de</strong> <strong>en</strong>teros precios, se pue<strong>de</strong> escribir:<br />

precios[0] = 10;<br />

precios[l] = 20;<br />

preciosl31 = 30;<br />

precios[4] = 40;<br />

...<br />

La primera s<strong>en</strong>t<strong>en</strong>cia fija precios [ 0 1 al valor 10, precios [ 11 al valor 20, etc. Sin embargo, este<br />

método no es práctico cuando el array conti<strong>en</strong>e muchos elem<strong>en</strong>tos. El método utilizado, normalm<strong>en</strong>te,<br />

es inicializar el array completo <strong>en</strong> una sola s<strong>en</strong>t<strong>en</strong>cia.<br />

Cuando se inicializa un array, el tamaño <strong>de</strong>l array se pue<strong>de</strong> <strong>de</strong>terminar automáticam<strong>en</strong>te por las<br />

constantes <strong>de</strong> inicialización. Estas constantes se separan por comas y se <strong>en</strong>cierran <strong>en</strong>tre llaves, como<br />

<strong>en</strong> los sigui<strong>en</strong>tes ejemplos:<br />

int numeros[ól = 110, 20, 30, 40, 50, 601;<br />

int n[l = {3, 4, 51 /* Declara un array <strong>de</strong> 3 elem<strong>en</strong>tos */<br />

char c[] = {’L’,‘u’,’i’,’s’}; /* Declara un array <strong>de</strong> 4 elem<strong>en</strong>tos */<br />

El array numeros ti<strong>en</strong>e 6 elem<strong>en</strong>tos, n ti<strong>en</strong>e 3 elem<strong>en</strong>tos y el array c ti<strong>en</strong>e 4 elem<strong>en</strong>tos.


Arrays (listas y tablas) 265<br />

En C los arrays <strong>de</strong> caracteres, las cad<strong>en</strong>as, se caracterizan por t<strong>en</strong>er un carácter final que indica el<br />

fin <strong>de</strong> la cad<strong>en</strong>a, es el carácter nulo. Lo habitual es inicializar un array <strong>de</strong> caracteres (una variable<br />

cad<strong>en</strong>a) con una constante cad<strong>en</strong>a.<br />

char s[] = "Puesta <strong>de</strong>l Sol";<br />

Nota<br />

c pued<br />

rchetes vacíos, sólo cuando se asignan vdores al<br />

int cu<strong>en</strong>ta!] = (15, 25, -45, O, 501;<br />

El compilador asigna automáticam<strong>en</strong>te cinco elem<strong>en</strong>tos a cu<strong>en</strong>ta<br />

El método <strong>de</strong> inicializar arrays mediante valores constantes <strong>de</strong>spués <strong>de</strong> su <strong>de</strong>finición es a<strong>de</strong>cuado<br />

cuando el número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array es pequeño. Por ejemplo, para inicializar un array (lista) <strong>de</strong> I O<br />

<strong>en</strong>teros a los valores 10 a I, y a continuación visualizar dichos valores <strong>en</strong> un ord<strong>en</strong> inverso, se pue<strong>de</strong><br />

escribir:<br />

int cu<strong>en</strong>ta[lOl = {lo, 9, 8, 7, 6, 5, 4, 3, 2, l};<br />

for (i = 9; i >= O; i--)<br />

printf ("\n cu<strong>en</strong>ta <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te %d = %d',i,cu<strong>en</strong>ta[i]);<br />

Se pued<strong>en</strong> asignar constantes simbólicas como valores numéricos, <strong>de</strong> modo que las s<strong>en</strong>t<strong>en</strong>cias<br />

sigui<strong>en</strong>tes son válidas:<br />

#<strong>de</strong>fine ENE 31<br />

I #<strong>de</strong>fine FER 28<br />

#<strong>de</strong>fine MAR 31<br />

...<br />

int meses[l2] = {ENE, FEB, MAR, ABR, MAY, JUN,<br />

JUL, AGO, SEP, OCT, NOV, DIC};<br />

Pued<strong>en</strong> asignarse valores a un array utilizando un bucle for o while/do-while, y éste suele ser<br />

el sistema más empleado normalm<strong>en</strong>te. Por ejemplo, para inicializar todos los valores <strong>de</strong>l array<br />

numeros al valor O, se pue<strong>de</strong> utilizar la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia:<br />

for (i = O; i


266 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

for (i = O; i < NUM; i++)<br />

i<br />

printf('Por favor, introduzca el número: 'I);<br />

i<br />

scanf ("%d", &nums [ i I ) ;<br />

}<br />

print f ("\nLista <strong>de</strong> números : I' ) ;<br />

for (i = O; i < NUM; i++)<br />

{<br />

1<br />

printf ("%d ",nums [il ) ;<br />

total += nums [ il ;<br />

printf('\nLa suma <strong>de</strong> los números es %d",total);<br />

return O;<br />

Las variables globales que repres<strong>en</strong>tan arrays se inicializan a O por <strong>de</strong>fecto. Por ello, la ejecución <strong>de</strong>l<br />

sigui<strong>en</strong>te programa visualiza O para los 1 O valores <strong>de</strong>l array:<br />

int lista[lOl;<br />

int main0<br />

i<br />

int j;<br />

for (j = O; j


Arrays (listas y tablas) 267<br />

char cad<strong>en</strong>a [ ] = "ABCDEFG" ;<br />

Es importante compr<strong>en</strong><strong>de</strong>r la difer<strong>en</strong>cia <strong>en</strong>tre un array <strong>de</strong> caracter<br />

cad<strong>en</strong>as conti<strong>en</strong><strong>en</strong> un carácter nulo al final <strong>de</strong>l array <strong>de</strong> caracteres.<br />

r<br />

una cad<strong>en</strong>a <strong>de</strong> caracteres. Las<br />

Las cad<strong>en</strong>as se <strong>de</strong>b<strong>en</strong> almac<strong>en</strong>ar <strong>en</strong> arrays <strong>de</strong> caracteres, pero no todos los arrays <strong>de</strong> caracteres<br />

conti<strong>en</strong><strong>en</strong> cad<strong>en</strong>as.<br />

Examine la Figura 8.5. don<strong>de</strong> se muestra una cad<strong>en</strong>a <strong>de</strong> caracteres y un array <strong>de</strong> caracteres.<br />

I,<br />

U<br />

P<br />

1<br />

I<br />

d<br />

,<br />

r<br />

Figura 8.5. (a) Array <strong>de</strong> caracteres; (b) cad<strong>en</strong>a.<br />

Cad<strong>en</strong>a<br />

A B C D E F \O


268 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Cad<strong>en</strong>a[31 = 'D';<br />

Cad<strong>en</strong>a[41 = 'E';<br />

Cad<strong>en</strong>a[5] = IF';<br />

Cad<strong>en</strong>a[bl = '\O';<br />

Sin embargo, no se pue<strong>de</strong> asignar una cad<strong>en</strong>a a un array <strong>de</strong>l sigui<strong>en</strong>te modo:<br />

Cad<strong>en</strong>a = "ABCDEF" ;<br />

Para copiar una constante cad<strong>en</strong>a o copiar una variable <strong>de</strong> cad<strong>en</strong>a a otra variable <strong>de</strong> cad<strong>en</strong>a se <strong>de</strong>be<br />

utilizar la función <strong>de</strong> la biblioteca estándar -posteriorm<strong>en</strong>te se estudiará- strcpy ( ) («copiar cad<strong>en</strong>as»).<br />

strcpy ( ) permite copiar una constante <strong>de</strong> cad<strong>en</strong>a <strong>en</strong> una cad<strong>en</strong>a. Para copiar el nombre "Abracadabra"<br />

<strong>en</strong> el array nombre, se pue<strong>de</strong> escribir<br />

s trcpy (nombre, "Abracadabra" ) ; /*Copia Abracadabra <strong>en</strong> nombre */<br />

strcpy ( ) aña<strong>de</strong> un carácter nulo al final <strong>de</strong> la cad<strong>en</strong>a. A fin <strong>de</strong> que no se produzcan errores <strong>en</strong> la<br />

s<strong>en</strong>t<strong>en</strong>cia anterior, se <strong>de</strong>be asegurar que el array <strong>de</strong> caracteres nombre t<strong>en</strong>ga elem<strong>en</strong>tos sufici<strong>en</strong>tes para<br />

cont<strong>en</strong>er la cad<strong>en</strong>a situada a su <strong>de</strong>recha.<br />

Ejemplo 8.3<br />

Rell<strong>en</strong>ar los elem<strong>en</strong>tos <strong>de</strong> un array con números reales positivos proced<strong>en</strong>tes <strong>de</strong>l teclado.<br />

#inclu<strong>de</strong> <br />

/* Constantes y variables globalcs */<br />

#<strong>de</strong>fine MAX 10<br />

float muestra[MAXl;<br />

void main0<br />

1<br />

int i;<br />

printf("\nIntroduzca una lista <strong>de</strong> Bd elem<strong>en</strong>tos positivos.\n",MAX);<br />

for (i = O; i < MAX; muestra[i]>O?++i:i)<br />

scanf ('%f",&muestra[i] ) ;<br />

En el bucle principal, sólo se increm<strong>en</strong>ta i si muestra [ i 1 es positivo: muestra [ i 1 > 0 ? + + i : i.<br />

Con este increm<strong>en</strong>to condicional se consigue que todos los valores almac<strong>en</strong>ados sean positivos.<br />

Ejemplo 8.4<br />

Visualizar el array muestra <strong>de</strong>spués <strong>de</strong> introducir <strong>datos</strong> <strong>en</strong> el mismo, separándolos con el tabulador:<br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine MAX 10<br />

float muestra[MAXl;<br />

void main0<br />

{<br />

I<br />

int i;<br />

printf("\nIntroduzca una lista <strong>de</strong> ad elem<strong>en</strong>tos positivos.\n",MAX);<br />

for (i = O; i < MAX; muestra[i]>O?++i:i)<br />

scanf ("%f",&muestra[i]);<br />

printf ("\nDatos leidos <strong>de</strong>l teclado: ") ;<br />

for ( i = O, i < MAX; ++i)<br />

printf ('%f\t',muestra[i]) ;


Arrays (listas y tablas) 269<br />

8.4. ARRAYS MULTIDIMENSIONALES<br />

Los arrays vistos anteriorm<strong>en</strong>te se conoc<strong>en</strong> como arrays unidim<strong>en</strong>sionales (una sola dim<strong>en</strong>sión) y se<br />

caracterizan por t<strong>en</strong>er un solo subíndice. Estos arrays se conoc<strong>en</strong> también por el término listas. Los<br />

arrays multidim<strong>en</strong>sionales son aquellos que ti<strong>en</strong><strong>en</strong> más <strong>de</strong> una dim<strong>en</strong>sión y, <strong>en</strong> consecu<strong>en</strong>cia, más <strong>de</strong> un<br />

índice. Los arrays más usuales son los <strong>de</strong> dos dim<strong>en</strong>siones, conocidos también por el nombre <strong>de</strong> tablas<br />

o matrices. Sin embargo, es posible crear arrays <strong>de</strong> tantas dim<strong>en</strong>siones como requieran sus aplicaciones,<br />

esto es, tres, cuatro o más dim<strong>en</strong>siones.<br />

Un array <strong>de</strong> dos dim<strong>en</strong>siones equivale a una tabla con múltiples filas y múltiples columnas (Fig.<br />

8.6).<br />

O 1 2 3 n<br />

I 1 I I<br />

mL u<br />

Figura 8.6. Estructura <strong>de</strong> un array <strong>de</strong> dos dim<strong>en</strong>siones.<br />

Obsérvese que <strong>en</strong> el array bidim<strong>en</strong>sional <strong>de</strong> la Figura 8.6, si las filas se etiquetan <strong>de</strong> O a m y las<br />

columnas <strong>de</strong> O a n, el número <strong>de</strong> elem<strong>en</strong>tos que t<strong>en</strong>drá el array será el resultado <strong>de</strong>l producto (m+ 1 ) x<br />

(n+l). El sistema <strong>de</strong> localizar un elem<strong>en</strong>to será por las coord<strong>en</strong>adas repres<strong>en</strong>tadas por su número <strong>de</strong> fila<br />

y su número <strong>de</strong> columna (a, b). La sintaxis para la <strong>de</strong>claración <strong>de</strong> un array <strong>de</strong> dos dim<strong>en</strong>siones es:<br />

[


270 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Un array <strong>de</strong> dos dim<strong>en</strong>siones <strong>en</strong> realidad es un array <strong>de</strong> arrays. Es <strong>de</strong>cir, es un array unidim<strong>en</strong>sional,<br />

y cada elem<strong>en</strong>to no es un valor <strong>en</strong>tero, o <strong>de</strong> coma flotante o carácter, sino que cada elem<strong>en</strong>to es otro<br />

array.<br />

Los elem<strong>en</strong>tos <strong>de</strong> los arrays se almac<strong>en</strong>an <strong>en</strong> memoria <strong>de</strong> modo que el subíndice más próximo al<br />

nombre <strong>de</strong>l array es la fila y el otro subíndice, la columna. En la Tabla 8.1 se repres<strong>en</strong>tan todos los<br />

elem<strong>en</strong>tos y sus posiciones relativas <strong>en</strong> memoria <strong>de</strong>l array, int tabla [ 4 I [ 2 1 ; suponi<strong>en</strong>do que cada<br />

<strong>en</strong>tero ocupa 2 bytes.<br />

Elem<strong>en</strong>to<br />

~<br />

tablaL01 [O1<br />

tabla[Ol [l]<br />

tabla[ll [O1<br />

tabla[ll [l]<br />

tabla[2] [O]<br />

tabla[21 [l]<br />

tablaL31 [ O1<br />

tabla[31 [ll<br />

Tabla 8.1. Un array bidim<strong>en</strong>sional.<br />

Posición relativa <strong>de</strong> memoria<br />

O<br />

2<br />

4<br />

6<br />

8<br />

10<br />

12<br />

14<br />

8.4.1. Inicialización <strong>de</strong> arrays multidim<strong>en</strong>sionales<br />

Los arrays multidim<strong>en</strong>sionales se pued<strong>en</strong> inicializar, al igual que los <strong>de</strong> una dim<strong>en</strong>sión, cuando se<br />

<strong>de</strong>claran. La inicialización consta <strong>de</strong> una lista <strong>de</strong> constantes separadas por comas y <strong>en</strong>cerradas <strong>en</strong>tre<br />

llaves, como <strong>en</strong> los ejemplos sigui<strong>en</strong>tes:<br />

1. int tabla[2] [ 3] = (51, 52, 53, 54, 55, 56);<br />

o bi<strong>en</strong> <strong>en</strong> los formatos mas amigables:<br />

int tabla[2] [31= { 151, 52, 531,<br />

{54, 55, 56) i ;<br />

int tabla[2] [ 3]= {{Sl, 52, 531, 154, 55, 1611;<br />

int tabla[2] [31= 1<br />

{51, 52, 531,<br />

(54, 55, 561<br />

1;<br />

Fild<br />

O 1 2 3 Columna<br />

Figura 8.7. Tablas <strong>de</strong> dos dim<strong>en</strong>siones.


Arrays (listas y tablas) 271<br />

2. int tabla:! [31 [41 = {<br />

11, 3, 3, 41,<br />

15, 6, '1, 81,<br />

19, 10, 11, 121<br />

1;<br />

Consejo<br />

'<br />

Los arrays multidim<strong>en</strong>sionales (a m<strong>en</strong>os que sean globales) no se inicializan a valores específicos<br />

a m<strong>en</strong>os que se les asign<strong>en</strong> valores <strong>en</strong> el mom<strong>en</strong>to <strong>de</strong> la <strong>de</strong>claración o <strong>en</strong> el programa. Si se<br />

inicializan uno o más elem<strong>en</strong>tos, pero no todos, G rell<strong>en</strong>a el resto con ceros o valores nulos<br />

( ' \ 0 ' ) . Si se <strong>de</strong>sea inicializar a cero un array multidim<strong>en</strong>sional, utilice una s<strong>en</strong>t<strong>en</strong>cia tal como<br />

ésta:<br />

float v<strong>en</strong>tasE31 [41 = {O.,O. ,O. ,O., O. ,O. ,O.,O., O. ,O. , O.,O. 1;<br />

t ah I


272 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

extracción <strong>de</strong> elem<strong>en</strong>tos<br />

= [indice fila] [indice columna];<br />

Algunos ejemplos <strong>de</strong> inserciones:<br />

Tabla[2] [31 = 4.5;<br />

Resist<strong>en</strong>cias[LI [41 = 50;<br />

Asi<strong>en</strong>tosLibres [5] [12] = 5;<br />

y <strong>de</strong> extracción <strong>de</strong> valores:<br />

V<strong>en</strong>tas = Tabla[ll [ll ;<br />

Dia = Semana[31 [61;<br />

8.4.3. Lectura y escritura <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong> arrays bidim<strong>en</strong>sionales<br />

Las funciones <strong>de</strong> <strong>en</strong>trada o salida se aplican <strong>de</strong> igual forma a los elem<strong>en</strong>tos <strong>de</strong> un array bidim<strong>en</strong>sional.<br />

Por ejemplo,<br />

int tablar31 [41;<br />

double resist<strong>en</strong>cias [4] [51 ;<br />

scanf ("%d',&tabla[21 [311;<br />

printf ("%4d",tabla[l][l]);<br />

scanf ("%lf",&resist<strong>en</strong>cias[21[411;<br />

if (asi<strong>en</strong>tosLibres[3][11)<br />

puts ( "VERDADERO" ) ;<br />

else<br />

puts ("FALSO" ) ;<br />

8.4.4. Acceso a elem<strong>en</strong>tos mediante bucles<br />

Se pue<strong>de</strong> acce<strong>de</strong>r a los elem<strong>en</strong>tos <strong>de</strong> arrays bidim<strong>en</strong>sionales mediante bucles anidados. Su sintaxis es:<br />

int IndiceFila, IndiceCol;<br />

for (IndiceFila = O; IndiceFila < NumFilas; ++IndiceFila)<br />

for (IndiceCol = O; IndiceCol < NumCol; ++IndiceCol)<br />

Procesar elem<strong>en</strong>to[IndiceFilal [IndiceColl;<br />

Ejemplo 8.9<br />

Dejine una tabla <strong>de</strong> discos, rell<strong>en</strong>a la tabla con <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada y se muestran por pantalla.<br />

float discos C21 [41 ;<br />

int fila, col;<br />

for (fila = O; fila < 2; fila++)<br />

i<br />

for (col = O; col < 4; col++)<br />

i<br />

i<br />

1<br />

scanf ("%f",&discos[fila][col]) ;<br />

/* Visualizar la tabla */


h<br />

Arrays (listas y tablas) 273<br />

for (fila = O; fila < 2; fila++)<br />

t<br />

for (col = O; col < 4; col++)<br />

I<br />

printf ("\n Pts %.lf \n",discos[fila][col]) ;<br />

I<br />

Ejercicio 8.1<br />

Lectura y visualización <strong>de</strong> un array <strong>de</strong> dos dim<strong>en</strong>siones.<br />

La función leer ( ) lee un array (una tabla) <strong>de</strong> dos dim<strong>en</strong>siones y la función visualizar ( )<br />

pres<strong>en</strong>ta la tabla <strong>en</strong> la pantalla.<br />

#inclu<strong>de</strong> <br />

/* prototipos */<br />

void leer(int a[l [5]);<br />

void visualizar(const int all [51);<br />

int main ( )<br />

{<br />

I<br />

int a[3] [5];<br />

leer (a) ;<br />

visualizar(a);<br />

return O;<br />

void leer (int a [ I [51<br />

I<br />

int i, j;<br />

I<br />

puts("1ntroduzca 15 números <strong>en</strong>teros, 3 por fila");<br />

for (i = O; i < 3; i++)<br />

{<br />

printf ("Fila %d: 'I, i) ;<br />

for (j = O; j < 5; j++)<br />

scanf ("&d",&a[il ljl 1;<br />

I<br />

void visualizar (const int a[] 151)<br />

I<br />

int i, j;<br />

for (i = O; i < 3; i++)<br />

I<br />

r fila


~<br />

274 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Fila O: 45 75 25 10<br />

Fila 1: 20 14 36 15<br />

Fila 2: 21 15 37 16<br />

45 75 25 10 40<br />

20 14 36 15 26<br />

21 15 37 16 27<br />

40<br />

26<br />

27<br />

8.4.5. Arrays <strong>de</strong> más <strong>de</strong> dos dim<strong>en</strong>siones<br />

C proporciona la posibilidad <strong>de</strong> almac<strong>en</strong>ar varias dim<strong>en</strong>siones, aunque raram<strong>en</strong>te los <strong>datos</strong> <strong>de</strong>l mundo<br />

real requier<strong>en</strong> más <strong>de</strong> dos o tres dim<strong>en</strong>siones. El medio más fácil <strong>de</strong> dibujar un array <strong>de</strong> tres dim<strong>en</strong>siones<br />

es imaginar un cubo tal como se muestra <strong>en</strong> la Figura 8.10.<br />

Un array tridim<strong>en</strong>sional se pue<strong>de</strong> consi<strong>de</strong>rar como un conjunto <strong>de</strong> arrays bidim<strong>en</strong>sionales<br />

combinados juntos para formar, <strong>en</strong> profundidad, una tercera dim<strong>en</strong>sión. El cubo se construye con filas<br />

(dim<strong>en</strong>sión vertical), columnas (dim<strong>en</strong>sión horizontal) y planos (dim<strong>en</strong>sión <strong>en</strong> profundidad). Por<br />

consigui<strong>en</strong>te, un elem<strong>en</strong>to dado se localiza especificando su plano, fila y columna. Una <strong>de</strong>finición <strong>de</strong> un<br />

array tridim<strong>en</strong>sional equipos es:<br />

int equipos[31 [151 [lo1 ;<br />

Un ejemplo típico <strong>de</strong> un array <strong>de</strong> tres dim<strong>en</strong>siones es el mo<strong>de</strong>lo libro, <strong>en</strong> el que cada página <strong>de</strong>l<br />

libro es un array bidim<strong>en</strong>sional construido por filas y columnas. Así, por ejemplo, cada página ti<strong>en</strong>e<br />

cuar<strong>en</strong>ta y cinco líneas que forman las filas <strong>de</strong>l array y och<strong>en</strong>ta caracteres por línea, que forman las<br />

columnas <strong>de</strong>l array. Por consigui<strong>en</strong>te, si el libro ti<strong>en</strong>e quini<strong>en</strong>tas páginas, existirán quini<strong>en</strong>tos planos y<br />

el número <strong>de</strong> elem<strong>en</strong>tos será 500 x 80 x 45 = 1.800.000.<br />

4<br />

5<br />

Figura 8.10. Un array<strong>de</strong> tres dim<strong>en</strong>siones (4 x 5 x 3).<br />

8.4.6. Una aplicación práctica<br />

El array libro ti<strong>en</strong>e tres dim<strong>en</strong>siones [PAGINAS] [ LINEAS] [COLUMNAS], que <strong>de</strong>fin<strong>en</strong> el tamaño<br />

<strong>de</strong>l array. El tipo <strong>de</strong> <strong>datos</strong> <strong>de</strong>l array es char, ya que los elem<strong>en</strong>tos son caracteres.<br />

I<br />

i<br />

h<br />

I


Arrays (listas y tablas) 275<br />

¿Cómo se pue<strong>de</strong> acce<strong>de</strong>r a la información <strong>de</strong>l libro? El método más fácil es mediante bucles<br />

anidados. Dado que el libro se compone <strong>de</strong> un conjunto <strong>de</strong> páginas, el bucle más externo será el bucle<br />

<strong>de</strong> página; y el bucle <strong>de</strong> columnas el bucle más interno. Esto significa que el bucle <strong>de</strong> filas se insertará<br />

<strong>en</strong>tre los bucles página y columna. El código sigui<strong>en</strong>te permite procesar el array<br />

int pagina, linea, columna;<br />

for (pagina = O; pagina < PAGINAS; ++pagina)<br />

for (linea = O; linea < LINEAS; ++linea)<br />

for (columna = O; columna < COLUMNAS; ++columna)<br />

<br />

l<br />

Ejercicio 8.2<br />

Comprobar si una matriz <strong>de</strong> números <strong>en</strong>teros es simétrica respecto a la diagonal principal.<br />

La matriz se g<strong>en</strong>era internam<strong>en</strong>te, con la función random ( y argum<strong>en</strong>to N(8) para que la matriz<br />

t<strong>en</strong>ga valores <strong>de</strong> O a 7. El tamaño <strong>de</strong> la matriz se pi<strong>de</strong> como dato <strong>de</strong> <strong>en</strong>trada. La función s ime t r ica ( )<br />

<strong>de</strong>termina si la matriz es simétrica. La función main ( g<strong>en</strong>era matrices hasta <strong>en</strong>contrar una que sea<br />

simétrica y la escribe <strong>en</strong> pantalla.<br />

/*<br />

*/<br />

Determina si una matriz es simétrica. La matriz se g<strong>en</strong>era con números<br />

aleatorios <strong>de</strong> O a 7. El programa itera hasta <strong>en</strong>contrar una matriz<br />

simétrica.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine N 8<br />

void g<strong>en</strong>mat (int a[] [NI , int n) ;<br />

int simetrica(int a[] [NI, int n) ;<br />

void escribemat (int a[] [NI, int n) ;<br />

int main (void)<br />

t<br />

int a[N][N]; /* <strong>de</strong>fine matriz <strong>de</strong> tamaño máximo N */<br />

int n, i, j ;<br />

int es-sim;<br />

randomize ();<br />

do i<br />

printf("\nTamaño <strong>de</strong> cada dim<strong>en</strong>sión <strong>de</strong> la matriz, máximo %d: ",N);<br />

scanf ("%d", &n);<br />

}while (nN);<br />

do i<br />

g<strong>en</strong>-mat (a, n) ;<br />

es-sim = simetrica(a,n);<br />

if (es-sim)<br />

i<br />

puts ("\n\Encontrada matriz simétrica. \n");<br />

escribe-mat(a,n);<br />

1<br />

} while ( !es-sim) ;<br />

return O;


k<br />

276 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

void g<strong>en</strong>mat (int a [ ] [NI , int n)<br />

i<br />

int i,j;<br />

I<br />

for (i=0; i


~ ~ ~--<br />

7<br />

Arrays (listas y tablas) 277<br />

int main0<br />

t<br />

char<br />

palabra [ 4 I ="AB(:";<br />

cambiar- í pa labra ;<br />

puts(pa1abra);<br />

return O;<br />

void<br />

I<br />

Figura 8.11. Paso <strong>de</strong> un array por dirección.<br />

A la función SumaDeDatos se pued<strong>en</strong> <strong>en</strong>tonces pasar argum<strong>en</strong>tos <strong>de</strong> tipo array junto con un <strong>en</strong>tero<br />

n, que informa a la función sobre cuantos valores conti<strong>en</strong>e el array. Por ejemplo, esta s<strong>en</strong>t<strong>en</strong>cia visualiza<br />

la suma <strong>de</strong> valores <strong>de</strong> los <strong>datos</strong> <strong>de</strong>l array:<br />

printf ("\nSuma = %If",SumaDeDatos (<strong>datos</strong>, MAX) ) ;<br />

La función SumaDeDatos no es difícil <strong>de</strong> escribir. Un simple bucle for suma los elem<strong>en</strong>tos <strong>de</strong>l<br />

array y una s<strong>en</strong>t<strong>en</strong>cia return <strong>de</strong>vuelve el resultado <strong>de</strong> nuevo al llamador:<br />

double SumaDeDatos(doub1e <strong>datos</strong>i], int n)<br />

I<br />

double suma = O;<br />

1<br />

while (n > O)<br />

suma += <strong>datos</strong>[--nl;<br />

return suma;<br />

El código que se utiliza para pasar un array a una función incluye el tipo <strong>de</strong> elem<strong>en</strong>to <strong>de</strong>l array y su<br />

nombre. El sigui<strong>en</strong>te ejemplo incluye dos funciones que procesan arrays. En ambas listas <strong>de</strong> parámetros,<br />

el array a [ 1 se <strong>de</strong>clara <strong>en</strong> la lista <strong>de</strong> parámetros tal como<br />

double a[l<br />

El número real <strong>de</strong> elem<strong>en</strong>tos se pasa mediante una variable <strong>en</strong>tera in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te. Cuando se pasa un<br />

array a una función, se pasa realm<strong>en</strong>te sólo la dirección <strong>de</strong> la celda <strong>de</strong> memoria don<strong>de</strong> comi<strong>en</strong>za el<br />

array. Este valor se repres<strong>en</strong>ta por el nombre <strong>de</strong>l array a. La función pue<strong>de</strong> cambiar <strong>en</strong>tonces el<br />

cont<strong>en</strong>ido <strong>de</strong>l array accedi<strong>en</strong>do directam<strong>en</strong>te a las celdas <strong>de</strong> memoria <strong>en</strong> don<strong>de</strong> se almac<strong>en</strong>an los<br />

elem<strong>en</strong>tos <strong>de</strong>l array. Así, aunque el nombre <strong>de</strong>l array se pasa por valor, sus elem<strong>en</strong>tos se pued<strong>en</strong> cambiar<br />

como si se hubieran pasado por refer<strong>en</strong>cia.<br />

Ejemplo 8.5<br />

Paso <strong>de</strong> arrays a funciones. En el ejemplo se lee un array y se escribe.<br />

El array ti<strong>en</strong>e un tamaño máximo, L, aunque el número real <strong>de</strong> elem<strong>en</strong>tos es <strong>de</strong>terminado <strong>en</strong> la<br />

función leerArray ( ) . El segundo argum<strong>en</strong>to es, por tanto, un puntero para así po<strong>de</strong>r transmitir por<br />

refer<strong>en</strong>cia y obt<strong>en</strong>er dicho dato <strong>de</strong> la función.


k<br />

278 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine L 100<br />

void leerArray(doub1e a[], int* );<br />

void imprimirArray (const double [ I ,<br />

int);<br />

int main()<br />

i<br />

double a[L] ;<br />

int n;<br />

1eerArray (a, &n) ;<br />

printf("E1 array a ti<strong>en</strong>e %d elem<strong>en</strong>tos, estos son\n ",n);<br />

imprimirArray (a, n) ;<br />

1<br />

return O;<br />

void leerArray(doub1e a[], int* num)<br />

i<br />

int n = 0;<br />

puts("1ntroduzca <strong>datos</strong>. Para terminar pulsar O.\n");<br />

for (; n < L; n++)<br />

i<br />

printf ("%d:",n);<br />

scanf ("%lf",&a[n]);<br />

if (a[n] == O) break;<br />

I;<br />

*num = n;<br />

1<br />

void imprimirArray(const double a[l,int n)<br />

t<br />

int i = 0;<br />

for (; i < n; i++)<br />

printf ("\t%d:%lf\n",i,a[i]);<br />

1: 15.25<br />

2: 44.77<br />

3: O<br />

El array ti<strong>en</strong>e tres elem<strong>en</strong>tos, éstos son:<br />

O: 31.31<br />

1: 15 -25<br />

2: 44.77


Arrays (listas y tablas) 279<br />

Ejercicio 8.2<br />

Escribir una función que calcule el máximo <strong>de</strong> los primeros n elem<strong>en</strong>tos <strong>de</strong> un array especificado.<br />

double maximo(const double a[l,int n)<br />

double mx;<br />

int i;<br />

mx = a[Ol;<br />

for (i = 1; i < n; i++)<br />

m~ = (a[i]>mx ? a[il: mx);<br />

I<br />

return mx;<br />

8.5.1. Precauciones<br />

Cuando se utiliza una variable array como argum<strong>en</strong>to, la función receptora pue<strong>de</strong> no conocer cuántos<br />

elem<strong>en</strong>tos exist<strong>en</strong> <strong>en</strong> el array. Sin su conocimi<strong>en</strong>to una función no pue<strong>de</strong> utilizar el array. Aunque la<br />

variable array pue<strong>de</strong> apuntar al comi<strong>en</strong>zo <strong>de</strong> él, no proporciona ninguna indicación <strong>de</strong> don<strong>de</strong> termina el<br />

array.<br />

La función SumaDeEnteros ( ) suma los valores <strong>de</strong> todos los elem<strong>en</strong>tos <strong>de</strong> un array y <strong>de</strong>vuelve el<br />

total.<br />

int SumaDeEnteros(int "ArrayEnteros)<br />

{<br />

1<br />

int main( )<br />

{<br />

1<br />

...<br />

int lista[i] = {lo, 11, 12, 13, 141;<br />

SumaDeEnteros (lista);<br />

...<br />

Aunque SumaDeEnteros ( ) conoce don<strong>de</strong> comi<strong>en</strong>za el array, no conoce cuántos elem<strong>en</strong>tos hay <strong>en</strong><br />

el array; <strong>en</strong> consecu<strong>en</strong>cia, no sabe cuántos elem<strong>en</strong>tos hay que sumar.<br />

Se pued<strong>en</strong> utilizar dos métodos alternativos para permitir que una función conozca el número<br />

<strong>de</strong> argum<strong>en</strong>tos asociados con un array que se pasa como argum<strong>en</strong>to <strong>de</strong> una función:<br />

situar un valor <strong>de</strong> señal al final <strong>de</strong>l array, que indique a la función que se ha <strong>de</strong> <strong>de</strong>t<strong>en</strong>er<br />

el proceso <strong>en</strong> ese mom<strong>en</strong>to;<br />

un segundo argum<strong>en</strong>to que indica el número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array.<br />

I<br />

Todas las cad<strong>en</strong>as utilizan el primer método ya que terminan <strong>en</strong> nulo. Una segunda alternativa es<br />

pasar el número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array siempre que se pasa el array como un argum<strong>en</strong>to. El array y el<br />

número <strong>de</strong> elem<strong>en</strong>tos se conviert<strong>en</strong> <strong>en</strong>tonces <strong>en</strong> una pareja <strong>de</strong> argum<strong>en</strong>tos que se asocian con la función<br />

llamada. La función SumaDeEnteros ( 1, por ejemplo, se pue<strong>de</strong> actualizar así:<br />

int SumaDeEnteros(int ArrayEnteros[], int NoElem<strong>en</strong>tos)<br />

i<br />

...<br />

1


280 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El segundo argum<strong>en</strong>to, NoElem<strong>en</strong>tos, es un valor <strong>en</strong>tero que indica a la función SumaDeEnteros ( )<br />

cuantos elem<strong>en</strong>tos se procesarán <strong>en</strong> el array ArrayEnteros . Este método suele ser el utilizado para<br />

arrays <strong>de</strong> elem<strong>en</strong>tos que no son caracteres.<br />

Ejemplo 8.6<br />

Este programa introduce una lista <strong>de</strong> I O números <strong>en</strong>teros y calcula su suma y el valor máximo.<br />

#inclu<strong>de</strong> <br />

int SumaDeEnteros(const int ArrayEnteros[], int NoElem<strong>en</strong>tos);<br />

int maximo(const int ArrayEnteros[], int NoElem<strong>en</strong>tos);<br />

int main0<br />

{<br />

int items [lo] ;<br />

int Total, i;<br />

puts('1ntroduzca 10 números, seguidos por return");<br />

for (i = O; i < 10; i++)<br />

scanf ("%d",&Items[il) ;<br />

printf('Tota1 = %d \n',SumaDeEnteros(Items,lO));<br />

printf("Va1or máximo: %d \n",maximo(Items,lO) 1;<br />

return O;<br />

1<br />

int SumaDeEnteros(cons int ArrayEnteros[l, int NoElem<strong>en</strong>tos)<br />

i<br />

int i, Total = O;<br />

for (i = 0; i < NoElem<strong>en</strong>tos; i++)<br />

Total += ArrayEnteros[i];<br />

1<br />

return Total;<br />

int maximo(const int ArrayEnteros[], int NoElem<strong>en</strong>tos)<br />

i<br />

int mx;<br />

int i;<br />

1<br />

mx = ArrayEnteros [ 0 I ;<br />

for (i = 1; i < NoElem<strong>en</strong>tos; i++)<br />

mx = (ArrayEnteros [i] >mx ? ArrayEnteros [i] : mx) ;<br />

return mx;<br />

El sigui<strong>en</strong>te programa muestra cómo se pasa un array <strong>de</strong> <strong>en</strong>teros a una función <strong>de</strong> ord<strong>en</strong>ación,<br />

ord<strong>en</strong>ar ( ) .<br />

#inclu<strong>de</strong> <br />

void ord<strong>en</strong>ar(int[l,int); /* prototipo <strong>de</strong> ord<strong>en</strong>ar */<br />

int main ( )<br />

t<br />

int ListaEnt[ ] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 101;<br />

int i;<br />

int LongLista = sizeof(ListaEnt) / sizeof(int);<br />

ord<strong>en</strong>ar(ListaEnt,LongLista);


1<br />

Arrays (listas y tablas) 281<br />

1<br />

for (i = O; i < LongLista; i++)<br />

printf ("%d",ListaEnt[il ) ;<br />

return O;<br />

void ord<strong>en</strong>ar(int lista[l,int numElem<strong>en</strong>tos)<br />

{<br />

/* cuerpo <strong>de</strong> la función ord<strong>en</strong>ar el array */<br />

1<br />

Como C trata las cad<strong>en</strong>as como arrays <strong>de</strong> caracteres, las reglas para pasar arrays como argum<strong>en</strong>tos<br />

a funciones se aplican también a cad<strong>en</strong>as. El sigui<strong>en</strong>te ejemplo <strong>de</strong> una función <strong>de</strong> cad<strong>en</strong>a que convierte<br />

los caracteres <strong>de</strong> sus argum<strong>en</strong>tos a mayúsculas, muestra el paso <strong>de</strong> parámetros tipo cad<strong>en</strong>a.<br />

void conviertemayus(char cad[])<br />

i<br />

int i = O;<br />

int intervalo = 'a'-'A ';<br />

while (cad[il)<br />

1<br />

{<br />

1<br />

cad[i] = (cad[i]>='a' && cad[il


282 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

8.6. ORDENACIÓN DE LISTAS<br />

La ord<strong>en</strong>ación <strong>de</strong> arrays es otra <strong>de</strong> las tareas usuales <strong>en</strong> la mayoría <strong>de</strong> los programas. La ord<strong>en</strong>ación o<br />

clasificación es el procedimi<strong>en</strong>to mediante el cual se dispon<strong>en</strong> los elem<strong>en</strong>tos <strong>de</strong>l array <strong>en</strong> un ord<strong>en</strong><br />

especificado, tal como ord<strong>en</strong> alfabético u ord<strong>en</strong> numérico.<br />

18<br />

14<br />

Lista<br />

<strong>de</strong>sord<strong>en</strong>ada<br />

Lista ord<strong>en</strong>ada<br />

(asc<strong>en</strong>d<strong>en</strong>te)<br />

Lista ord<strong>en</strong>ada<br />

(<strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te)<br />

Figura 8.12. Lista <strong>de</strong> números <strong>de</strong>sord<strong>en</strong>ada y ord<strong>en</strong>ada <strong>en</strong> ord<strong>en</strong> asc<strong>en</strong>d<strong>en</strong>te y <strong>en</strong> ord<strong>en</strong> <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te.<br />

Un diccionario es un ejemplo <strong>de</strong> una lista ord<strong>en</strong>ada alfabéticam<strong>en</strong>te, y una ag<strong>en</strong>da telefónica o lista<br />

<strong>de</strong> cu<strong>en</strong>tas <strong>de</strong> un banco es un ejemplo <strong>de</strong> una lista ord<strong>en</strong>ada numéricam<strong>en</strong>te. El ord<strong>en</strong> <strong>de</strong> clasificación<br />

u ord<strong>en</strong>ación pue<strong>de</strong> ser asc<strong>en</strong>d<strong>en</strong>te o <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te.<br />

Exist<strong>en</strong> numerosos <strong>algoritmos</strong> <strong>de</strong> ord<strong>en</strong>ación <strong>de</strong> arrays: inserción, burbuja, selección, rápido (quick<br />

sort), fusión (merge), montículo (heap), shell, etc.<br />

8.6.1. Algoritmo <strong>de</strong> la burbuja<br />

La ord<strong>en</strong>ación por burbuja es uno <strong>de</strong> los métodos más fáciles <strong>de</strong> ord<strong>en</strong>ación. El método (algoritmo) <strong>de</strong><br />

ord<strong>en</strong>ación es muy simple. Se compara cada elem<strong>en</strong>to <strong>de</strong>l array con el sigui<strong>en</strong>te (por parejas), si no<br />

están <strong>en</strong> el ord<strong>en</strong> correcto, se intercambian <strong>en</strong>tre sí sus valores. El valor más pequeñoflota hasta la parte<br />

superior <strong>de</strong>l array como si fuera una burbuja <strong>en</strong> un vaso <strong>de</strong> refresco con gas.<br />

La Figura 8.13 muestra una lista <strong>de</strong> números, antes, durante las sucesivas comparaciones y a la<br />

terminación <strong>de</strong>l algoritmo <strong>de</strong> la burbuja. Se van realizando difer<strong>en</strong>tes pasadas hasta que la lista se<br />

<strong>en</strong>cu<strong>en</strong>tra ord<strong>en</strong>ada totalm<strong>en</strong>te <strong>en</strong> ord<strong>en</strong> asc<strong>en</strong>d<strong>en</strong>te.<br />

Lista <strong>de</strong>sord<strong>en</strong>ada: 6 4 10 2 8<br />

Primera pasada 6 4 4 4<br />

4 6 6 6<br />

10 10 2 2<br />

2 2 10 8<br />

8 8 8 10<br />

Segunda pasada 4 4<br />

6 2<br />

2 6<br />

8 8<br />

10 10


Arrays (listas y tablas) 283<br />

Tercera pasada 4 2<br />

2 4<br />

6 6<br />

8 8<br />

10 10<br />

Cuarta pasada 2<br />

4<br />

6<br />

8<br />

10<br />

Figura 8.13. Secu<strong>en</strong>cias <strong>de</strong> ord<strong>en</strong>ación.<br />

La ord<strong>en</strong>ación <strong>de</strong> arrays requiere siempre un intercambio <strong>de</strong> valores, cuando éstos no se <strong>en</strong>cu<strong>en</strong>tran<br />

<strong>en</strong> el ord<strong>en</strong> previsto. Si, por ejemplo, <strong>en</strong> la primera pasada 6 y 4 no están ord<strong>en</strong>ados se han <strong>de</strong><br />

intercambiar sus valores. Suponi<strong>en</strong>do que el array se d<strong>en</strong>omina lista:<br />

lista [O I 6<br />

lista[l] 4<br />

lista [ 21 10<br />

lista[3] 2<br />

lista[4] 8<br />

para intercambiar dos valores, se necesita utilizar una tercera variable auxiliar que cont<strong>en</strong>ga el resultado<br />

inmediato. Así, por ejemplo, si las dos variables son 1 i s t a [ O I y 1 1 s t a [ 1 1 , el sigui<strong>en</strong>te código realiza<br />

el intercambio <strong>de</strong> dos variables:<br />

Ejemplo 8.7<br />

La función intercambio intercambia los valores <strong>de</strong> dos variables x e y<br />

El algoritmo <strong>de</strong> intercambio utiliza una variable auxiliar<br />

aux = x;<br />

x = y;<br />

y = aux;<br />

La función intercambio sirve para intercambiar dos elem<strong>en</strong>tos x e y que se pasan a ella. Al t<strong>en</strong>er<br />

que pasar por refer<strong>en</strong>cia, los argum<strong>en</strong>tos <strong>de</strong> la función son punteros.<br />

void intercambio(float* x, float" y)<br />

i<br />

float aux;<br />

aux = *x;<br />

*x = *y;<br />

*y = aux;<br />

I<br />

Una llamada a esta función:<br />

float r, v;<br />

intercambio(&r,&v) ;


284 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 8.8<br />

El programa sigui<strong>en</strong>te ord<strong>en</strong>a una lista <strong>de</strong> números reales y a continuación los imprime.<br />

#inclu<strong>de</strong> <br />

/* prototipos */<br />

void imprimir(f1oat a[], int n);<br />

void intercambio(float* x, float* y);<br />

void ord<strong>en</strong>ar (float a[], int n) ;<br />

int main( )<br />

float a[10]={25.5,34.1,27.6,15.24. 3.27, 5.14, 6.21,7.57,4.61, 5.41;<br />

imprimir(a,lO);<br />

ord<strong>en</strong>ar (a, 1 O ) ;<br />

imprimir(a,lO);<br />

return O;<br />

1<br />

void imprimir(f1oat a[], int n)<br />

i<br />

int i = O;<br />

for (; i < n-1; i++) {<br />

printf ("%f,%c",a[i], ((i+1)%10==0? '\n' : ' ') 1;<br />

1<br />

printf ("%f \n",a[n-11);<br />

1<br />

void intercambio(float* x, float* y)<br />

{<br />

1<br />

float aux;<br />

aux = *x;<br />

*x = *y;<br />

*y = aux;<br />

/* ord<strong>en</strong>ar burbuja */<br />

void ord<strong>en</strong>ar (float a[], int n)<br />

{<br />

1<br />

int i, j;<br />

for (i = n-1; i>O; i--)<br />

for (j =,O; j < i; j++)<br />

if (a[]] > a[j+ll)<br />

intercambio(&a[jl ,&a[j+ll 1 ;<br />

8.7. BÚSQUEDA EN LISTAS<br />

Los arrays (listas y tablas) son uno <strong>de</strong> los medios principales por los cuales se almac<strong>en</strong>an los <strong>datos</strong> <strong>en</strong><br />

programas C. Debido a esta causa, exist<strong>en</strong> operaciones fundam<strong>en</strong>tales cuyo tratami<strong>en</strong>to es imprescindible<br />

conocer. Estas operaciones es<strong>en</strong>ciales son: la búsqueda <strong>de</strong> elem<strong>en</strong>tos y la ord<strong>en</strong>ación o clasificación<br />

<strong>de</strong> las listas.<br />

La búsqueda <strong>de</strong> un elem<strong>en</strong>to dado <strong>en</strong> un array (lista o tabla) es una aplicación muy usual <strong>en</strong> el<br />

<strong>de</strong>sarrollo <strong>de</strong> programas <strong>en</strong> C. Dos <strong>algoritmos</strong> típicos que realizan esta tarea son la búsqueda secu<strong>en</strong>cial


1<br />

Arrays (listas y tablas) 285<br />

o <strong>en</strong> serie y la búsqueda binaria o dicotómica. La búsqueda secu<strong>en</strong>cial es el método utilizado para<br />

listas no ord<strong>en</strong>adas, mi<strong>en</strong>tras que la búsqueda binaria se utiliza <strong>en</strong> arrays que ya están ord<strong>en</strong>ados.<br />

8.7.1. Búsqueda secu<strong>en</strong>cial<br />

Este algoritmo busca el elem<strong>en</strong>to dado, recorri<strong>en</strong>do secu<strong>en</strong>cialm<strong>en</strong>te el array <strong>de</strong>s<strong>de</strong> un elem<strong>en</strong>to al<br />

sigui<strong>en</strong>te, com<strong>en</strong>zando <strong>en</strong> la primera posición <strong>de</strong>l array y se <strong>de</strong>ti<strong>en</strong>e cuando se <strong>en</strong>cu<strong>en</strong>tra el elem<strong>en</strong>to<br />

buscado o bi<strong>en</strong> se alcanza el final <strong>de</strong>l array.<br />

Por consigui<strong>en</strong>te, el algoritmo <strong>de</strong>be comprobar primero el elem<strong>en</strong>to almac<strong>en</strong>ado <strong>en</strong> la primera<br />

posición <strong>de</strong>l array, a continuación el segundo elem<strong>en</strong>to y así sucesivam<strong>en</strong>te, hasta que se <strong>en</strong>cu<strong>en</strong>tra el<br />

elem<strong>en</strong>to buscado o se termina el recorrido <strong>de</strong>l array. Esta tarea repetitiva se realizará con bucles, <strong>en</strong><br />

nuestro caso con el bucle while.<br />

Algoritmo BusquedaSec<br />

Se utiliza una variable lógica, <strong>en</strong> C tipo int, d<strong>en</strong>ominada Encontrado, que indica si el elem<strong>en</strong>to se<br />

<strong>en</strong>contró <strong>en</strong> la búsqueda. La variable Encontrado se inicializa a fulso(0) y se activa a verdu<strong>de</strong>ro(1)<br />

cuando se <strong>en</strong>cu<strong>en</strong>tra el elem<strong>en</strong>to. Se utiliza un operador and ( <strong>en</strong> c &&) , que permita evaluar las dos<br />

condiciones <strong>de</strong> terminación <strong>de</strong> la búsqueda: elem<strong>en</strong>to <strong>en</strong>contrado o no haya más elem<strong>en</strong>tos (índice <strong>de</strong>l<br />

array exce<strong>de</strong> al último valor válido <strong>de</strong>l mismo).<br />

Cuando el bucle se termina, el elem<strong>en</strong>to o bi<strong>en</strong> se ha <strong>en</strong>contrado, o bi<strong>en</strong> no se ha <strong>en</strong>contrado. Si el<br />

elem<strong>en</strong>to se ha <strong>en</strong>contrado, el valor <strong>de</strong> Encontrado será verda<strong>de</strong>ro y el valor <strong>de</strong>l índice será la posición<br />

<strong>de</strong>l array (índice <strong>de</strong>l elem<strong>en</strong>to <strong>en</strong>contrado). Si el elem<strong>en</strong>to no se ha <strong>en</strong>contrado, el valor <strong>de</strong> Encontrado<br />

seráfalso y se <strong>de</strong>vuelve el valor -I ai programa llamador.<br />

BusquedaSec<br />

inicio<br />

Poner Encontrado = falso<br />

Poner Indice = primer indice <strong>de</strong>l array<br />

mi<strong>en</strong>tras (Elem<strong>en</strong>to no Encontrado) y (Indice < Ultimo) hacer<br />

si (A[indice] = Elem<strong>en</strong>to) <strong>en</strong>tonces<br />

Poner Encontrado a Verda<strong>de</strong>ro<br />

si no<br />

Increm<strong>en</strong>tar Indice<br />

fin-mi<strong>en</strong>tras<br />

si (Encontrado) <strong>en</strong>tonces<br />

retorno ( Indice)<br />

si no<br />

retorno (-1)<br />

fin-si<br />

€in<br />

El algoritmo anterior implem<strong>en</strong>tado como una función para un array Lista es:<br />

<strong>en</strong>um {FALSE, TRUE};<br />

int BusquedaSec(int Lista[MAX], int Elem<strong>en</strong>to)<br />

i<br />

int Encontrado = FALSE;<br />

int i = O;<br />

/* Búsqueda <strong>en</strong> la lista hasta que se <strong>en</strong>cu<strong>en</strong>tra el elem<strong>en</strong>to<br />

o se alcanza el final <strong>de</strong> la lista.<br />

*/


286 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

while ((!Encontrado) && (i


I<br />

return O;<br />

item,veces);<br />

/* Busqueda lineal <strong>en</strong> toda la matriz */<br />

int buscar(int a[] [Cl,int* fila,int* co1,int elem<strong>en</strong>to)<br />

Arrays (listas y tablas) 287<br />

I'<br />

}<br />

static int x = O, y = -1;<br />

int i,j,<strong>en</strong>contrado;<br />

/* avanza al sigui<strong>en</strong>te elem<strong>en</strong>to(fila,columna) */<br />

if (y == C-1) /* ultima columna */<br />

I<br />

y = o;<br />

x = x+l;<br />

I<br />

else<br />

y = y+l;<br />

<strong>en</strong>contrado = O;<br />

while (!<strong>en</strong>contrado && (x


288 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 8.10<br />

En este programa se quiere buscar la fila <strong>de</strong> una matriz real que ti<strong>en</strong>e la máxima suma <strong>de</strong> sus elem<strong>en</strong>tos<br />

<strong>en</strong> valor absoluto, La matriz se g<strong>en</strong>era con números aleatorios, las dim<strong>en</strong>siones <strong>de</strong> la matriz se<br />

establec<strong>en</strong> con una constante pre<strong>de</strong>jinida.<br />

Para <strong>de</strong>terminar la suma <strong>de</strong> una fila se <strong>de</strong>fine la función sumar ( ) , se la pasa la dirección <strong>de</strong>l primer<br />

elem<strong>en</strong>to <strong>de</strong> la fila para tratar cada fila como una array unidim<strong>en</strong>sional. Para g<strong>en</strong>erar números aleatorios<br />

<strong>de</strong> tipo real, se divi<strong>de</strong> el número que <strong>de</strong>vuelve la función rand ( ) <strong>en</strong>tre 100.0.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine F 6<br />

#<strong>de</strong>fine C 10<br />

#<strong>de</strong>fine V 100.0<br />

void escribe-mat (float mt [ I [Cl ) ;<br />

void g<strong>en</strong>-mat (float mt [ 1 [Cl ) ;<br />

float sumar(f1oat v[l);<br />

int maximo(f1oat mt [I [Cl ) ;<br />

int main ( )<br />

t<br />

float mat [Fl [C] ;<br />

int fila;<br />

1<br />

randomize ( ) ;<br />

g<strong>en</strong>-mat (mat) ;<br />

escribemat(mat) ;<br />

fila = maximo(mat);<br />

printf("\n\nFila cuya suma <strong>de</strong> elem<strong>en</strong>tos es mayor: %d",fila);<br />

return O;<br />

void g<strong>en</strong>-mat (float mat [ 1 [Cl<br />

{<br />

1<br />

int i, j;<br />

for (i=O; i


Arrays (listas y tablas) 289<br />

I<br />

for (s=O.O,i=O; id; i++)<br />

s += v[i];<br />

return s;<br />

int maxirno(f1oat mt [I [Cl )<br />

{<br />

float mx;<br />

int i, f;<br />

mx = sumar(&mt[O][O]); /* dirección <strong>de</strong> primera fila */<br />

printf ("\nSuma fila %d %.2f",O,mx);<br />

for (f=O,i=l; i rnx)<br />

{<br />

mx = t;<br />

f = i;<br />

I<br />

I<br />

return f;<br />

1


290 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

8.9. EJERCICIOS<br />

Para los Ejercicios 8.1 a 8.5, suponga las <strong>de</strong>claraciones:<br />

int i, j,k;<br />

int Primero [21], Segundo [211;<br />

int TerceroL61 [I21 ;<br />

Determinar la salida <strong>de</strong> cada segm<strong>en</strong>to <strong>de</strong> programa<br />

(<strong>en</strong> los casos que se necesite, se indica <strong>de</strong>bajo el<br />

archivo <strong>de</strong> <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada correspondi<strong>en</strong>te).<br />

8.1. for (i=l; i


Arrays (listas y tablas) 291<br />

8.1.<br />

8.2.<br />

8.3.<br />

8.4.<br />

85.<br />

8.6.<br />

número arábigo.<br />

Reglas <strong>de</strong> conversión<br />

M 1000<br />

D 500<br />

c 100<br />

L 50<br />

X 10<br />

V 5<br />

1 1<br />

nvierta un número<br />

Escribir un programa que permita visualizar el<br />

triángulo <strong>de</strong> Pascal:<br />

1 2 1<br />

1 3 3 1 8.7.<br />

1 4 6 4 1<br />

1 5 10 1 0 5 1<br />

1 6 15 20 15 6 1<br />

En el triángulo <strong>de</strong> Pascal cada número es la<br />

suma <strong>de</strong> los dos números situados <strong>en</strong>cima <strong>de</strong><br />

él. Este problema se <strong>de</strong>be resolver utilizando<br />

un array <strong>de</strong> una sola dim<strong>en</strong>sión.<br />

Escribir una función que invierta el cont<strong>en</strong>ido<br />

<strong>de</strong> n números <strong>en</strong>teros. El primero se vuelve el<br />

último; el segundo, el p<strong>en</strong>último, etc.<br />

Escribir una función a la cual se le proporcione<br />

una fecha (día, mes, año), así como un número<br />

<strong>de</strong> días a añadir a esta fecha. La función<br />

calcula la nueva fecha y se visualiza.<br />

Un número <strong>en</strong>tero es primo si ningún otro<br />

número primo más pequeño que él es divisor<br />

suyo. A continuación escribir<br />

rell<strong>en</strong>e una tabla con los 80 primeros números<br />

primos y los visualice.<br />

Escribir un programa que visualice un cuadrado<br />

mágico <strong>de</strong> ord<strong>en</strong> impar n compr<strong>en</strong>dido<br />

<strong>en</strong>tre 3 y I 1 ; el usuario <strong>de</strong>be elegir el valor <strong>de</strong><br />

- 8.8.<br />

8.9.<br />

Ejemplo<br />

8 1 6<br />

3 5 7<br />

rado caiga <strong>en</strong> una<br />

casilla situada<br />

<strong>de</strong> ser situado.<br />

letras individuales. Diseñar un programa para<br />

jugar al ahorcado. Sugemncia: almac<strong>en</strong>ar una<br />

lista <strong>de</strong> palabras <strong>en</strong> un array y seleccionar palabras<br />

aleatoriam<strong>en</strong>te.<br />

Escribir un programa que lea las dim<strong>en</strong>siones<br />

<strong>de</strong> una matriz, lea y visualice la matriz y a<br />

continuación <strong>en</strong>cu<strong>en</strong>tre el mayor y m<strong>en</strong>or<br />

elem<strong>en</strong>to <strong>de</strong> la matriz y sus posiciones.<br />

Si x repres<strong>en</strong>ta la media <strong>de</strong> los niimeros xi,<br />

xz, ..A,,, <strong>en</strong>tonces la varianza es la media <strong>de</strong> los<br />

cuadrados <strong>de</strong> las <strong>de</strong>sviaciones <strong>de</strong> los números<br />

<strong>de</strong> la media.<br />

Y la <strong>de</strong>sviación esbandar es la raíz cuadra-<br />

continuación calcule e imprima su media,<br />

varianza y <strong>de</strong>sviación estándar. Utilizar funciones<br />

para calcular la media, vananZa y <strong>de</strong>sviacibn<br />

estándar.


292 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

tro un vector que pue<strong>de</strong> cont<strong>en</strong>er elem<strong>en</strong>tos<br />

duplicados. La función <strong>de</strong>be sustituir cada<br />

valor repetido por -5 y <strong>de</strong>volver ai punto don<strong>de</strong><br />

fue llamado el vector modificado y el<br />

número <strong>de</strong> <strong>en</strong>tradas modificadas.<br />

8.12. Los resultados <strong>de</strong> las Últimas elecciones a<br />

alcal<strong>de</strong> <strong>en</strong> el pueblo x han sido los sigui<strong>en</strong>tes:<br />

Distrito Cdidato Candidato Candidato Candidaio<br />

A 3 c D<br />

1 194 48 206 45<br />

2 180 20 16<br />

3 221 90 20<br />

4 432 50 821 14<br />

5 820 61 946 18<br />

Escribir un<br />

tes tareas:<br />

a que haga las sigui<strong>en</strong>-<br />

a) Imprimir la tabla anterior con cabeceras<br />

incluidas.<br />

1<br />

2<br />

3<br />

10<br />

necesita saber cuál es el v<strong>en</strong><strong>de</strong>dor que más<br />

coches ha v<strong>en</strong>dido.<br />

1 2 3 4... 15<br />

4 8 1 4<br />

12 4 25 14<br />

15 3 4 7<br />

8.15. Diseñar un programa que <strong>de</strong>termine la fre-<br />

.E tante.<br />

mbre <strong>de</strong> los dos candi<strong>datos</strong><br />

8.18. Escribir un programa que lea una frase y a conthuaci6n<br />

visuaii abra <strong>de</strong> la frase <strong>en</strong><br />

820. Escribir u lea una linea <strong>de</strong>


Arrays (listas y tablas) 293<br />

8.22. Escribir una serie <strong>de</strong><br />

cad<strong>en</strong>as,<br />

e si la cad<strong>en</strong>a<br />

es un i<strong>de</strong><br />

la sintaxis <strong>de</strong><br />

C. Suger<strong>en</strong>cias: utilizar las sigui<strong>en</strong>tes funcio-<br />

&o <strong>de</strong>l id<strong>en</strong>tifícador <strong>en</strong> el<br />

primero (<strong>de</strong>terminar si el<br />

nombre comi<strong>en</strong>za con un símbolo permitido);<br />

restantes (comprueba si los restantes son<br />

cartícteres permitidos).<br />

8.23. Escriba una funci6n sort que ord<strong>en</strong>e un conjunto<br />

<strong>de</strong> n cad<strong>en</strong>as <strong>en</strong> ord<strong>en</strong> alfaóético.<br />

8.24. Diseñar un programa que <strong>de</strong>termine la media<br />

<strong>de</strong>l número <strong>de</strong> horas trabajadas durante todos<br />

los días <strong>de</strong> la semana. para cada uno <strong>de</strong> los<br />

empleados <strong>de</strong> la Universidad.<br />

8.25. Escriba una función que ord<strong>en</strong>e <strong>en</strong> s<strong>en</strong>tido <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>te<br />

los n primeros elem<strong>en</strong>tos <strong>de</strong> un array<br />

<strong>de</strong> cad<strong>en</strong>as basado <strong>en</strong> las longitu<strong>de</strong>s <strong>de</strong> las cad<strong>en</strong>as.<br />

Por ejemplo, 'bibi' v<strong>en</strong>drá antes que 'Ana'.<br />

8.26. Se introduce una frase por teclado. Se <strong>de</strong>sea<br />

imprimir cada palabra <strong>de</strong> la frase <strong>en</strong> líneas<br />

difer<strong>en</strong>tes y consecutivas.<br />

837. Escribir un programa que <strong>de</strong>termine si una fra-<br />

número <strong>de</strong> veces que se <strong>en</strong>cu<strong>en</strong>tra la palabra<br />

<strong>en</strong> las n lineas.<br />

839. Se dice que una matriz ti<strong>en</strong>e un punto <strong>de</strong> silla<br />

si alguna posición <strong>de</strong> la matriz es el m<strong>en</strong>or<br />

valor <strong>de</strong> su fila, y a la<br />

columna. Escribir un<br />

como <strong>en</strong>trada una<br />

calcule la posició<br />

que existe).<br />

z <strong>de</strong> numeros reales, y<br />

punto <strong>de</strong> silla (si es<br />

trar el vector original y el vector con la distribución<br />

indicada.


Este capítulo examina <strong>estructura</strong>s, uniones, <strong>en</strong>umeraciones y tipos <strong>de</strong>finidos<br />

por el usuario que permite a un programador crear nuevos tipos <strong>de</strong> <strong>datos</strong>. La<br />

capacidad para crear nuevos tipos es una característica importante y pot<strong>en</strong>te <strong>de</strong><br />

C y libera a un programador <strong>de</strong> restringirse al uso <strong>de</strong> los tipos ofrecidos por el<br />

l<strong>en</strong>guaje. Una <strong>estructura</strong> conti<strong>en</strong>e múltiples variables, que pued<strong>en</strong> ser <strong>de</strong> tipos<br />

difer<strong>en</strong>tes. La <strong>estructura</strong> es importante para la creación <strong>de</strong> programm pot<strong>en</strong>tes,<br />

tales como bases <strong>de</strong> <strong>datos</strong> u otras aplicaciones que requieran gran<strong>de</strong>s<br />

cantida<strong>de</strong>s <strong>de</strong> <strong>datos</strong>. Por otra parte, se analizará el concepto <strong>de</strong> unión, otro tipo<br />

<strong>de</strong> dato no tan importante como las <strong>estructura</strong>s array y <strong>estructura</strong>, pero si<br />

necesarias <strong>en</strong> algunos casos.<br />

Un tipo <strong>de</strong> dato <strong>en</strong>umerado es una colección <strong>de</strong> miembros con nombre que<br />

ti<strong>en</strong><strong>en</strong> valores <strong>en</strong>teros equival<strong>en</strong>tes. Un type<strong>de</strong>f es <strong>de</strong> hecho no un nuevo tipo<br />

<strong>de</strong> dato sino simplem<strong>en</strong>te un sinónimo <strong>de</strong> un tipo exist<strong>en</strong>te.<br />

'<br />

I<br />

CONCEPTOS CLAVE<br />

Estructura.<br />

0 Estructuras anidadas.<br />

o Selector <strong>de</strong> campos.<br />

e struct.<br />

o sizeof.<br />

o union.<br />

o type<strong>de</strong>f.<br />

0 Operadores <strong>de</strong> bits,<br />

295


~~ ~<br />

296 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

9.1. ESTRUCTURAS<br />

Los arrays son <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> que conti<strong>en</strong><strong>en</strong> un número <strong>de</strong>terminado <strong>de</strong> elem<strong>en</strong>tos (su tamaño) y<br />

todos los elem<strong>en</strong>tos han <strong>de</strong> ser <strong>de</strong>l mismo tipo <strong>de</strong> <strong>datos</strong>; es una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> homogénea. Esta<br />

característica supone una gran limitación cuando se requier<strong>en</strong> grupos <strong>de</strong> elem<strong>en</strong>tos con tipos difer<strong>en</strong>tes<br />

<strong>de</strong> <strong>datos</strong> cada uno. Por ejemplo, si se dispone <strong>de</strong> una lista <strong>de</strong> temperaturas, es muy Útil un array; sin<br />

embargo, si se necesita una lista <strong>de</strong> información <strong>de</strong> cli<strong>en</strong>tes que cont<strong>en</strong>gan elem<strong>en</strong>tos tales como el<br />

nombre, la edad, la dirección, el número <strong>de</strong> la cu<strong>en</strong>ta, etc., los arrays no son a<strong>de</strong>cuados. La solución a<br />

este problema es utilizar un tipo <strong>de</strong> dato registro, <strong>en</strong> C llamado <strong>estructura</strong>.<br />

Los compon<strong>en</strong>tes individuales <strong>de</strong> una <strong>estructura</strong> se llaman miembros. Cada miembro (elem<strong>en</strong>to) <strong>de</strong><br />

una <strong>estructura</strong> pue<strong>de</strong> cont<strong>en</strong>er <strong>datos</strong> <strong>de</strong> un tipo difer<strong>en</strong>te <strong>de</strong> otros miembros. Por ejemplo, se pue<strong>de</strong><br />

utilizar una <strong>estructura</strong> para almac<strong>en</strong>ar difer<strong>en</strong>tes tipos <strong>de</strong> información sobre una persona, tal como<br />

nombre, estado civil, edad y fecha <strong>de</strong> nacimi<strong>en</strong>to. Cada uno <strong>de</strong> estos elem<strong>en</strong>tos se d<strong>en</strong>ominan nombre<br />

<strong>de</strong>l miembro.<br />

miembros,<br />

Una <strong>estructura</strong> pue<strong>de</strong> cont<strong>en</strong>er cualquier número <strong>de</strong> miembros, cada uno <strong>de</strong> los cuales ti<strong>en</strong>e un<br />

nombre único, d<strong>en</strong>ominado nombre <strong>de</strong>l miembro. Supongamos que se <strong>de</strong>sea almac<strong>en</strong>ar los <strong>datos</strong> <strong>de</strong> una<br />

colección <strong>de</strong> discos compactos (CD) <strong>de</strong> música. Estos <strong>datos</strong> pued<strong>en</strong> ser:<br />

o Título.<br />

O Artista.<br />

O Número <strong>de</strong> canciones.<br />

O Precio.<br />

O Fecha <strong>de</strong> compra.<br />

La <strong>estructura</strong> CD conti<strong>en</strong>e cinco miembros. Tras <strong>de</strong>cidir los miembros, se <strong>de</strong>be <strong>de</strong>cidir cuáles son<br />

los tipos <strong>de</strong> <strong>datos</strong> para utilizar por los miembros. Esta información se repres<strong>en</strong>ta <strong>en</strong> la tabla sigui<strong>en</strong>te:<br />

~~<br />

Nombre miembro<br />

Título<br />

Artista<br />

Número <strong>de</strong> canciones<br />

Precio<br />

Fecha <strong>de</strong> compra<br />

Tipo <strong>de</strong> dato<br />

Array <strong>de</strong> caracteres <strong>de</strong> tamaño 30.<br />

Array <strong>de</strong> caracteres <strong>de</strong> tamaño 25.<br />

Entero.<br />

Coma flotante.<br />

Array <strong>de</strong> caracteres <strong>de</strong> tamaño 8.<br />

La Figura 9.1 conti<strong>en</strong>e la <strong>estructura</strong> CD, mostrando gráficam<strong>en</strong>te los tipos <strong>de</strong> <strong>datos</strong> d<strong>en</strong>tro <strong>de</strong> la<br />

<strong>estructura</strong>. Obsérvese que cada miembro es un tipo <strong>de</strong> dato difer<strong>en</strong>te.<br />

Título<br />

Artista<br />

Número <strong>de</strong> canciones<br />

Precio<br />

Fecha <strong>de</strong> compra<br />

Ay, ay, ay, cómo se aleja el sol.<br />

NO me pises la sandalias.<br />

10<br />

2222.25<br />

8-10-1999<br />

I<br />

Figura 9.1. Repres<strong>en</strong>tación gráfica <strong>de</strong> una <strong>estructura</strong> CD.


IC-<br />

Estructuras y uniones 297<br />

9.1.1. Declaración <strong>de</strong> una <strong>estructura</strong><br />

Una <strong>estructura</strong> es un tipo <strong>de</strong> dato <strong>de</strong>finido por el usuario, que se <strong>de</strong>be <strong>de</strong>clarar antes <strong>de</strong> que se pueda<br />

utilizar. El formato <strong>de</strong> la <strong>de</strong>claración es:<br />

struct <br />

i<br />

<br />

<br />

...<br />

<br />

1;<br />

La <strong>de</strong>claración <strong>de</strong> la <strong>estructura</strong> CD es<br />

struct coleccion-CD<br />

i<br />

char titulo[30] ;<br />

char artista[25] ;<br />

int num-canciones;<br />

float precio;<br />

char f echa-compra [ 8 1 ;<br />

i<br />

Ejemplo<br />

struct complejo<br />

{<br />

float parte-real, parte-imaginaria;<br />

1;<br />

En este otro ejemplo se <strong>de</strong>clara el tipo <strong>estructura</strong> v<strong>en</strong>ta ;<br />

struct v<strong>en</strong>ta<br />

i<br />

char ve<strong>de</strong>dor [ 3 O I ;<br />

unsigned int codigo;<br />

int inids-articulos;<br />

float precio-unit;<br />

1;<br />

ti<br />

,<br />

9.1.2. Definición <strong>de</strong> variables <strong>de</strong> <strong>estructura</strong>s<br />

AI igual que a los tipos <strong>de</strong> <strong>datos</strong> <strong>en</strong>umerados, a una <strong>estructura</strong> se acce<strong>de</strong> utilizando una variable o 1<br />

variables que se <strong>de</strong>b<strong>en</strong> <strong>de</strong>finir <strong>de</strong>spués <strong>de</strong> la <strong>de</strong>claración <strong>de</strong> la <strong>estructura</strong>. Del mismo modo que suce<strong>de</strong><br />

<strong>en</strong> otras situaciones, <strong>en</strong> C exist<strong>en</strong> dos conceptos similares a consi<strong>de</strong>rar, <strong>de</strong>claración y <strong>de</strong>jnición. La<br />

difer<strong>en</strong>cia técnica es la sigui<strong>en</strong>te, una <strong>de</strong>claración especifica simplem<strong>en</strong>te el nombre y el formato <strong>de</strong> la<br />

<strong>estructura</strong> <strong>de</strong> <strong>datos</strong>, pero no reserva almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> memoria; la <strong>de</strong>claración especifica un nuevo<br />

tipo <strong>de</strong> dato: struct . Por consigui<strong>en</strong>te, cada <strong>de</strong>finición <strong>de</strong> variable para una<br />

<strong>estructura</strong> dada crea un área <strong>en</strong> memoria <strong>en</strong> don<strong>de</strong> los <strong>datos</strong> se almac<strong>en</strong>an <strong>de</strong> acuerdo al formato<br />

<strong>estructura</strong>do <strong>de</strong>clarado.<br />

Las variables <strong>de</strong> <strong>estructura</strong>s se pued<strong>en</strong> <strong>de</strong>finir <strong>de</strong> dos formas: 1) listándolas inmediatam<strong>en</strong>te <strong>de</strong>spués<br />

<strong>de</strong> la llave <strong>de</strong> cierre <strong>de</strong> la <strong>de</strong>claración <strong>de</strong> la <strong>estructura</strong>, o 2) listando el tipo <strong>de</strong> la <strong>estructura</strong> creado seguida<br />

por las variables correspondi<strong>en</strong>tes <strong>en</strong> cualquier lugar <strong>de</strong>l programa antes <strong>de</strong> utilizarlas. La <strong>de</strong>finición y<br />

<strong>de</strong>claración <strong>de</strong> la <strong>estructura</strong> colecciones-CD se pue<strong>de</strong> hacer por cualquiera <strong>de</strong> los dos métodos:<br />

I


F<br />

298 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1. struct colecciones-CD<br />

i<br />

char titulo[301 ;<br />

char artistar251 ;<br />

int num-canciones;<br />

float precio;<br />

char fecha_compra[8];<br />

} cdl, cd2, cd3;<br />

2. struct colecciones-CD cdl, cd2, cd3;<br />

Otros ejemplos <strong>de</strong> <strong>de</strong>finición/<strong>de</strong>claración<br />

Considérese un programa que gestione libros y procese los sigui<strong>en</strong>tes <strong>datos</strong>: título <strong>de</strong>l libro, nombre <strong>de</strong>l<br />

autor, <strong>editorial</strong> y año <strong>de</strong> publicación. Una <strong>estructura</strong> info-libro podría ser:<br />

struct info-libro<br />

{<br />

char titulo[60] ;<br />

char autor [ 3 O I ;<br />

char <strong>editorial</strong> [30] ;<br />

int anyo;<br />

i;<br />

La <strong>de</strong>finición <strong>de</strong> la <strong>estructura</strong> se pue<strong>de</strong> hacer así:<br />

1.struct info-libro<br />

t<br />

char titulo[601;<br />

char autor [301;<br />

char <strong>editorial</strong> [301;<br />

int anyo ;<br />

} librol, llbro2, libro3;<br />

2.struct info-libro librol, libro2, libro3;<br />

Ahora se nos plantea una aplicación <strong>de</strong> control <strong>de</strong> los participantes <strong>en</strong> una carrera popular, cada<br />

participante se repres<strong>en</strong>ta por los <strong>datos</strong>: nombre, edad, sexo, categoría, club y tiempo. El registro se<br />

repres<strong>en</strong>ta con la <strong>estructura</strong> corredor :<br />

struct corredor<br />

i<br />

char nombre [ 40 I ;<br />

int edad;<br />

char sexo;<br />

char categoria[201 ;<br />

char clubL261 ;<br />

float tiempo;<br />

1;<br />

La <strong>de</strong>finición <strong>de</strong> variables <strong>estructura</strong> se pue<strong>de</strong> hacer así:<br />

struct corredor vl, sl, cl;<br />

9.1.3. Uso <strong>de</strong> <strong>estructura</strong>s <strong>en</strong> asignaciones<br />

Como una <strong>estructura</strong> es un tipo <strong>de</strong> dato similar a un int o un char, se pue<strong>de</strong> asignar una <strong>estructura</strong> a<br />

otra. Por ejemplo, se pue<strong>de</strong> hacer que libro3, libro4 y libro5 t<strong>en</strong>gan los mismos valores <strong>en</strong> sus<br />

miembros que 1 ibrol. Por consigui<strong>en</strong>te, seda necesario realizar las sigui<strong>en</strong>tes s<strong>en</strong>t<strong>en</strong>cias:


I-<br />

7<br />

Estructuras y uniones 299<br />

libro3 = librol;<br />

libro4 = librol;<br />

libro5 = librol;<br />

De modo alternativo se pue<strong>de</strong> escribir<br />

libro4 = libro5 = libro6 = librol;<br />

9.1.4. Inicialización <strong>de</strong> una <strong>de</strong>claración <strong>de</strong> <strong>estructura</strong>s<br />

Se pue<strong>de</strong> inicializar una <strong>estructura</strong> <strong>de</strong> dos formas. Se pue<strong>de</strong> inicializar una <strong>estructura</strong> d<strong>en</strong>tro <strong>de</strong> la sección<br />

<strong>de</strong> código <strong>de</strong> su programa, o bi<strong>en</strong> se pue<strong>de</strong> inicializar la <strong>estructura</strong> como parte <strong>de</strong> la <strong>de</strong>finición. Cuando<br />

se inicializa una <strong>estructura</strong> como parte <strong>de</strong> la <strong>de</strong>finición, se especifican los valores iniciales, <strong>en</strong>tre llaves,<br />

<strong>de</strong>spués <strong>de</strong> la <strong>de</strong>finición <strong>de</strong> variables <strong>estructura</strong>. El formato g<strong>en</strong>eral <strong>en</strong> este caso:<br />

struct =<br />

{ valor miembro,<br />

valor miembro ,<br />

...<br />

val or miembro<br />

I ;<br />

struct info-libro<br />

t<br />

char titulo[bOl ;<br />

char auto[30];<br />

char <strong>editorial</strong> [30] ;<br />

int anyo;<br />

} librol = {"Maravilla <strong>de</strong>l saber ",l'Lucas Garcia", "McGraw-Hill", 1999);<br />

Otro ejemplo podría ser:<br />

struct coleccion-CD<br />

i<br />

char titulo[30] ;<br />

char artista[25];<br />

int num-canciones;<br />

float precio;<br />

char fecha-compra[8];<br />

} cdl = {<br />

"El humo nubla tus ojos",<br />

"col Porter",<br />

15,<br />

2545,<br />

" O 2 / 6 / 9 9 I'<br />

1;<br />

Otro ejemplo con la <strong>estructura</strong> corredor:<br />

struct corredor vl = i<br />

"Salvador Rapido" ,<br />

29,<br />

'V' ,<br />

I' s<strong>en</strong> i or 'I ,<br />

'In<strong>de</strong>p<strong>en</strong>di<strong>en</strong>te',<br />

0.0<br />

1;


300 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

9.1.5. El tamaño <strong>de</strong> una <strong>estructura</strong><br />

El operador sizeof se aplica sobre un tipo <strong>de</strong> <strong>datos</strong>, o bi<strong>en</strong> sobre una variable. Se pue<strong>de</strong> aplicar para<br />

<strong>de</strong>terminar el tamaño que ocupa <strong>en</strong> memoria una <strong>estructura</strong>. El sigui<strong>en</strong>te programa ilustra el uso <strong>de</strong>l<br />

operador s izeof para <strong>de</strong>terminar el tamaño <strong>de</strong> una <strong>estructura</strong>:<br />

#inclu<strong>de</strong> <br />

/* <strong>de</strong>clarar una <strong>estructura</strong> Persona */<br />

struct persona<br />

i<br />

char nombre [30 I ;<br />

int edad;<br />

float altura;<br />

float peso;<br />

I;<br />

void main( )<br />

i<br />

struct persona mar;<br />

printf ("Sizeof(persona): %d \n", sizeof (mar ) ;<br />

1<br />

Al ejecutar el programa se produce la salida:<br />

Sizeof (persona) : 40<br />

El resultado se obti<strong>en</strong>e <strong>de</strong>terminando el número <strong>de</strong> bytes que ocupa la <strong>estructura</strong><br />

Persona Miembros dato Tamaño (bytes)<br />

nombre [ 301 char(1) 30<br />

edad int (2) 2<br />

altura float (4) 4<br />

peso float ( 4) 4<br />

Total 40<br />

9.2. ACCESO A ESTRUCTURAS<br />

Cuando se acce<strong>de</strong> a una <strong>estructura</strong>, o bi<strong>en</strong> se almac<strong>en</strong>a información <strong>en</strong> la <strong>estructura</strong> o se recupera la<br />

información <strong>de</strong> la <strong>estructura</strong>. Se pue<strong>de</strong> acce<strong>de</strong>r a los miembros <strong>de</strong> una <strong>estructura</strong> <strong>de</strong> una <strong>de</strong> estas dos<br />

formas: 1) utilizando el operador punto (.), o bi<strong>en</strong> 2) utilizando el operador puntero ->.<br />

9.2.1. Almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> información <strong>en</strong> <strong>estructura</strong>s<br />

Se pue<strong>de</strong> almac<strong>en</strong>ar información <strong>en</strong> una <strong>estructura</strong> mediante inicialización, asignación directa o lectura<br />

<strong>de</strong>l teclado. El proceso <strong>de</strong> inicialización ya se ha examinado, veamos ahora la asignación directa y la<br />

lectura <strong>de</strong>l teclado.<br />

Acceso a una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> mediante el operador punto<br />

La asignación <strong>de</strong> <strong>datos</strong> a los miembros <strong>de</strong> una variable <strong>estructura</strong> se hace mediante el operador punto.<br />

La sintaxis <strong>en</strong> C es:<br />

. = <strong>datos</strong>;


m<br />

Estructuras y uniones 301<br />

Algunos ejemplos:<br />

strcpy(cd1. titulo,"Granada") ;<br />

cdl.precio = 3450.75;<br />

cdl.num-canciones = 7;<br />

El operador punto proporciona el camino directo al miembro correspondi<strong>en</strong>te. Los <strong>datos</strong> que se<br />

almac<strong>en</strong>an <strong>en</strong> un miembro dado <strong>de</strong>b<strong>en</strong> ser <strong>de</strong>l mismo tipo que el tipo <strong>de</strong>clarado para ese miembro. En<br />

el sigui<strong>en</strong>te ejemplo se lee <strong>de</strong>l teclado los <strong>datos</strong> <strong>de</strong> una variable <strong>estructura</strong> corredor:<br />

struct corredor cr;<br />

printf ("Nombre: ");<br />

gets(cr.nombre);<br />

printf ("edad: ") ;<br />

scanf ("%d", &cr. edad) ;<br />

printf ("Sexo: ") ;<br />

scanf ("%c", &cr. sexo) ;<br />

printf ("Club: ") ;<br />

gets(cr.club) ;<br />

if (cr.edad


302 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Nota<br />

Previam<strong>en</strong>te habría que crear espacio <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> memoria; por ejemplo, con la función<br />

malloc (1.<br />

9.2.2. Lectura <strong>de</strong> información <strong>de</strong> una <strong>estructura</strong><br />

Si ahora se <strong>de</strong>sea introducir la información <strong>en</strong> la <strong>estructura</strong> basta con acce<strong>de</strong>r a los miembros <strong>de</strong> la<br />

<strong>estructura</strong> con el operador punto o flecha (puntero). Se pue<strong>de</strong> introducir la información <strong>de</strong>s<strong>de</strong> el teclado<br />

o <strong>de</strong>s<strong>de</strong> un archivo, o asignar valores calculados.<br />

Así, si z es una variable <strong>de</strong> tipo <strong>estructura</strong> complejo, se lee parte real, parte imaginaria y se calcula<br />

el módulo:<br />

s t ruc t comple j o<br />

i<br />

float pr;<br />

float pi;<br />

float modulo;<br />

I;<br />

struct complejo z;<br />

printf ("\nParte real: " ) ;<br />

scanf ("%í",&z.pr);<br />

printf ("\nParte imaginaria: " ) ;<br />

scanf ("%f",&z.pi);<br />

/* calculo <strong>de</strong>l módulo */<br />

z.modulo = sqrt(z.pr*z.pr + z.pi*z.pi);<br />

9.2.3. Recuperación <strong>de</strong> información <strong>de</strong> una <strong>estructura</strong><br />

Se recupera información <strong>de</strong> una <strong>estructura</strong> utilizando el operador <strong>de</strong> asignación o una s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> salida<br />

(printf ( ) , puts ( ) , ...). Igual que antes, se pue<strong>de</strong> emplear el operador punto o el operador flecha<br />

(puntero). El formato g<strong>en</strong>eral toma uno <strong>de</strong> estos formatos:<br />

1. =<br />

.;<br />

o bi<strong>en</strong><br />

=<br />

-> ;<br />

2 . para salida:<br />

printf(" ",.);<br />

o bi<strong>en</strong><br />

printf(" ",-> );<br />

Algunos ejemplos <strong>de</strong>l uso <strong>de</strong> la <strong>estructura</strong> complejo:<br />

float x,y;<br />

struct complejo z;<br />

struct complejo *pz;


pz = &z;<br />

x = z.pr;<br />

y = z.pi;<br />

...<br />

printf("\nNÚmero complejo (%.lf,%.lf), módulo: %.2f",<br />

pz->pr,pz->pi,pz->modulo) ;<br />

Estructuras y uniones 303<br />

9.3. ESTRUCTURAS ANIDADAS<br />

Una <strong>estructura</strong> pue<strong>de</strong> cont<strong>en</strong>er otras <strong>estructura</strong>s llamadas <strong>estructura</strong>s anidadas. Las <strong>estructura</strong>s anidadas<br />

ahorran tiempo <strong>en</strong> la escritura <strong>de</strong> programas que utilizan <strong>estructura</strong>s similares. Se han <strong>de</strong> <strong>de</strong>finir los<br />

miembros comunes sólo una vez <strong>en</strong> su propia <strong>estructura</strong> y a continuación utilizar esa <strong>estructura</strong> como<br />

un miembro <strong>de</strong> otra <strong>estructura</strong>. Consi<strong>de</strong>remos las sigui<strong>en</strong>tes dos <strong>de</strong>finiciones <strong>de</strong> <strong>estructura</strong>s:<br />

struct empleado<br />

{<br />

char nombre-emp [ 3 O I ;<br />

char direccion [25] ;<br />

char ciudad [ 2 O I ;<br />

char provincia [2O] ;<br />

long int cod-postal;<br />

double salario;<br />

i;<br />

struct cli<strong>en</strong>tes<br />

};<br />

char nombre_cli<strong>en</strong>te[30];<br />

char direccion [ 2 5 I ;<br />

char ciudad[20];<br />

char provincia[201;<br />

long int cod-postal;<br />

double saldo;<br />

Estas <strong>estructura</strong>s conti<strong>en</strong><strong>en</strong> muchos <strong>datos</strong> difer<strong>en</strong>tes, aunque hay <strong>datos</strong> que están solapados. Así, se<br />

podría disponer <strong>de</strong> una <strong>estructura</strong>, inf o-dir, que contuviera los miembros comunes.<br />

struct info-dir<br />

char direccion[25] ;<br />

char ciudadL201;<br />

char provincia [2O] ;<br />

long int cod-postal;<br />

1;<br />

Esta <strong>estructura</strong> se pue<strong>de</strong> utilizar como un miembro <strong>de</strong> las otras <strong>estructura</strong>s, es <strong>de</strong>cir, anidarse.<br />

struct empleado<br />

{<br />

char nombre-emp [ 3 O ] ;<br />

struct info-dir direccion-emp;<br />

double salario;<br />

I;<br />

struct cli<strong>en</strong>tes<br />

i


304 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

char nombre_cli<strong>en</strong>te[30];<br />

struct info-dir direccion-cli<strong>en</strong>;<br />

double saldo;<br />

I;<br />

Gráficam<strong>en</strong>te se podrían mostrar <strong>estructura</strong>s anidadas <strong>en</strong> la Figura 9.2.<br />

empleado:<br />

nombre-emp<br />

in f o-di r<br />

salario<br />

I<br />

direccion<br />

ciudad<br />

provincia<br />

cod-postal<br />

cli<strong>en</strong>te:<br />

nombre-cli<strong>en</strong>te<br />

I<br />

direccion<br />

i n f o-di r ciudad<br />

provincia<br />

c od-po s t a 1<br />

saldo<br />

Figura 9.2. Estructuras anidadas.<br />

9.3.1. Ejemplo <strong>de</strong> <strong>estructura</strong>s anidadas<br />

Se <strong>de</strong>sea diseñar una <strong>estructura</strong> que cont<strong>en</strong>ga información <strong>de</strong> operaciones financieras. Esta <strong>estructura</strong><br />

<strong>de</strong>be constar <strong>de</strong> un número <strong>de</strong> cu<strong>en</strong>ta, una cantidad <strong>de</strong> dinero, el tipo <strong>de</strong> operación (<strong>de</strong>pósito=O, retirada<br />

<strong>de</strong> fondos=l, puesta al día=2 o estado <strong>de</strong> la cu<strong>en</strong>ta=3) y la fecha y hora <strong>en</strong> que la operación se ha<br />

realizado. A fin <strong>de</strong> realizar el acceso correcto a los campos día, mes y año, así como el tiempo (la hora<br />

y minutos) <strong>en</strong> que se efectuó la operación, se <strong>de</strong>fine una <strong>estructura</strong> fecha y una <strong>estructura</strong> tiempo. La<br />

<strong>estructura</strong> registro-operac ion ti<strong>en</strong>e como miembros una variable (un campo) <strong>de</strong> tipo fecha, otra<br />

variable <strong>de</strong>l tipo tiempo y otras variables para repres<strong>en</strong>tar los otros campos. La repres<strong>en</strong>tación <strong>de</strong>l tipo<br />

<strong>de</strong> operación se hace con una variable <strong>en</strong>tera, aunque el tipo apropiado es un tipo <strong>en</strong>umerado (<strong>de</strong>scrito<br />

<strong>en</strong> sigui<strong>en</strong>tes apartados). A continuación se <strong>de</strong>clara estos tipos, se escribe una función que lee una<br />

operación financiera y <strong>de</strong>vuelve la operación leída. La fecha y hora es captada <strong>de</strong>l sistema.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

struct registro-operation <strong>en</strong>trada();<br />

struct fecha<br />

{<br />

unsigned int mes, dia, anyo;<br />

I;<br />

struct tiempo<br />

{<br />

unsigned int horas, minutos;<br />

1;<br />

struct registro-operacion<br />

I<br />

long numero-cu<strong>en</strong>ta;<br />

float cantidad;<br />

int tipo-operacion;<br />

struct fecha f;<br />

struct tiempo t;<br />

1;<br />

int main()<br />

{<br />

struct registro-operacion w;


Estructuras y uniones 305<br />

1<br />

w = <strong>en</strong>trada() ;<br />

printf ("\n\n OperaciCn realizada\n ") ;<br />

print f ( 'I\ t % 1 d\ n" , w . numero-cu<strong>en</strong> t a ) ;<br />

printf("\t%d-%d-%d\n",w.f.dia,w.f.mes,w.f.anyo) ;<br />

printf ( "\ t%d: %d\n", w. t. horas, w. t. minutos ) ;<br />

return O;<br />

struct registro-operacion <strong>en</strong>trada0<br />

I<br />

struct time t;<br />

struct date d;<br />

struct registro-operacion una;<br />

printf ("\nNÚmero <strong>de</strong> cu<strong>en</strong>ta: ");<br />

scanf ("%ld", &una.numero-cu<strong>en</strong>ta);<br />

puts ("\n\tTipo <strong>de</strong> operación") ;<br />

puts("Deposito(0)") ;<br />

puts ("Retirada <strong>de</strong> fondos (1)") ;<br />

puts('Puesta al dia(2)");<br />

puts ("Estado <strong>de</strong> la cu<strong>en</strong>ta ( 3 ) I' ) ;<br />

scanf ("%d", &una. t ipo-operacion) ;<br />

1<br />

/* Fecha y tiempo <strong>de</strong>l sistema */<br />

gettime (&t) ;<br />

una.t.horas = t.ti-hour;<br />

una.t.minutos = t.timin;<br />

getdate (&d) ;<br />

una.f.anyo = d.dajear;<br />

una.f.mes = d.darnon;<br />

una.f.dia = d.da-day;<br />

return una;<br />

Ejercicio 9.1<br />

Se <strong>de</strong>sea registrar una <strong>estructura</strong> PersonaEmpl eado que cont<strong>en</strong>ga como miembros los <strong>datos</strong> <strong>de</strong> una<br />

persona empleado que a su vez t<strong>en</strong>ga los <strong>datos</strong> <strong>de</strong> la fecha <strong>de</strong> nacimi<strong>en</strong>to. En un programa se muestra<br />

el uso <strong>de</strong> la <strong>estructura</strong>, se <strong>de</strong>fine una función para dar <strong>en</strong>trada a los <strong>datos</strong> <strong>de</strong> la <strong>estructura</strong> y otra finción<br />

para dar salida a los <strong>datos</strong> <strong>de</strong> una <strong>estructura</strong> persona. A la función <strong>de</strong> <strong>en</strong>trada se transmite por<br />

dirección (&p) la variable <strong>estructura</strong>, por lo que el argum<strong>en</strong>to correspondi<strong>en</strong>te ti<strong>en</strong>e que ser un<br />

I puntero( *p) y el acceso a los campos se hace con el selector -><br />

I persona-Empleado I<br />

#inclu<strong>de</strong> <br />

struct fecha<br />

I<br />

1;<br />

I<br />

fecha I<br />

unsigned int dia, mes, anyo;


[i-<br />

-<br />

- 7!<br />

306 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

struct persona i<br />

char nombre [ 2 O 1 ;<br />

unsigned int edad;<br />

int altura;<br />

int peso;<br />

struct fecha fec;<br />

I;<br />

struct persona-empleado<br />

i<br />

struct persona unapersona;<br />

unsigned int salario;<br />

unsigned int horas_por-semana;<br />

I;<br />

/* prototipos <strong>de</strong> funciones */<br />

void <strong>en</strong>trada(struct persona-empleado *p);<br />

void muestra(struct persona-empleado up);<br />

void main()<br />

i<br />

/* <strong>de</strong>fine una variable persona-empleado */<br />

struct persona-empleado p;<br />

/* llamada a <strong>en</strong>trada() transmiti<strong>en</strong>do la direccion */<br />

<strong>en</strong>trada(&p);<br />

/* salida <strong>de</strong> los <strong>datos</strong> almac<strong>en</strong>ados */<br />

muestra(p) ;<br />

i<br />

void <strong>en</strong>trada(struct persona-empleado *p)<br />

{<br />

printf ("\nIntroduzca su nombre: ") ;<br />

gets(p->unapersona.nombre);<br />

printf ("introduzca su edad: "1;<br />

scanf ("%d", &p->unapersona. edad) ;<br />

printf ("Introduzca su altura: ");<br />

scanf ("%d",&p->unapersona. altura) ;<br />

printf ('Introduzca su peso: ") ;<br />

scanf ("%d", &p->unapersona.peso);<br />

printf("1ntroduzca su fecha <strong>de</strong> nacimi<strong>en</strong>to: ");<br />

scanf ("%d %d %d",&p->unapersona. fec. dia,<br />

&p->unapersona.fec.mes,<br />

&p->unapersona.fec.anyo);<br />

printf ("Introduzca su salario:") ;<br />

scanf ("%d", &p->salario);<br />

printf ("introduzca numero <strong>de</strong> horas:") ;<br />

scanf ("%d",&p->horas-por-semana) ;<br />

1<br />

void muestra(struct persona-empleado up)<br />

i<br />

puts ("\n\n\<br />

tDatos <strong>de</strong> un empleado" ) ;<br />

puts ("\n\n\t ");<br />

print€("Nombre: %s \n",up.unapersona.nombre);<br />

printf("Edad: %d \n",up.unapersona.edad);<br />

printf("fecha <strong>de</strong> nacimi<strong>en</strong>to: %d-%d-&d\n",


P- '.<br />

Estructuras y uniones 307<br />

1<br />

up.unapersona.fec.dia,<br />

up.unapersona.tec.mes,<br />

up.unapersona.fec.anyo);<br />

printf("A1tura: %d \n",up.unapersona.aIturd);<br />

printf("Peso: %d \n",up.unapersona.peso);<br />

printf("Numero <strong>de</strong> horas: %d \n",up.horas-por-semana);<br />

El acceso a miembros dato <strong>de</strong> <strong>estructura</strong>s anidadas requiere el uso <strong>de</strong> múitiples operadores punto.<br />

Ejemplo: acceso ai áía <strong>de</strong>l mes <strong>de</strong> la fecha <strong>de</strong> nacimi<strong>en</strong>to <strong>de</strong> un e<br />

up.unapercona.Eec.dka<br />

Las <strong>estructura</strong>s se pued<strong>en</strong> anidar a cualquier grado. También es posible inicializar <strong>estructura</strong>s<br />

anidadas <strong>en</strong> la <strong>de</strong>finición. El sigui<strong>en</strong>te ejemplo inicializa una variable Luis <strong>de</strong> tipo struct persona.<br />

struct persona Luis { "Luis" , 25, 1940, 40, (12, 1, 701);<br />

9.4. ARRAYS DE ESTRUCTURAS<br />

Se pue<strong>de</strong> crear un array <strong>de</strong> <strong>estructura</strong>s tal como se crea un array <strong>de</strong> otros tipos. Los arrays <strong>de</strong> <strong>estructura</strong>s<br />

son idóneos para almac<strong>en</strong>ar un archivo completo <strong>de</strong> empleados, un archivo <strong>de</strong> inv<strong>en</strong>tario, o cualquier<br />

otro conjunto <strong>de</strong> <strong>datos</strong> que se adapte a un formato <strong>de</strong> <strong>estructura</strong>. Mi<strong>en</strong>tras que los arrays proporcionan<br />

un medio práctico <strong>de</strong> almac<strong>en</strong>ar diversos valores <strong>de</strong>l mismo tipo, los arrays <strong>de</strong> <strong>estructura</strong>s le permit<strong>en</strong><br />

almac<strong>en</strong>ar juntos diversos valores <strong>de</strong> difer<strong>en</strong>tes tipos, agrupados como <strong>estructura</strong>s.<br />

Muchos programadores <strong>de</strong> C utilizan arrays <strong>de</strong> <strong>estructura</strong>s como un método para almac<strong>en</strong>ar <strong>datos</strong><br />

<strong>en</strong> un archivo <strong>de</strong> disco. Se pued<strong>en</strong> introducir y calcular sus <strong>datos</strong> <strong>de</strong> disco <strong>en</strong> arrays <strong>de</strong> <strong>estructura</strong>s y a<br />

continuación almac<strong>en</strong>ar esas <strong>estructura</strong>s <strong>en</strong> memoria. Los arrays <strong>de</strong> <strong>estructura</strong>s proporcionan también un<br />

medio <strong>de</strong> guardar <strong>datos</strong> que se le<strong>en</strong> <strong>de</strong>l disco.<br />

La <strong>de</strong>claración <strong>de</strong> un array <strong>de</strong> <strong>estructura</strong>s info-libro se pue<strong>de</strong> hacer <strong>de</strong> un modo similar a<br />

cualquier array, es <strong>de</strong>cir,<br />

struct info-libro libros[100];<br />

asigna un array <strong>de</strong> 100 elem<strong>en</strong>tos d<strong>en</strong>ominado libros. Para acce<strong>de</strong>r a los miembros <strong>de</strong> cada uno <strong>de</strong> los<br />

elem<strong>en</strong>tos <strong>estructura</strong> se utiliza una notación <strong>de</strong> array. Para inicializar el primer elem<strong>en</strong>to <strong>de</strong> 1 ibros, por<br />

ejemplo, su código <strong>de</strong>be hacer refer<strong>en</strong>cia a los miembros <strong>de</strong> libros [ O I <strong>de</strong> la forma sigui<strong>en</strong>te:<br />

strcpy(libros[O] .titulo, "C++ a su alcance");<br />

strcpy(1ibros [O] .autor, "Luis Joyanes") ;<br />

strcpy(1ibros [O] .<strong>editorial</strong>, "McGrdw-Hill') ;<br />

libros[O] .anyo = 1999;<br />

También pue<strong>de</strong> inicializarse un array <strong>de</strong> <strong>estructura</strong>s <strong>en</strong> el punto <strong>de</strong> la <strong>de</strong>claración <strong>en</strong>cerrando la lista<br />

<strong>de</strong> inicializadores <strong>en</strong>tre llaves, ( }. Por ejemplo,<br />

struct info-libro libros[3] = { "C++ a su alcance", "Luis Joyanes",<br />

'McGraw-Hill", 1999, "Estructura <strong>de</strong> <strong>datos</strong>", "Luis Joyanes",<br />

"McGraw-Hill", 1999, "Problemas <strong>en</strong> Pascal", "Angel Hermoso",<br />

"McGraw-Hill", 19971 ;<br />

En el sigui<strong>en</strong>te ejemplo se <strong>de</strong>clara una <strong>estructura</strong> que repres<strong>en</strong>ta a un número racional, un array <strong>de</strong><br />

números racionales es inicializado con valores al azar.


308 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

struct racional<br />

{<br />

int N,<br />

int D;<br />

1;<br />

struct racinal rs[4] = { 1,2, 2,3, -4,7, O,l};<br />

9.4.1. Arrays como miembros<br />

Los miembros <strong>de</strong> las <strong>estructura</strong>s pue<strong>de</strong> ser asimismo arrays. En este caso, será preciso extremar las<br />

precauciones cuando se acce<strong>de</strong> a los elem<strong>en</strong>tos individuales <strong>de</strong>l array.<br />

Considérese la sigui<strong>en</strong>te <strong>de</strong>finición <strong>de</strong> <strong>estructura</strong>. Esta s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong>clara un array <strong>de</strong> 1 O0 <strong>estructura</strong>s,<br />

cada <strong>estructura</strong> conti<strong>en</strong>e información <strong>de</strong> <strong>datos</strong> <strong>de</strong> empleados <strong>de</strong> una compañía.<br />

struct nomina<br />

I<br />

char nombre [ 3 O 1 ;<br />

int <strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes;<br />

char <strong>de</strong>partam<strong>en</strong>to [ 10 1 ;<br />

float horas_dias[71; /* array <strong>de</strong> tipo float */<br />

float salario;<br />

} empleado [lo01 ; /* Un array <strong>de</strong> 100 empleados */<br />

Ejemplo 9.1<br />

Una librería <strong>de</strong>sea cafalogar su inv<strong>en</strong>tario <strong>de</strong> libros. El sigui<strong>en</strong>te programa crea un array <strong>de</strong> 100<br />

<strong>estructura</strong>s, don<strong>de</strong> cada <strong>estructura</strong> conti<strong>en</strong>e diversos tipos <strong>de</strong> variables, incluy<strong>en</strong>do arrays.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

struct inv<strong>en</strong>tario<br />

i<br />

char titulo [25] ;<br />

char €echasub [ 2 O 1 ;<br />

char autor [301 ;<br />

int num;<br />

int pedido;<br />

€loat precio-v<strong>en</strong>ta;<br />

I;<br />

int main ( )<br />

{<br />

struct inv<strong>en</strong>tario libro[1001;<br />

int total = O;<br />

char resp, b[211 ;<br />

do {<br />

printf ("Total libros %d \n",(total+l))<br />

printf ("¿Cuál es el título?: "1;<br />

gets(libro[totall .titulo);<br />

printf("iCuá1 es la fecha <strong>de</strong> publicación?: " );<br />

gets ( libro [total I . f echagub) ;<br />

;


-<br />

Estructuras y uniones<br />

309<br />

printf ("¿Quién es el autor?") ;<br />

gets(libro[total] .autor) ;<br />

printf ("¿Cuántos libros exist<strong>en</strong>?: ") ;<br />

scanf ("%d", &libro[total I . num) ;<br />

printf("¿Cuántos ejemplares exist<strong>en</strong> pedidos?: " 1;<br />

scanf ('%d",&libro[total] .pedido) ;<br />

printf("¿Cuál es el precio <strong>de</strong> v<strong>en</strong>ta?: ");<br />

gets (b);<br />

libro[total].precio-v<strong>en</strong>ta = atof(b); /* conversión a real */<br />

f f lush (stdin);<br />

printf ("\n¿Hay más libros? (S/N)");<br />

scanf ("%e", &resp);<br />

f f lush (stdin);<br />

resp = toupper(resp); /* convierte a mayúsculas */<br />

if (resp == 'S')<br />

i<br />

total++;<br />

continue ;<br />

1<br />

} while (resp == 'S');<br />

return O;<br />

9.5. UTILIZACIÓN DE ESTRUCTURAS COMO PARÁMETROS<br />

C permite pasar <strong>estructura</strong>s a funciones, bi<strong>en</strong> por valor o bi<strong>en</strong> por refer<strong>en</strong>cia utilizando el operador &.<br />

Si la <strong>estructura</strong> es gran<strong>de</strong>, el tiempo necesario para copiar un parámetro struct a la pila pue<strong>de</strong> ser<br />

prohibitivo. En tales casos, se <strong>de</strong>be consi<strong>de</strong>rar el método <strong>de</strong> pasar la dirección <strong>de</strong> la <strong>estructura</strong>.<br />

El listado sigui<strong>en</strong>te muestra un programa que pasa la dirección <strong>de</strong> una <strong>estructura</strong> a una función para<br />

<strong>en</strong>trada <strong>de</strong> <strong>datos</strong>. La misma variable <strong>estructura</strong> la pasa por valor a otra función para salida <strong>de</strong> los campos.<br />

#inclu<strong>de</strong> <br />

/* Define el tipo <strong>estructura</strong> infogersona */<br />

struct info-persona {<br />

char nombre [ 2 O I ;<br />

char calle[30];<br />

char ciudadL251;<br />

char provincia[25] ;<br />

char codiqopostal[ól;<br />

1;<br />

/* prototipos <strong>de</strong> funciones */<br />

void <strong>en</strong>trad_pna(struct infogersona* pp);<br />

void ver-info(struct infogersona p);<br />

void main (void)<br />

{<br />

struct info-persona reg-dat;<br />

/* Pasa por refer<strong>en</strong>cia la variable */<br />

<strong>en</strong>tradgna(&reg-dat);<br />

/* Pasa por valor */<br />

ver-info(reg-dat);


310 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

}<br />

printt( "\nPulsa cualquier carácter para continuar\n");<br />

getchar ( ) ;<br />

void <strong>en</strong>trad_pna(struct infogersona" pp)<br />

i<br />

puts("\n Entrada <strong>de</strong> los <strong>datos</strong> <strong>de</strong> una persona\n");<br />

1<br />

/* Para ace<strong>de</strong>r a los campos se utiliza el selector -> */<br />

printf ("Nombre: 'I); gets (pp->nombre);<br />

printf ("Calle: "); gets (pp->calle);<br />

printf ("Ciudad: ") ; gets (pp->ciudad);<br />

printf ("Provincia: ") ; gets (pp->provincia)<br />

printf ("Código postal: ") ; gets (pp->codigopostal);<br />

void ver-info(struct into-persona p)<br />

i<br />

puts('\n\tInformación realativa a la persona");<br />

i<br />

puts (p. nombre) ;<br />

puts (p. calle) ;<br />

puts (p. ciudad) ;<br />

puts (p.provincia);<br />

puts (p.codigoposta1);<br />

;<br />

Si se <strong>de</strong>sea pasar la <strong>estructura</strong> por refer<strong>en</strong>cia, necesita situar un operador <strong>de</strong> refer<strong>en</strong>cia & antes<br />

<strong>de</strong> reg-dat <strong>en</strong> la llamada a la función <strong>en</strong>tradasna ( ) . El parhetro<br />

<strong>de</strong> ser tipo puntero struct infoqersona* pp . El acceso a miembro<br />

a partir <strong>de</strong> un puntero requiere el uso <strong>de</strong>l selector ->.<br />

9.6. UNIONES<br />

Las uniones son similares a las <strong>estructura</strong>s <strong>en</strong> cuanto que agrupa a una serie <strong>de</strong> variables, pero la forma<br />

<strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to es difer<strong>en</strong>te y, por consigui<strong>en</strong>te, efectos difer<strong>en</strong>tes. Una <strong>estructura</strong> (s truct) permite<br />

almac<strong>en</strong>ar variables relacionadas juntas y almac<strong>en</strong>adas <strong>en</strong> posiciones contiguas <strong>en</strong> memoria. Las<br />

uniones, <strong>de</strong>claradas con la palabra reservada union, almac<strong>en</strong>an también miembros múltiples <strong>en</strong> un<br />

paquete; sin embargo, <strong>en</strong> lugar <strong>de</strong> situar sus miembros unos <strong>de</strong>trás <strong>de</strong> otros, <strong>en</strong> una unión, todos los<br />

miembros se solapan <strong>en</strong>tre sí <strong>en</strong> la misma posición. El tamaño ocupado por una unión se <strong>de</strong>termina así:<br />

es analizado el tamaño <strong>de</strong> cada variable <strong>de</strong> la unión, el mayor tamaño <strong>de</strong> variable será el tamaño <strong>de</strong> la<br />

unión. La sintaxis <strong>de</strong> una unión es la sigui<strong>en</strong>te:<br />

union nombre {<br />

t ipol mi embrol ;<br />

t ipo2 mi <strong>en</strong>ibro2;<br />

...<br />

1;<br />

Un ejemplo:<br />

union Pruebaünion<br />

{<br />

float Iteml;<br />

int Item2;


Estructuras y uniones 31 1<br />

La cantidad <strong>de</strong> memoria reservada para una unión es igual a la anchura <strong>de</strong> la variable más gran<strong>de</strong>.<br />

En el tipo union, cada uno <strong>de</strong> los miembros dato compart<strong>en</strong> memoria con los otros miembros <strong>de</strong> la<br />

unión. La cantidad total <strong>de</strong> memoria utilizada por la unión comparte es <strong>de</strong> 8 bytes, ya que el elem<strong>en</strong>to<br />

double es el miembro dato mayor <strong>de</strong> la unión.<br />

union comparte<br />

{<br />

char letra;<br />

int elem<strong>en</strong>to;<br />

float precio;<br />

double z;<br />

1;<br />

Una razón para utilizar una unión es ahorrar memoria. En muchos programas se <strong>de</strong>b<strong>en</strong> t<strong>en</strong>er varias<br />

variables, pero no necesitan utilizarse todas al mismo tiempo. Considérese la situación <strong>en</strong> que se<br />

necesitan t<strong>en</strong>er diversas cad<strong>en</strong>as <strong>de</strong> caracteres <strong>de</strong> <strong>en</strong>trada. Se pued<strong>en</strong> crear varios arrays <strong>de</strong> cad<strong>en</strong>as <strong>de</strong><br />

caracteres, tales como las sigui<strong>en</strong>tes:<br />

char linea_ord<strong>en</strong>es[80];<br />

char m<strong>en</strong>saje-error [ 80 I ;<br />

char ayuda [ 80 I ;<br />

Estas tres variables ocupan 240 bytes <strong>de</strong> memoria. Sin embargo, si su programa no necesita utilizar<br />

las tres variables simultáneam<strong>en</strong>te, ¿por qué no permitirle compartir la memoria utilizando una unión?<br />

Cuando se combinan <strong>en</strong> el tipo union frases, estas variables ocupan un total <strong>de</strong> sólo 80 bytes.<br />

union frases {<br />

char linea_ord<strong>en</strong>es[80] ;<br />

char m<strong>en</strong>saje_error[80];<br />

char ayuda [80] ;<br />

} cad<strong>en</strong>as, *pc;<br />

Para referirse a los miembros <strong>de</strong> una unión, se utiliza el operador punto (.), o bi<strong>en</strong> el operador -> si<br />

se hace <strong>de</strong>s<strong>de</strong> un puntero a unión. Así:<br />

cad<strong>en</strong>as.ayuda;<br />

cad<strong>en</strong>as.m<strong>en</strong>saje-error;<br />

pc -> m<strong>en</strong>saje-error;<br />

9.7. ENUMERACIONES<br />

Un <strong>en</strong>um es un tipo <strong>de</strong>finido por el usuario con constantes <strong>de</strong> nombre <strong>de</strong> tipo <strong>en</strong>tero. En la <strong>de</strong>claración<br />

<strong>de</strong> un tipo <strong>en</strong>um se escribe una lista <strong>de</strong> id<strong>en</strong>tificadores que internam<strong>en</strong>te se asocian con las constantes<br />

<strong>en</strong>teras O, 1, 2, etc.<br />

Formato<br />

1. <strong>en</strong>um<br />

{<br />

<strong>en</strong>umerador , <strong>en</strong>umerador , ... <strong>en</strong>umerador<br />

I;<br />

2. <strong>en</strong>um nombre<br />

i<br />

<strong>en</strong>umerador , <strong>en</strong>umerador , ... <strong>en</strong>umerador<br />

1;<br />

Y


-7<br />

312 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En la <strong>de</strong>claración <strong>de</strong>l tipo <strong>en</strong>um pued<strong>en</strong> asociarse a los id<strong>en</strong>tificadores valores constantes <strong>en</strong> vez <strong>de</strong><br />

la asociación que por <strong>de</strong>fecto se hace (O, 1, 2, etc.). Para ello se utiliza este formato:<br />

3. <strong>en</strong>um nombre<br />

I;<br />

{<br />

<strong>en</strong>umeradorl = expresiÓn-constantel,<br />

<strong>en</strong>umerador = expresión-constante ,<br />

...<br />

<strong>en</strong>umerador, = exprsesión-constante,<br />

Ejemplo 9.2<br />

Usos tipicos <strong>de</strong> <strong>en</strong>Um<br />

<strong>en</strong>um Interruptor<br />

{<br />

ENCENDIDO,<br />

APAGADO<br />

I;<br />

<strong>en</strong>um Boolean<br />

{<br />

FALSE,<br />

TRUE<br />

I;<br />

Ejemplo<br />

<strong>en</strong>um<br />

I<br />

ROJO, VERDE, AZUL<br />

1;<br />

<strong>de</strong>fine tres constantes ROJO, VERDE y AZUL <strong>de</strong> valores iguales a O, 1 y 2, respectivam<strong>en</strong>te. Los<br />

miembros <strong>datos</strong> <strong>de</strong> un <strong>en</strong>um se llaman <strong>en</strong>umeradores y la constante <strong>en</strong>tera por <strong>de</strong>fecto <strong>de</strong>l primer<br />

<strong>en</strong>umerador <strong>de</strong> la lista <strong>de</strong> los miembros <strong>datos</strong> es igual a O. Obsérvese que, al contrario que struct y<br />

union, los miembros <strong>de</strong> un tipo <strong>en</strong>um se separan por el operador coma. El ejemplo anterior es<br />

equival<strong>en</strong>te a la <strong>de</strong>finición <strong>de</strong> las tres constantes, ROJO, VERDE y AZUL, tal como:<br />

const int ROJO = O;<br />

const int VERDE = 1;<br />

const int AZUL = 2;<br />

En la sigui<strong>en</strong>te <strong>de</strong>claración <strong>de</strong> tipo <strong>en</strong>umerado se le da un nombre al tipo<br />

<strong>en</strong>um dias-semana<br />

{<br />

LUNES, MARTES, MIERCOLES, JUEVES, VIERNES, SABADO, DOMINGO<br />

1;<br />

Una variable <strong>de</strong> tipo <strong>en</strong>um dias-semana pue<strong>de</strong> tomar los valores especificados <strong>en</strong> la <strong>de</strong>claración<br />

<strong>de</strong>l tipo. El sigui<strong>en</strong>te bucle está controlado por una variable <strong>de</strong>l tipo <strong>en</strong>umerado.<br />

<strong>en</strong>um dias-semana dia;<br />

for (dia = LUNES; dia


Estructuras y uniones 313<br />

1<br />

printf ("%d",dia);<br />

La ejecución <strong>de</strong>l bucle escribiría <strong>en</strong> pantalla: O 1 2 3 4 5 6.<br />

A los <strong>en</strong>umeradores se pued<strong>en</strong> asignar valores constantes o expresiones constantes durante la<br />

<strong>de</strong>claración:<br />

<strong>en</strong>um Hexaedro<br />

i<br />

VERTICES = 8,<br />

LADOS = 12,<br />

CARAS =6<br />

1<br />

Ejercicio 9.2<br />

El sigui<strong>en</strong>te programa muestra el uso <strong>de</strong> la <strong>en</strong>umeracidn boo1 ean . El programa lee un texto y cu<strong>en</strong>ta<br />

las vocales leídas. La función vocal ( ) <strong>de</strong>vuelve TRUE si el carácter <strong>de</strong> <strong>en</strong>trada es vocal.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

<strong>en</strong>um boolean<br />

i<br />

FALSE, TRUE<br />

1;<br />

<strong>en</strong>um boolean vocal(char c);<br />

void main()<br />

{<br />

1<br />

char car;<br />

int numvocal = O;<br />

puts("\nIntroduce un texto. Para terminar: INTRO<br />

while ( (car = getchar () ) ! =' \n')<br />

i<br />

if (vocal(tolower(car)) )<br />

numvocal++;<br />

1<br />

printf("\n Total <strong>de</strong> vocales leidas: %d\n",numvocal);<br />

<strong>en</strong>um boolean vocal(char c)<br />

n<br />

i<br />

switch (c)<br />

{<br />

I<br />

case 'a':<br />

case 'e':<br />

case 'ir:<br />

case 'o':<br />

case 'u':<br />

return TRUE;<br />

<strong>de</strong>fault:<br />

return FALSE;


31 4 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

9.7.1. sizeof <strong>de</strong> tipos <strong>de</strong> <strong>datos</strong> <strong>estructura</strong>dos<br />

El tamaño <strong>en</strong> bytes <strong>de</strong> una <strong>estructura</strong>, <strong>de</strong> una unión o <strong>de</strong> un tipo <strong>en</strong>umerado se pue<strong>de</strong> <strong>de</strong>terminar con el<br />

operador sizeof.<br />

El sigui<strong>en</strong>te programa extrae el tamaño <strong>de</strong> una <strong>estructura</strong> (struct), <strong>de</strong> una unión (union) con<br />

miembros dato idénticos, y <strong>de</strong> un tipo <strong>en</strong>umerado.<br />

/* <strong>de</strong>clara una union */<br />

union tipo-union<br />

i<br />

char c;<br />

int i;<br />

float f ;<br />

double d;<br />

I;<br />

/* <strong>de</strong>clara una <strong>estructura</strong> */<br />

struct tipo-<strong>estructura</strong><br />

i<br />

char c;<br />

int i;<br />

float f;<br />

double d;<br />

I;<br />

/* <strong>de</strong>clara un tipo <strong>en</strong>umerado */<br />

<strong>en</strong>um monedas<br />

{<br />

I;<br />

...<br />

PESETA,<br />

DURO,<br />

CINCODUROS,<br />

CIEN<br />

printf ("\nsizeof(tipo-<strong>estructura</strong>): %d\n",<br />

sizeof(struct tipo-<strong>estructura</strong>) 1 ;<br />

printf ("\nsizeof(tipo-union): Bd\n",<br />

sizeof(union tipo-union));<br />

printf ("\nsizeof(monedas): %d\n",<br />

sizeof(<strong>en</strong>um monedas));<br />

La salida que se g<strong>en</strong>era con estos <strong>datos</strong>:<br />

sizeof(tipo_<strong>estructura</strong>):15<br />

sizeof(tipo-union): 8<br />

sizeof(monedas): 2<br />

9.7.2. type<strong>de</strong>f<br />

Un type<strong>de</strong>f permite a un programador crear un sinónimo <strong>de</strong> un tipo <strong>de</strong> dato <strong>de</strong>finido por el usuario o<br />

<strong>de</strong> un tipo ya exist<strong>en</strong>te.


Estructuras y uniones 315<br />

Ejemplo<br />

Uso <strong>de</strong> type<strong>de</strong>f para <strong>de</strong>clarar un nuevo nombre, Longi tud, <strong>de</strong> tipo <strong>de</strong> dato double.<br />

...<br />

type<strong>de</strong>f double Longitud;<br />

...<br />

Longitud Distancia (const struct Pto* p, const struct Pto* p2)<br />

{<br />

I<br />

...<br />

Longitud longitud = sqrt(r-cua);<br />

return longitud;<br />

Otros ejemplos:<br />

type<strong>de</strong>f char* String;<br />

type<strong>de</strong>f const char* string;<br />

Pue<strong>de</strong> <strong>de</strong>clararse un tipo <strong>estructura</strong> o un tipo unión y a continuación asociar el tipo estructrura a un<br />

nombre con type<strong>de</strong>f.<br />

Ejemplo<br />

Declaración <strong>de</strong>l tipo <strong>de</strong> dato complejo y asociación a complex.<br />

struct complejo<br />

{<br />

1;<br />

float x,y;<br />

type<strong>de</strong>f struct complejo complex;<br />

/* <strong>de</strong>finición <strong>de</strong> un array <strong>de</strong> complejos */<br />

complex v[121;<br />

La v<strong>en</strong>taja <strong>de</strong> type<strong>de</strong>f es que permite dar nombres L; tipos LV <strong>datos</strong> más acor<strong>de</strong>s con lo que<br />

repres<strong>en</strong>tan <strong>en</strong> una <strong>de</strong>terminada aplicación.<br />

9.8. CAMPOS DE BIT<br />

El l<strong>en</strong>guaje c permite realizar operaciones con los bits <strong>de</strong> una palabra. Ya se han estudiado los<br />

operadores <strong>de</strong> manejo <strong>de</strong> bits: >>,


Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

tipo ha <strong>de</strong> ser <strong>en</strong>tero, int; g<strong>en</strong>eralm<strong>en</strong>te unsigned int<br />

longitud es el número <strong>de</strong> bits consecutivos que se toman<br />

Ejemplo 9.3<br />

En este ejemplo se <strong>de</strong>clara un campo <strong>de</strong> bits para repres<strong>en</strong>tar <strong>en</strong> formato comprimido el día, mes aiio<br />

(los dos últimos dígitos) y si el año es bisiesto.<br />

struct fecha {<br />

unsigned dia: 5;<br />

unsigned mes: 4;<br />

unsigned año: 7;<br />

unsigned bisiesto: 1;<br />

I;<br />

t<br />

Ejemplo 9.4<br />

El sigui<strong>en</strong>te ejemplo muestra cómo pue<strong>de</strong> utilizarse campos <strong>de</strong> bits para repres<strong>en</strong>tar .si están o no<br />

conectados diversos compon<strong>en</strong>tes eléctricos. Cada compon<strong>en</strong>te se repres<strong>en</strong>ta con un pag, con un bit;<br />

cuando esté puesto a cero es que no está conectado, cuando esté puesto a uno está conectado.<br />

struct compon<strong>en</strong>tes {<br />

unsigned diodo: 1;<br />

unsigned resist<strong>en</strong>cia: 1;<br />

unsigned amperimetro: 1;<br />

unsigned transistor: 1;<br />

unsigned cond<strong>en</strong>sador: 1;<br />

unsigned inductancia: 1;<br />

1;<br />

Los campos individuales se refer<strong>en</strong>cian como cualquier otro miembro <strong>de</strong> una <strong>estructura</strong>: selector<br />

punto ( . ). Por ejemplo,<br />

struct compon<strong>en</strong>tes ct;<br />

ct.diodo = 1;<br />

if (ct.amperimetro)<br />

i<br />

1<br />

Al <strong>de</strong>clarar campos <strong>de</strong> bits, la suma <strong>de</strong> los bits <strong>de</strong>clarados pue<strong>de</strong> exce<strong>de</strong>r el tamaño <strong>de</strong> un <strong>en</strong>tero; <strong>en</strong><br />

ese caso se emplea la sigui<strong>en</strong>te posición <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong>tero. No está permitido que un campo <strong>de</strong><br />

bits solape los límites <strong>en</strong>tre dos int.<br />

Al <strong>de</strong>clarar una <strong>estructura</strong> pue<strong>de</strong> haber miembros que sean variables y otros campos <strong>de</strong> bits. La<br />

sigui<strong>en</strong>te <strong>estructura</strong> ti<strong>en</strong>e esta característica:<br />

struct reg-estudiante{<br />

char nombre [331;<br />

char ape111 [331;<br />

char ape112 [331 ;<br />

unsigned masculino: 1;<br />

unsigned fem<strong>en</strong>ino: 1;<br />

unsigned curso: 3;<br />

1;


Estructuras y uniones 31 7<br />

Los campos <strong>de</strong> bits se utilizan para rutinas <strong>de</strong> <strong>en</strong>criptación <strong>de</strong> <strong>datos</strong> y fundam<strong>en</strong>talm<strong>en</strong>te para ciertos<br />

interfaces <strong>de</strong> dispositivos externos. Pres<strong>en</strong>tan ciertas restricciones. Así, no se pue<strong>de</strong> tomas la dirección<br />

<strong>de</strong> una variable campo <strong>de</strong> bits; no pue<strong>de</strong> haber arrays <strong>de</strong> campos <strong>de</strong> bits; no se pue<strong>de</strong> solapar fronteras<br />

<strong>de</strong> int. Dep<strong>en</strong><strong>de</strong> <strong>de</strong>l procesador que los campos <strong>de</strong> bits se aline<strong>en</strong> <strong>de</strong> izquierda a <strong>de</strong>recha o <strong>de</strong> <strong>de</strong>recha<br />

a izquierda (convi<strong>en</strong>e hacer una comprobación para cada procesador, utilizando para ello un union con<br />

variable <strong>en</strong>tera y campos <strong>de</strong> bits).<br />

Ejemplo 9.5<br />

Se ti<strong>en</strong>e la función pet icion-acceso í 1 capaz <strong>de</strong> direccionar una posición <strong>de</strong> memoria <strong>de</strong> 8 bits si<br />

recibe como argum<strong>en</strong>to una variable llamada ochobit s. Con esta variable controla a través <strong>de</strong> cada<br />

bit las peticiones <strong>de</strong> acceso a cada uno <strong>de</strong> los ocho periféricos distintos con que trabaja; ev<strong>en</strong>tos<br />

externos son los que se <strong>en</strong>cargan <strong>de</strong> cargar la variable ochobit s.<br />

Se quiere escribir una función que <strong>de</strong>termine cuantos accesos se produc<strong>en</strong> por cada periférico <strong>en</strong> un<br />

bucle <strong>de</strong> 1000 llamadas a la función peticion-acceso ( ) . Se supone que cada llamada sólo activa<br />

un periférico.<br />

Análisis<br />

El tipo <strong>de</strong> la variable ochobits va a ser una <strong>estructura</strong> <strong>de</strong> campos <strong>de</strong> bits, cada campo con longitud 1;<br />

por lo que pue<strong>de</strong> t<strong>en</strong>er dos estados, O o 1, para indicar no acceso o sí acceso. Un array <strong>de</strong> 8 elem<strong>en</strong>tos,<br />

tantos como periféricos se utiliza para contar los accesos a cada periférico.<br />

/* Tipo <strong>estructura</strong> <strong>de</strong> campos <strong>de</strong> bits */<br />

struct perifericos{<br />

unsigned perfl: 1;<br />

unsigned perf2: 1;<br />

unsigned perf3: 1;<br />

unsigned perf4: 1;<br />

unsigned perf5: 1;<br />

unsigned perfó: 1;<br />

unsigned perf7: 1;<br />

unsigned perf8: 1;<br />

I;<br />

/* Prototipo <strong>de</strong> función peticion-acceso0 */<br />

void petition-acceso (const struct perifericos ochobits);<br />

/* Función que contabiliza los accesos a cada periférico. */<br />

void accesosgerf(int acceper[])<br />

l<br />

int i;<br />

const struct perifericos ochobits;<br />

const nev<strong>en</strong>tos=1000;<br />

for (izo; i


318 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

petition-acceso(ochobits);<br />

if (ochobits.perf1)<br />

++acceper [O I ;<br />

elseif (ocho.bits.perf2);<br />

++acceper [ 13 ;<br />

elseif (ocho.bits.perf3);<br />

++acceper [2];<br />

elseif (ocho.bits.perf4);<br />

++acceper [ 3 I ;<br />

elseif (ocho.bits.perf5);<br />

++acceper [ 43 ;<br />

elseif (ocho.bits.perf6);<br />

++acceper [ 51 ;<br />

elseif (ocho.bits.perf7);<br />

++acceper [ 61 ;<br />

elseif (ocho.bits.perf8);<br />

++acceper [7];<br />

Ejemplo 9.6<br />

Haci<strong>en</strong>do uso <strong>de</strong> una <strong>estructura</strong> <strong>de</strong> campo <strong>de</strong> bits y <strong>de</strong> una union, <strong>en</strong> este ejercicio se escribe un<br />

programa para visualizar la <strong>de</strong>codijkación <strong>en</strong> bits <strong>de</strong> cualquier carácter leído por teclado.<br />

Análisis<br />

Se <strong>de</strong>clara un tipo <strong>estructura</strong> campo <strong>de</strong> bits, con tantos campos como bits ti<strong>en</strong>e un byte, que a su vez es<br />

el almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> un carácter. La <strong>de</strong>codificación es inmediata <strong>de</strong>clarando una union <strong>en</strong>tre una<br />

variable carácter y una variable campo <strong>de</strong> bits <strong>de</strong>l tipo indicado.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine mayus (ch) ((ch>=’a’&& ch


Estructuras y uniones 31 9<br />

void main()<br />

t<br />

puts ('Teclea caracteres. Para salir carácter XI);<br />

do {<br />

caracter.ch = getcheo;<br />

printf ('I : ") ;<br />

<strong>de</strong>codifica(caracter.bits);<br />

)while mayusc(caracter.ch) !='X');<br />

1<br />

void <strong>de</strong>codifica(struct byte b)<br />

/* Los campos <strong>de</strong> bits se alinean <strong>de</strong> <strong>de</strong>recha a izquierda, por esa razón se<br />

escrib<strong>en</strong> los campos <strong>en</strong> ord<strong>en</strong> inverso */<br />

print f ("%2u%2u%2~%2~%2u%2~%2U%2u%~u \nu',<br />

b.h, b.g, b.f, b.e, b.d, b.c, b.b, b.a);<br />

9.9. RES N<br />

Para crear una variable estnictura se escribe<br />

struct empleado pepe;<br />

sinónimo <strong>de</strong> un tipo exist<strong>en</strong>te.<br />

type<strong>de</strong>f struct empleado<br />

regempleado;


I<br />

320 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

9.10. EJERCICIOS


Estructuras y uniones 321<br />

9.11. PROBLEMAS<br />

9.1. cular el número <strong>de</strong><br />

as; <strong>de</strong>clarar fecha<br />

92. Escribe un programa <strong>de</strong> facturación <strong>de</strong> cli<strong>en</strong>tes.<br />

Los cli<strong>en</strong>tes ti<strong>en</strong><strong>en</strong> un nombre, el número <strong>de</strong><br />

unida<strong>de</strong>s solicitadas, el precio <strong>de</strong> cada unidad y<br />

se <strong>en</strong>cu<strong>en</strong>tra: motoso, atrasado,<br />

ama <strong>de</strong>be g<strong>en</strong>erar a los diversos<br />

93. Modifique el pro <strong>de</strong> facturación <strong>de</strong> cli<strong>en</strong>tes<br />

<strong>de</strong> tal modo que se puedan obt<strong>en</strong>er los<br />

sigui<strong>en</strong>tes listados.<br />

* Cli<strong>en</strong>tes <strong>en</strong> estado moroso.<br />

Cli<strong>en</strong>tes <strong>en</strong> estado pagado con fachira mayor<br />

<strong>de</strong> una <strong>de</strong>terminada cantidad.<br />

9.4. Escribe un programa que permita hacer las operaciones<br />

<strong>de</strong> suma, resta, multiplicación y división<br />

<strong>de</strong> números complejos. El tipo complejo ha<br />

<strong>de</strong> <strong>de</strong>finirse como una <strong>estructura</strong>,<br />

9.5. Un número racional se caracteriza por el numerador<br />

y d<strong>en</strong>ominador. Escribe un programa para<br />

Número <strong>de</strong> perdidas <strong>de</strong> balón.<br />

4 Número <strong>de</strong> rebotes cogidos.<br />

* Nombre <strong>de</strong>l mejor anotador <strong>de</strong> triples.<br />

Número <strong>de</strong> triples <strong>de</strong>l mejor triplisia<br />

<strong>de</strong> futbol aiiadir la información:<br />

información para todos los equipos integrantes<br />

<strong>en</strong> ambas ligas.<br />

9.7. Modificar el programa 9.6 para obt<strong>en</strong>er los<br />

sigui<strong>en</strong>tes informes o <strong>datos</strong>.<br />

Listado <strong>de</strong> los mejores triplistas <strong>de</strong> cada<br />

equipo.<br />

Máximo goleador<br />

número racional.<br />

la distancia <strong>en</strong>tre<br />

Dados dos puntos<br />

Dados tres<br />

información:


CAPíTULO 10<br />

PUNTEROS<br />

(APUNTADORES)<br />

CONTENIDO<br />

10.1. Direcciones <strong>en</strong> memoria.<br />

10.8. Concepto <strong>de</strong> puntero<br />

(apuntador).<br />

10.9. Punteros null y void.<br />

10.4. Punteros a punteros.<br />

10.6. Punteros a arrays.<br />

10.6. Arrays <strong>de</strong> punteros.<br />

10.7. Punteros a cad<strong>en</strong>as.<br />

10.8. Aritmética <strong>de</strong> punteros.<br />

10.9. Punteros constantes fr<strong>en</strong>te<br />

a punteros a constantes.<br />

10.10. Punteros como argum<strong>en</strong>to<br />

<strong>de</strong> funciones.<br />

10.11. Punteros a funciones.<br />

10.12. Punteros a <strong>estructura</strong>s.<br />

10.13. Resum<strong>en</strong>.<br />

10.14. Ejercicios.<br />

10.15. Problemas.


1<br />

Los punteros <strong>en</strong> C ti<strong>en</strong><strong>en</strong> la fama, <strong>en</strong> el mundo <strong>de</strong> la programación, <strong>de</strong><br />

dificultad, tanto <strong>en</strong> el apr<strong>en</strong>dizaje como <strong>en</strong> su uso. En este capítulo se tratara <strong>de</strong><br />

mostrar que los punteros no son más difíciles <strong>de</strong> apr<strong>en</strong><strong>de</strong>r que cualquier otra<br />

herrami<strong>en</strong>ta <strong>de</strong> programación ya examinada o por examinar a lo largo <strong>de</strong> este<br />

libro. El puntero, no es más que una herrami<strong>en</strong>ta muy pot<strong>en</strong>te que pue<strong>de</strong><br />

utilizar <strong>en</strong> sus programas para hacerlos más efici<strong>en</strong>tw y flexibles. Los punteros<br />

son, sin género <strong>de</strong> dudas, una <strong>de</strong> las razones fundam<strong>en</strong>tales para qye el<br />

l<strong>en</strong>guaje C sea tan pot<strong>en</strong>te y tan utilizado.<br />

Una vaziabiepmtem (opmtero, como se iiarna nonnahn<strong>en</strong>te) es una variable<br />

que conti<strong>en</strong>e direcciones <strong>de</strong> otras variables. 'ibdas las variables vistas hasta este<br />

mom<strong>en</strong>to conti<strong>en</strong><strong>en</strong> valores <strong>de</strong> <strong>datos</strong>, por el contrario las variables punteros<br />

conti<strong>en</strong><strong>en</strong> valores que son direcciones <strong>de</strong> memoria don<strong>de</strong> se almac<strong>en</strong>an <strong>datos</strong>. En<br />

resum<strong>en</strong>, un puntero es una variable que conti<strong>en</strong>e una dirección <strong>de</strong> memoria, y<br />

utilizando punteros su programa pue<strong>de</strong> realizar muchas tareas que no sería<br />

posible utiliwtndo tipos <strong>de</strong> <strong>datos</strong> estándar.<br />

En este capítulo se estudiarán los difer<strong>en</strong>tes aspectos <strong>de</strong> los punteros:<br />

0 punteros;<br />

0 utilización <strong>de</strong> punteros;<br />

0 asignación dinámica <strong>de</strong> memoria;<br />

0 aritmética <strong>de</strong> punteros;<br />

0 arrays <strong>de</strong> punteros;<br />

0 punteros a punteros, funciones y <strong>estructura</strong>s.<br />

*<br />

CONCEPTOS CLAVE<br />

Puntero (apuntador).<br />

Direcciones.<br />

Refer<strong>en</strong>cias.<br />

Palabra reservada null.<br />

Palabra reservada void.<br />

Arrays <strong>de</strong> punteros.<br />

Aritmética <strong>de</strong> punteros.<br />

Punteros versus arrays.<br />

Tipos <strong>de</strong> punteros.<br />

Palabra reservada const.<br />

323


324 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

10.1. DIRECCIONES EN MEMORIA<br />

Cuando una variable se <strong>de</strong>clara, se asocian tres atributos fundam<strong>en</strong>tales con la misma: su nombre, su tipo<br />

y su dirección <strong>en</strong> memoria.<br />

Ejemplo<br />

int n;<br />

n-<br />

int<br />

Ox4f f fd34<br />

/* asocia al nombre n, el tipo int y la dirección<br />

<strong>de</strong> alguna posición <strong>de</strong> memoria don<strong>de</strong> se almac<strong>en</strong>a<br />

el valor <strong>de</strong> n<br />

*/<br />

Esta caja repres<strong>en</strong>ta la posición <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>en</strong> memoria. El nombre <strong>de</strong> la variable está a la<br />

izquierda <strong>de</strong> la caja, la dirección <strong>de</strong> variable está <strong>en</strong>cima <strong>de</strong> la caja y el tipo <strong>de</strong> variable está <strong>de</strong>bajo <strong>en</strong><br />

la caja. Si el valor <strong>de</strong> la variable se conoce, se repres<strong>en</strong>ta <strong>en</strong> el interior <strong>de</strong> la caja<br />

n<br />

int<br />

75<br />

Al valor <strong>de</strong> una variable se acce<strong>de</strong> por medio <strong>de</strong> su nombre. Por ejemplo, se pue<strong>de</strong> imprimir el valor<br />

<strong>de</strong> n con la s<strong>en</strong>t<strong>en</strong>cia:<br />

printf ("%d",n) ;<br />

A la dirección <strong>de</strong> la variable se acce<strong>de</strong> por medio <strong>de</strong>l operador <strong>de</strong> dirección &. Por ejemplo, se<br />

pue<strong>de</strong> imprimir la dirección <strong>de</strong> n con la s<strong>en</strong>t<strong>en</strong>cia:<br />

printf ("%p", &n);<br />

El operador <strong>de</strong> dirección "&" «opera» (se aplica) sobre el nombre <strong>de</strong> la variable para obt<strong>en</strong>er sus<br />

direcciones. Ti<strong>en</strong>e preced<strong>en</strong>cia <strong>de</strong> nivel 15 con el mismo nivel que el operador lógico NOT ( ! ) y el<br />

operador <strong>de</strong> preincrem<strong>en</strong>to + +. (Véase Capítulo 4.)<br />

Ejemplo 10.1<br />

Obt<strong>en</strong>er el valor y la dirección <strong>de</strong> una variable.<br />

#inclu<strong>de</strong> <br />

void main()<br />

t<br />

int n = 75;<br />

print f ("n = %d\n", n) ; /* visualiza el valor <strong>de</strong> n */<br />

printf ("&n = %p\n",&n); /* visualiza dirección <strong>de</strong> n */<br />

1


Punteros (apuntadores) 325<br />

Ejecución<br />

n = 45<br />

&n = Ox4fffd34<br />

Nota: 0x4 f f f d3 4 es una dirección <strong>en</strong> código hexa<strong>de</strong>cimal.<br />

If Ox" es el prefijo correspondi<strong>en</strong>te al código hexa<strong>de</strong>cimal.<br />

10.2. CONCEPTO DE PUNTERO (APUNTADOR)'<br />

Cada vez que se <strong>de</strong>clara una variable C, el compilador establece un área <strong>de</strong> memoria para almac<strong>en</strong>ar el<br />

cont<strong>en</strong>ido <strong>de</strong> la variable. Cuando se <strong>de</strong>clara una variable int (<strong>en</strong>tera), por ejemplo, el compilador asigna<br />

dos bytes <strong>de</strong> memoria. El espacio para esa variable se sitúa <strong>en</strong> una posición específica <strong>de</strong> la memoria,<br />

conocida como dirección <strong>de</strong> memoria. Cuando se refer<strong>en</strong>cia (se hace uso) al valor <strong>de</strong> la variable, el<br />

compilador <strong>de</strong> C acce<strong>de</strong> automáticam<strong>en</strong>te a la dirección <strong>de</strong> memoria don<strong>de</strong> se almac<strong>en</strong>a el <strong>en</strong>tero. Se<br />

pue<strong>de</strong> ganar <strong>en</strong> eficacia <strong>en</strong> el acceso a esta dirección <strong>de</strong> memoria utilizando un puntero.<br />

Cada variable que se <strong>de</strong>clara <strong>en</strong> C ti<strong>en</strong>e una dirección asociada con ella. Un puntero es una dirección<br />

<strong>de</strong> memoria. El concepto <strong>de</strong> punteros ti<strong>en</strong>e correspond<strong>en</strong>cia <strong>en</strong> la vida diaria. Cuando se <strong>en</strong>vía una carta<br />

por correo, su información se <strong>en</strong>trega basada <strong>en</strong> un puntero que es la dirección <strong>de</strong> esa carta. Cuando se<br />

telefonea a una persona, se utiliza un puntero (el número <strong>de</strong> teléfono que se marca). Así pues, una<br />

dirección <strong>de</strong> correos y un número <strong>de</strong> teléfono ti<strong>en</strong><strong>en</strong> <strong>en</strong> común que ambos indican dón<strong>de</strong> <strong>en</strong>contrar algo.<br />

Son punteros a edificios y teléfonos, respectivam<strong>en</strong>te. Un puntero <strong>en</strong> C también indica dón<strong>de</strong> <strong>en</strong>contrar<br />

algo, ¿,dón<strong>de</strong> <strong>en</strong>contrar los <strong>datos</strong> que están asociados con una variable? Un puntero C es la dirección <strong>de</strong><br />

una Variable. Los punteros se rig<strong>en</strong> por estas reglas básicas:<br />

un puntero es una variable como cualquier otra;<br />

una variable puntero conti<strong>en</strong>e una dirección que apunta a otra posición <strong>en</strong> memoria;<br />

<strong>en</strong> esa posición se almac<strong>en</strong>an los <strong>datos</strong> a los que apunta el puntero;<br />

un puntero apunta a una variable <strong>de</strong> memoria.<br />

Direccion <strong>de</strong><br />

memoria alta<br />

El valor <strong>de</strong> un puntero es<br />

una dirección. La dirección<br />

<strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong>l estado <strong>de</strong> la<br />

computadora <strong>en</strong> la cual se<br />

ejecuta el programa.<br />

1 O01<br />

p + 1000<br />

999<br />

1 o1<br />

alia + 100<br />

99<br />

Dirección <strong>de</strong><br />

memoria baja<br />

p conti<strong>en</strong>e el valor 100, que<br />

es la dirección <strong>de</strong> ai f ii<br />

*p es el valor <strong>de</strong>l elem<strong>en</strong>to<br />

al que apunta p Por consigui<strong>en</strong>te,<br />

*p toma el valor<br />

'A'<br />

'<br />

Figura 10.1. Relaciones <strong>en</strong>tre *p y el valor <strong>de</strong> p (dirección <strong>de</strong> al fa).<br />

En Latinoamérica es usual emplear el término u/xuttuúor.


326 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

I<br />

,<br />

1<br />

El tipo <strong>de</strong> variable que almac<strong>en</strong>a una dirección se d<strong>en</strong>omina puntero.<br />

Ejemplo 10.2<br />

#inclu<strong>de</strong> <br />

void main()<br />

i<br />

int n = 75;<br />

int* p = &n; /* p variable puntero, ti<strong>en</strong>e dirección <strong>de</strong> n*/<br />

printf("n = %d, &n = %p, p = %p\n",n,&n,p);<br />

printf ("&p = %p\n",&p);<br />

1<br />

Ejecución<br />

n = 75, &n = Ox4fffd34, p = Ox4fffd34<br />

&p = 0~4fffd30<br />

Ox4 f € f d30 Ox4fffd34<br />

p I0x4fffd34I n<br />

int*<br />

int<br />

La variable p se d<strong>en</strong>omina «puntero» <strong>de</strong>bido a que su valor «apunta» a la posición <strong>de</strong> otro valor. Es<br />

un puntero int cuando el valor al que apunta es <strong>de</strong> tipo int como <strong>en</strong> el ejemplo anterior.<br />

10.2.1. Declaración <strong>de</strong> punteros<br />

Al igual que cualquier variable, las variables punteros han <strong>de</strong> ser <strong>de</strong>claradas antes <strong>de</strong> utilizarlas. La<br />

<strong>de</strong>claración <strong>de</strong> una variable puntero <strong>de</strong>be indicar al compilador el tipo <strong>de</strong> dato al que apunta el puntero;<br />

para ello se hace prece<strong>de</strong>r a su nombre con un asterisco (*), mediante el sigui<strong>en</strong>te formato:<br />

*<br />

Algunos ejemplos <strong>de</strong> variables punteros:<br />

int* ptrl; /* Puntero a un tipo <strong>de</strong> dato <strong>en</strong>tero (int)*/<br />

long* ptr2; /* Puntero a un tipo <strong>de</strong> dato <strong>en</strong>tero largo (long int)*/<br />

char* ptr3; /* Puntero a un tipo <strong>de</strong> dato char */<br />

float *f; /* Puntero a un tipo <strong>de</strong> dato float */<br />

Un operador * <strong>en</strong> una <strong>de</strong>claración indica que la variable <strong>de</strong>clarada almac<strong>en</strong>ará una dirección <strong>de</strong> un<br />

tipo <strong>de</strong> dato especificado. La variable p trl almac<strong>en</strong>ará la dirección <strong>de</strong> un <strong>en</strong>tero, la variable p t r2<br />

almac<strong>en</strong>ará la dirección <strong>de</strong> un dato tipo long, etc.<br />

Siempre que aparezca un asterisco ( * ) <strong>en</strong> una <strong>de</strong>finición <strong>de</strong> una variable, ésta es una variable<br />

puntero.


7-<br />

Punteros (apuntadores)<br />

10.2.2. Inicialización* (iniciación) <strong>de</strong> punteros<br />

AI igual que otras variables, C no inicializa los punteros cuando se <strong>de</strong>claran y es preciso inicializarlos<br />

antes <strong>de</strong> su uso. La inicialización <strong>de</strong> un puntero proporciona a ese puntero la dirección <strong>de</strong>l dato<br />

correspondi<strong>en</strong>te. Después <strong>de</strong> la inicialización, se pue<strong>de</strong> utilizar el puntero para refer<strong>en</strong>ciar los <strong>datos</strong><br />

direccionados. Para asignar una dirección <strong>de</strong> memoria a un puntero se utiliza el operador <strong>de</strong> refer<strong>en</strong>cia<br />

&. Así, por ejemplo,<br />

&valor<br />

significa «la dirección <strong>de</strong> valor». Por consigui<strong>en</strong>te, el método <strong>de</strong> inicialización (iniciación), también<br />

d<strong>en</strong>ominado estático, requiere:<br />

Asignar memoria (estáticam<strong>en</strong>te) <strong>de</strong>fini<strong>en</strong>do una variable y a continuación hacer que el puntero<br />

apunte al valor <strong>de</strong> la variable.<br />

int i;<br />

int *p;<br />

p = &i;<br />

Asignar un valor a la dirección <strong>de</strong> memoria.<br />

*p = 50;<br />

/* <strong>de</strong>fine una variable i */<br />

/* <strong>de</strong>fine un puntero a un <strong>en</strong>tero p*/<br />

/* asLgnd la dirección <strong>de</strong> i a p */<br />

Cuando ya se ha <strong>de</strong>finido un puntero, el asterisco <strong>de</strong>lante <strong>de</strong> la variable puntero indica «e/ cont<strong>en</strong>ido<br />

d<strong>en</strong> <strong>de</strong> la memoria apuntada por el puntero y será <strong>de</strong>l tipo dado.<br />

Este tipo <strong>de</strong> inicialización es estática, ya que la asignación <strong>de</strong> memoria utilizada para almac<strong>en</strong>ar el<br />

valor es fijo y no pue<strong>de</strong> <strong>de</strong>saparecer. Una vez que la variable se <strong>de</strong>fine, el compilador establece sufici<strong>en</strong>te<br />

memoria para almac<strong>en</strong>ar un valor <strong>de</strong>l tipo <strong>de</strong> dato dado. La memoria permanece reservada para esta<br />

variable y no se pue<strong>de</strong> utilizar para otra cosa durante la ejecución <strong>de</strong>l programa. En otras palabras, no<br />

se pue<strong>de</strong> liberar la memoria reservada para una variable. El puntero a esa variable se pue<strong>de</strong> cambiar,<br />

pero permanecerá la cantidad <strong>de</strong> memoria reservada.<br />

El operador & <strong>de</strong>vuelve la dirección <strong>de</strong> la variable a la cual se aplica,<br />

Otros ejemplos <strong>de</strong> inicialización estáticos:<br />

1. int edad = 50; /*<strong>de</strong>fine una variable edad <strong>de</strong> valor 50 */<br />

int *p-edad = &edad; /*<strong>de</strong>fine un puntero <strong>de</strong> <strong>en</strong>teros inicializándolo<br />

con la dirección <strong>de</strong> edad */<br />

2. char *p; /*Fi.gura 10.1 */<br />

char alfa = 'A';<br />

p = &alfa;<br />

3. char cd[] = "Compacto';<br />

char *c;<br />

.C = cd; /*c ti<strong>en</strong>e la dirección <strong>de</strong> Id cad<strong>en</strong>a cd */<br />

Es un error asignar un valor, a un cont<strong>en</strong>ido <strong>de</strong> una variable puntero si previam<strong>en</strong>te no se ha<br />

inicializado con la dirección <strong>de</strong> una variable, o bi<strong>en</strong> se le ha asignado dinámicam<strong>en</strong>te memoria. Por<br />

ejemplo:<br />

float* px;<br />

*px = 23.5;<br />

/* puntero a float */<br />

/* error, px no conti<strong>en</strong>e dirección */<br />

' El diccionario <strong>de</strong> la Real Aca<strong>de</strong>mia dc la L<strong>en</strong>gua Española sdo acepta el término iniciar y el término inicial. El empleo<br />

<strong>de</strong> iniridircir sólo se.justifica por el ext<strong>en</strong>so uso <strong>de</strong> dicho término <strong>en</strong> .jerga informática.


328 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Existe un segundo método para inicializar un puntero, es mediante asignación dinámica <strong>de</strong><br />

memoria. Este método utiliza las funciones <strong>de</strong> asignación <strong>de</strong> memoria malloc ( ) , calloc ( ) ,<br />

reallot ( ) y free ( 1, y se analizará más a<strong>de</strong>lante <strong>en</strong> el capítulo sigui<strong>en</strong>te.<br />

10.2.3. Indirección <strong>de</strong> punteros<br />

Después <strong>de</strong> <strong>de</strong>finir una variable puntero, el sigui<strong>en</strong>te paso es inicializar el puntero y utilizarlo para<br />

direccionar algún dato específico <strong>en</strong> memoria. El uso <strong>de</strong> un puntero para obt<strong>en</strong>er el valor al que apunta,<br />

es <strong>de</strong>cir, su dato apuntado se d<strong>en</strong>omina indireccionar el puntero («<strong>de</strong>srefer<strong>en</strong>cia- el puntero»); para ello,<br />

se utiliza el operador <strong>de</strong> indirección * .<br />

int edad;<br />

int* p-edad;<br />

p-edad= &edad;<br />

*p-edad= 50;<br />

Las dos s<strong>en</strong>t<strong>en</strong>cias anteriores se <strong>de</strong>scrib<strong>en</strong> <strong>en</strong> la Figura 10.2. Si se <strong>de</strong>sea imprimir el valor <strong>de</strong> edad,<br />

se pue<strong>de</strong> utilizar la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia:<br />

printf ("%d",edad) ;/* imprime el valor <strong>de</strong> edad */<br />

También se pue<strong>de</strong> imprimir el valor <strong>de</strong> edad <strong>de</strong>refer<strong>en</strong>ciando el puntero a edad:<br />

printf ("%da',<br />

*p-edad); /*indirecciona p-edad */<br />

direcciones<br />

memoria<br />

t-<br />

~dxl <strong>en</strong><br />

120.000<br />

a-<br />

p-edad <strong>en</strong><br />

350.420<br />

-<br />

línea <strong>de</strong> memoria<br />

Figura 10.2. p-rdad conti<strong>en</strong>e la dirección <strong>de</strong> ( dd(i, i)-cTti,iri apunta a la variable edad<br />

El listado <strong>de</strong>l sigui<strong>en</strong>te programa muestra el concepto <strong>de</strong> creación, inicialización e indirección <strong>de</strong><br />

una variable puntero.<br />

#inclu<strong>de</strong> <br />

char c;<br />

int main0<br />

/* variable global <strong>de</strong> tipo carácter*/<br />

char *pc;<br />

pc = &c;<br />

for (c = 'A'; c


Punteros (apuntadores) 329<br />

La ejecución <strong>de</strong> este programa visualiza el alfabeto. La variable puntero pc es un puntero a una<br />

variable carácter. La línea pc = &c asigna a pc la dirección <strong>de</strong> la variable c (&c). El bucle for<br />

almac<strong>en</strong>a <strong>en</strong> c las letras <strong>de</strong>l alfabeto y la s<strong>en</strong>t<strong>en</strong>cia print f ("%c",*pc) ; visualiza el cont<strong>en</strong>ido <strong>de</strong> la<br />

variable apuntada por pc; c y pc se refier<strong>en</strong> a la misma posición <strong>en</strong> memoria. Si la variable c , que se<br />

almac<strong>en</strong>a <strong>en</strong> cualquier parte <strong>de</strong> la memoria, y pc , que apunta a esa misma posición, se refiere a los<br />

mismos <strong>datos</strong>, <strong>de</strong> modo que el cambio <strong>de</strong> una<br />

,<br />

variable <strong>de</strong>be afectar a la otra; pc y c se dice que son<br />

alias, <strong>de</strong>bido a que pc actúa como otro nombre <strong>de</strong> c.<br />

Valor <strong>de</strong> dirección <strong>de</strong>l<br />

puntero<br />

Dircccione5 O0 01 02 03 04 05 06 07<br />

Memoria<br />

Figura 10.3. pc' y c direccionan la misma posición <strong>de</strong> memoria.<br />

La Tabla 1 O. 1 resume los operadores <strong>de</strong> punteros.<br />

Tabla 10.1. Operadores <strong>de</strong> punteros.<br />

~~<br />

Operador<br />

&<br />

*<br />

*<br />

Propósito<br />

Obti<strong>en</strong>e la dirección <strong>de</strong> una variable.<br />

Define una variable como puntero.<br />

Obti<strong>en</strong>e el cont<strong>en</strong>ido <strong>de</strong> una variable puntero.<br />

Nota<br />

Son variables punteros aquellas que apuntan a la posición <strong>en</strong> don<strong>de</strong> otrds variablels <strong>de</strong> programa<br />

se almac<strong>en</strong>an.<br />

I<br />

10.2.4. Punteros y verificación <strong>de</strong> tipos I<br />

Los punteros se <strong>en</strong>lazan a tipos <strong>de</strong> <strong>datos</strong> específicos, <strong>de</strong> modo que C verificará si se asigna la dirección<br />

<strong>de</strong> un tipo <strong>de</strong> dato al tipo correcto <strong>de</strong> puntero. Así, por ejemplo, si se <strong>de</strong>fine un puntero a float, no se<br />

le pue<strong>de</strong> asignar la dirección <strong>de</strong> un carácter o un <strong>en</strong>tero. Por ejemplo, este segm<strong>en</strong>to <strong>de</strong> código no<br />

funcionará:<br />

I<br />

I<br />

float *fp;<br />

char c;<br />

fp = &c; /* no es válido */<br />

C no permite la asignación <strong>de</strong> la dirección <strong>de</strong> c a f p, ya que f p es una variable puntero que apunta<br />

a <strong>datos</strong> <strong>de</strong> tipo real, float.<br />

C requiere que las variables puntero direccion<strong>en</strong> realm<strong>en</strong>te variables <strong>de</strong>l mismo tipo <strong>de</strong> dato que<br />

está ligado a los punteros <strong>en</strong> sus <strong>de</strong>claraciones.


330 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

10.3. PUNTEROS null Y void<br />

Normalm<strong>en</strong>te un puntero inicializado a<strong>de</strong>cuadam<strong>en</strong>te apunta a alguna posición específica <strong>de</strong> la memoria.<br />

Sin embargo, un puntero no inicializado, como cualquier variable, ti<strong>en</strong>e un valor aleatorio hasta que se<br />

inicializa el puntero. En consecu<strong>en</strong>cia, será preciso asegurarse que las variables puntero utilic<strong>en</strong><br />

direcciones <strong>de</strong> memoria válida.<br />

Exist<strong>en</strong> dos tipos <strong>de</strong> punteros especiales muy utilizados <strong>en</strong> el tratami<strong>en</strong>to <strong>de</strong> sus programas: los<br />

punteros void y null (nulo).<br />

Un puntero nulo no apunta a ninguna parte -dato válido- <strong>en</strong> particular, es <strong>de</strong>cir, «un puntero nulo<br />

no direcciona ningún dato válido <strong>en</strong> memoria». Un puntero nulo se utiliza para proporcionar a un<br />

programa un medio <strong>de</strong> conocer cuando una variable puntero no direcciona a un dato válido. Para <strong>de</strong>clarar<br />

un puntero nulo se utiliza la macro NULL, <strong>de</strong>finida <strong>en</strong> los archivos <strong>de</strong> cabecera STDEF . H, STDIO. H,<br />

STDLIB. H y STRING. H. Se <strong>de</strong>be incluir uno o más <strong>de</strong> estos archivos <strong>de</strong> cabecera antes <strong>de</strong> que se pueda<br />

utilizar la macro NULL. Ahora bi<strong>en</strong>, se pue<strong>de</strong> <strong>de</strong>finir NULL <strong>en</strong> la parte superior <strong>de</strong> su programa (o <strong>en</strong> un<br />

archivo <strong>de</strong> cabecera personal) con la línea:<br />

#<strong>de</strong>fine NULL O<br />

Un sistema <strong>de</strong> inicializar una variable puntero a nulo es:<br />

char *p = NULL;<br />

Algunas funciones C también <strong>de</strong>vuelv<strong>en</strong> el valor NUL,L si se <strong>en</strong>cu<strong>en</strong>tra un error. Se pued<strong>en</strong> añadir test<br />

para el valor NULL comparando el puntero con NULL:<br />

char *p;<br />

p = malloc(l2l*sizeof(char));<br />

~f (p == NULL)<br />

I<br />

puts ("Error <strong>de</strong> asignación <strong>de</strong> memoria") ;<br />

o bi<strong>en</strong><br />

if (p != NULL) . ..<br />

/* este if es equival<strong>en</strong>te a : */<br />

if (p) . _ _<br />

Otra forma <strong>de</strong> <strong>de</strong>clarar un puntero nulo es asignar un valor <strong>de</strong> O. Por ejemplo,<br />

int *ptr = (int *) O; /* ptr es un puntero nulo */<br />

El mo<strong>de</strong>lo (casting) anterior ( int * 1, no es necesario, hay una conversión estándar <strong>de</strong> O a una<br />

variable puntero.<br />

int *ptr = O;<br />

Nunca se utiliza un puntero nulo para refer<strong>en</strong>ciar un valor. Como antes se ha com<strong>en</strong>tado, los<br />

punteros nulos se utilizan <strong>en</strong> un test condicional para <strong>de</strong>terminar si un puntero se ha inicializado. En el<br />

ejemplo<br />

if (ptr)<br />

printf("Va1or <strong>de</strong> la variable apuntada por ptr es: Xd\n",*ptr);<br />

se imprime un valor si el puntero es válido y no es un puntero nulo.<br />

Los punteros nulos se utilizan con frecu<strong>en</strong>cia <strong>en</strong> programas con arrays <strong>de</strong> punteros. Cada posición<br />

<strong>de</strong>l array se inicializa a NULL; <strong>de</strong>spués se reserva memoria dinámicam<strong>en</strong>te y se asigna a la posición<br />

correspondi<strong>en</strong>te <strong>de</strong>l array, la dirección <strong>de</strong> la memoria.<br />

En C se pue<strong>de</strong> <strong>de</strong>clarar un puntero <strong>de</strong> modo que apunte a cualquier tipo <strong>de</strong> dato, es <strong>de</strong>cir, no se<br />

asigna a un tipo <strong>de</strong> dato específico. El método es <strong>de</strong>clarar el puntero como un puntero vold *,<br />

d<strong>en</strong>ominado puntero g<strong>en</strong>érico.<br />

i<br />

i


Punteros (apuntadores) 331<br />

void *ptr; /* <strong>de</strong>clara un puntero void, punLero g<strong>en</strong>érico */<br />

El puntero ptr pue<strong>de</strong> direccionar cualquier posición <strong>en</strong> memoria, pero el puntero no está unido a<br />

un tipo <strong>de</strong> dato específico. De modo similar, los punteros void pued<strong>en</strong> direccionar una variable<br />

float, una char, o una posición arbitraria o una cad<strong>en</strong>a.<br />

Nota<br />

No confundir punteros void y NULL. Un puntero nulo no direcciona ningiín dato válido. Un<br />

puntero void direcciona <strong>datos</strong> <strong>de</strong> un tipo no especificado. Un puntero void se pue<strong>de</strong> igualar a<br />

nulo si no se direcciona ningún dato válido. Nulo es un valor; void es un tipo <strong>de</strong> dato.<br />

10.4. PUNTEROS A PUNTEROS<br />

Un puntero pue<strong>de</strong> apuntar a otra variable puntero. Este concepto se utiliza con mucha frecu<strong>en</strong>cia <strong>en</strong><br />

programas complejos <strong>de</strong> C. Para <strong>de</strong>clarar un puntero a un puntero se hace prece<strong>de</strong>r a la variable con<br />

dos asteriscos ( * * ) .<br />

En el ejemplo sigui<strong>en</strong>te ptr5 es un puntero a un puntero.<br />

int valor-e = 100;<br />

int *ptrl = &valor-e;<br />

int **ptr5 = &ptrl;<br />

ptri y ptr5 son punteros. ptrl apunta a la variable valor-e <strong>de</strong> tipo int. ptr5 conti<strong>en</strong>e la<br />

dirección <strong>de</strong> p t r 1. En la Figura 10.4 se muestran las <strong>de</strong>claraciones anteriores.<br />

Se pue<strong>de</strong> asignar valores a valor-e con cualquiera <strong>de</strong> las s<strong>en</strong>t<strong>en</strong>cias sigui<strong>en</strong>tes:<br />

valor-e = 95;<br />

*pt.rl= 105;<br />

**ptr5 = 99;<br />

ptrl 8080<br />

ptri 8082<br />

/* Asigna 105 d valor-c */<br />

/* Asignd 899 a valor-e */<br />

va 1 o r-e<br />

8000<br />

8081<br />

8080<br />

8083<br />

Figura 10.4. Un puntero a un puntero.<br />

Ejemplo<br />

char c = 'z';<br />

char* pc = &c;<br />

char** ppc = &pc;<br />

char*** pppc = &ppc;<br />

*** pppc = 'm'; /* cambia el valor <strong>de</strong> c a 'm' */<br />

Y


332 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

10.5. PUNTEROS Y ARRAYS<br />

Los arrays y punteros están fuertem<strong>en</strong>te relacionados <strong>en</strong> el l<strong>en</strong>guaje C. Se pued<strong>en</strong> direccionar arrays<br />

como si fueran punteros y punteros como si fueran arrays. La posibilidad <strong>de</strong> almac<strong>en</strong>ar y acce<strong>de</strong>r a<br />

punteros y arrays, implica que se pued<strong>en</strong> almac<strong>en</strong>ar cad<strong>en</strong>as <strong>de</strong> <strong>datos</strong> <strong>en</strong> elem<strong>en</strong>tos <strong>de</strong> arrays. Sin<br />

punteros eso no es posible, ya que no existe el tipo <strong>de</strong> dato cad<strong>en</strong>a (string) <strong>en</strong> C. No exist<strong>en</strong><br />

variables <strong>de</strong> cad<strong>en</strong>a. Únicam<strong>en</strong>te constantes <strong>de</strong> cad<strong>en</strong>a.<br />

10.5.1. Nombres <strong>de</strong> arrays como punteros<br />

Un nombre <strong>de</strong> un array es simplem<strong>en</strong>te un puntero. Supongamos que se ti<strong>en</strong>e la sigui<strong>en</strong>te <strong>de</strong>claración<br />

<strong>de</strong> un array:<br />

int lista[5] = 110, 20, 30, 40, 501;<br />

Mimor i u<br />

LO<br />

LO<br />

30<br />

40<br />

Figura 10.5. Un array almac<strong>en</strong>ado <strong>en</strong> memoria.<br />

Si se manda visualizar i i s t a [ O 1 se verá 1 O . Pero, ¿qué suce<strong>de</strong>rá si se manda visualizar * 1 is ta?<br />

Como un nombre <strong>de</strong> un array es un puntero, también se verá 1 O . Esto significa que<br />

lista + O apunta a lista[Ol<br />

lista + 1 apunta a lista[ll<br />

lista + 2 apunta a lista [2 I<br />

lista + 3 apunta a lista [3]<br />

lista + 4 apunta a lista[4]


-<br />

Punteros (apuntadores) 333<br />

Por consigui<strong>en</strong>te, para imprimir (visualizar), almac<strong>en</strong>ar o calcular un elem<strong>en</strong>to <strong>de</strong> un array, se pue<strong>de</strong><br />

utilizar notación <strong>de</strong> subíndices o notación <strong>de</strong> punteros. Dado que un nombre <strong>de</strong> un array conti<strong>en</strong>e la<br />

dirección <strong>de</strong>l primer elem<strong>en</strong>to <strong>de</strong>l array, se <strong>de</strong>be indireccionar el puntero para obt<strong>en</strong>er el valor <strong>de</strong>l<br />

elem<strong>en</strong>to.<br />

El nombre <strong>de</strong> un array es un puntero, conti<strong>en</strong>e la dirección <strong>en</strong> memoria <strong>de</strong> comi<strong>en</strong>zo <strong>de</strong> la secu<strong>en</strong>cia<br />

<strong>de</strong> elem<strong>en</strong>tos que forma el array. Es un puntero constante ya que no se pue<strong>de</strong> modificar, sólo se pue<strong>de</strong><br />

acce<strong>de</strong>r para in<strong>de</strong>xar a los elem<strong>en</strong>tos <strong>de</strong>l array. En el ejemplo se pone <strong>de</strong> manifiesto operaciones<br />

correctas y erróneas con nombres <strong>de</strong> array.<br />

float v[101;<br />

float *p;<br />

floa x = 100.5;<br />

int j;<br />

/* se in<strong>de</strong>xa a partir <strong>de</strong> v */<br />

for (j= 0; j


334 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

10.6. ARRAYS DE PUNTEROS<br />

Si se necesita reservar muchos punteros a muchos valores difer<strong>en</strong>tes, se pue<strong>de</strong> <strong>de</strong>clarar un arrup <strong>de</strong><br />

punteros. Un array <strong>de</strong> punteros es un array que conti<strong>en</strong>e como elem<strong>en</strong>tos punteros, cada uno <strong>de</strong> los<br />

cuales apunta a un tipo <strong>de</strong> dato específico. La línea sigui<strong>en</strong>te reserva un array <strong>de</strong> diez variables puntero<br />

a <strong>en</strong>teros:<br />

int *ptr[lOl; /* reserva un array <strong>de</strong> 10 punteros a <strong>en</strong>teros */<br />

La Figura 10.6 muestra cómo C organiza este array. Cada elem<strong>en</strong>to conti<strong>en</strong>e una dirección que<br />

apunta a otros valores <strong>de</strong> la memoria. Cada valor apuntado <strong>de</strong>be ser un <strong>en</strong>tero. Se pue<strong>de</strong> asignar a un<br />

elem<strong>en</strong>to <strong>de</strong> ptr una dirección, tal como para variables puntero no arrays. Así, por ejemplo,<br />

ptr[5] = &edad; /* ptr[5] apunta u. la dirección <strong>de</strong> edad */<br />

ptr[4] = NULL; /* ptr[4] no conti<strong>en</strong>e dirección alguna */<br />

Otro ejemplo <strong>de</strong> arrays <strong>de</strong> punteros, <strong>en</strong> este caso <strong>de</strong> caracteres es:<br />

char *puntos 1251 ; /* array <strong>de</strong> 25 punteros a carácter */<br />

De igual forma, se podría <strong>de</strong>clarar un puntero a un array <strong>de</strong> punteros a <strong>en</strong>teros.<br />

int * (*ptrlO)[ I ;<br />

y las operaciones paso a paso son:<br />

c<br />

(*ptrlO) es un puntero, ptrl0 PS un nombre <strong>de</strong> variable.<br />

(*ptrlO)[ 1 es un puntero a un array<br />

*(*ptrlO)[I<br />

es un puntero u. un array <strong>de</strong> punteros<br />

int *(*ptrlO)[] es un puntero un array <strong>de</strong> punteros <strong>de</strong> variables int<br />

Una matriz <strong>de</strong> número <strong>en</strong>teros, o reales, pue<strong>de</strong> verse como un array <strong>de</strong> punteros; <strong>de</strong> tantos elem<strong>en</strong>tos<br />

como filas t<strong>en</strong>ga la matriz, apuntando cada elem<strong>en</strong>to <strong>de</strong>l array a un array <strong>de</strong> <strong>en</strong>teros o reales, <strong>de</strong> tantos<br />

elem<strong>en</strong>tos como columnas.<br />

í41<br />

Cada elem<strong>en</strong>to pue<strong>de</strong> apuntar a un <strong>en</strong>tero<br />

memoria<br />

Figura 10.6. Un array <strong>de</strong> 10 punteros a <strong>en</strong>teros.


Punteros (apuntadores) 335<br />

10.6.1. Inicialización <strong>de</strong> un array <strong>de</strong> punteros a cad<strong>en</strong>as<br />

La inicialización <strong>de</strong> un array <strong>de</strong> punteros a cad<strong>en</strong>as se pue<strong>de</strong> realizar con una <strong>de</strong>claración similar a ésta:<br />

char *nombres-meses [121 = { "Enero","Febrero", "Marzo",<br />

"Abri 1 I' , "Mayo", "Junio",<br />

" Ju 1 io" , "Ago s t 0'' , I' S ep t i embr e " ,<br />

"Octubre","Noviembre",<br />

"Diciembre" } ;<br />

10.7. PUNTEROS DE CADENAS<br />

Los punteros se pued<strong>en</strong> utilizar <strong>en</strong> lugar <strong>de</strong> índices <strong>de</strong> arrays. Considérese la sigui<strong>en</strong>te <strong>de</strong>claración <strong>de</strong> un<br />

array <strong>de</strong> caracteres que conti<strong>en</strong>e las veintiséis letras <strong>de</strong>l alfabeto internacional (no se consi<strong>de</strong>ra la fi).<br />

char alfabeto [27] = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ";<br />

Declaremos ahora p un puntero a char<br />

char *p;<br />

Se establece que P apunta al primer carácter <strong>de</strong> alfabeto escribi<strong>en</strong>do<br />

p = &alfabeto[Ol; /* o también p = alfabeto */<br />

<strong>de</strong> modo que si escribe la s<strong>en</strong>t<strong>en</strong>cia<br />

printf ("%c \n",*p) ;<br />

se visualiza la letra A, ya que p apunta al primer elem<strong>en</strong>to <strong>de</strong> la cad<strong>en</strong>a. Se pue<strong>de</strong> hacer también<br />

p = halfabetotl51;<br />

<strong>de</strong> modo que p apuntará al carácter 16" (la letra Q). Sin embargo, no se pue<strong>de</strong> hacer<br />

p = &alfabeto;<br />

ya que alfabeto es un array cuyos elem<strong>en</strong>tos son <strong>de</strong> tipo char, y se produciría un error al compilar<br />

(tipo <strong>de</strong> asignación es incompatible).<br />

. I<br />

______L<br />

ABCDEFGHIJKLM<br />

Figura 10.7. Un puntero a iilf.ih=t o I 1 ',I .<br />

Es posible, <strong>en</strong>tonces, consi<strong>de</strong>rar dos tipos <strong>de</strong> <strong>de</strong>finiciones <strong>de</strong> cad<strong>en</strong>a:<br />

char cad<strong>en</strong>al[]='Hola viejo mundo"; /* array conti<strong>en</strong>e una cad<strong>en</strong>a */<br />

char *cptr = "C a su alcance"; /* puntero a cad<strong>en</strong>a, el sistema<br />

reserva memoria para la cad<strong>en</strong>a*/<br />

10.7.1. Punteros versus arrays<br />

El sigui<strong>en</strong>te programa implem<strong>en</strong>ta una función para contar el número <strong>de</strong> caracteres <strong>de</strong> una cad<strong>en</strong>a. En<br />

el primer programa, la cad<strong>en</strong>a se <strong>de</strong>scribe utilizando un array, y <strong>en</strong> el segundo, se <strong>de</strong>scribe utilizando un<br />

puntero.


336 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

/* Implem<strong>en</strong>tación con un array */<br />

#inclu<strong>de</strong> <br />

int longitud(const char cad[] 1 ;<br />

void main()<br />

t<br />

static char cad[ I = "Universidad Pont i.ficid";<br />

printf("La longitud <strong>de</strong> %s es %d caracteres\n",<br />

cad, longitud(cad1 ) ;<br />

1<br />

int longitud(const char cad[])<br />

i<br />

int posicion = O;<br />

while (cad[posicion] != '\O')<br />

{<br />

posícion++;<br />

1<br />

return posicion;<br />

1<br />

El segundo programa utiliza un puntero para la función que cu<strong>en</strong>ta los caracteres <strong>de</strong> la cad<strong>en</strong>a.<br />

A<strong>de</strong>más, utiliza la aritmética <strong>de</strong> punteros para in<strong>de</strong>xar los caracteres. El bucle termina cuando llega al<br />

último carácter, que es el <strong>de</strong>limitador <strong>de</strong> una cad<strong>en</strong>a: \ O.<br />

/* Implem<strong>en</strong>tación con un puntero */<br />

#inclu<strong>de</strong> istdio.h><br />

int longitud(const char*);<br />

void main()<br />

{<br />

static char cad[] = "Universidad Pontificia";<br />

print€ ("La longitud <strong>de</strong> %s es %d cdracteres\n",<br />

cad, longitud (cad ) ;<br />

1<br />

int longitud(const char* cad)<br />

{<br />

1<br />

int cu<strong>en</strong>ta = O;<br />

while (*cad++) cu<strong>en</strong>ta++;<br />

return cu<strong>en</strong>ta;<br />

En ambos casos se imprimirá:<br />

La longitud <strong>de</strong> Universidad Pontificia es 22 caracteres<br />

Comparaciones <strong>en</strong>tre punteros y arrays <strong>de</strong> punteros<br />

int *ptrl[ 1 ; /* Array <strong>de</strong> punteros a int */<br />

int (*ptr21 [ 1 ; /* Puntero a un array <strong>de</strong> elem<strong>en</strong>tos int */<br />

int *(*ptr3)[I; /* Puntero a un array <strong>de</strong> punteros a int */<br />

10.8. ARITMÉTICA DE PUNTEROS<br />

Al contrario que un nombre <strong>de</strong> array, que es un puntero constante y no se pue<strong>de</strong> modificar, un puntero<br />

es una variable que se pue<strong>de</strong> modificar. Como consecu<strong>en</strong>cia, se pued<strong>en</strong> realizar ciertas operaciones<br />

aritméticas sobre punteros.


Punteros (apuntadores) 337<br />

A un puntero se le pue<strong>de</strong> sumar o restar un <strong>en</strong>tero n; esto hace que apunte n posiciones a<strong>de</strong>lante, o<br />

atrás <strong>de</strong> la actual. Una variable puntero pue<strong>de</strong> modificarse para que cont<strong>en</strong>ga una dirección <strong>de</strong> memoria<br />

n posiciones a<strong>de</strong>lante o atrás. Observe el sigui<strong>en</strong>te fragm<strong>en</strong>to:<br />

int v[lO] ;<br />

int *p;<br />

p = v;<br />

(v+4); /* apunta al 5" elem<strong>en</strong>to */<br />

p = p+6; /* conti<strong>en</strong>e la dirección <strong>de</strong>l 7: elem<strong>en</strong>to */<br />

A una variable puntero se le pue<strong>de</strong> aplicar el operador ++, o el operador - - . Esto hace que cont<strong>en</strong>ga<br />

la dirección <strong>de</strong>l sigui<strong>en</strong>te, o anterior elem<strong>en</strong>to. Por ejemplo:<br />

float m[201;<br />

float *r;<br />

r = m;<br />

r++; /* conti<strong>en</strong>e la dirección <strong>de</strong>l elem<strong>en</strong>to sigui<strong>en</strong>te */<br />

Recuér<strong>de</strong>se que un puntero es una dirección, por consigui<strong>en</strong>te, sólo aquellas operaciones <strong>de</strong> «s<strong>en</strong>tido<br />

común» son legales. No ti<strong>en</strong>e s<strong>en</strong>tido, por ejemplo, sumar o restar una constante <strong>de</strong> coma flotante.<br />

Operaciones no vadas con punteros<br />

No se pued<strong>en</strong> sumar dos punteros.<br />

No se pued<strong>en</strong> multiplicar dos punteros.<br />

No se pued<strong>en</strong> dividir dos punteros.<br />

Ejemplo 10.3<br />

Si p apunta a la letra A <strong>en</strong> alfabeto, si se escribe<br />

p = p+l;<br />

<strong>en</strong>tonces p apunta a la letra B.<br />

Se pue<strong>de</strong> utilizar esta técnica para explorar cada elem<strong>en</strong>to <strong>de</strong> al f abeto sin utilizar una variable <strong>de</strong><br />

índice. Un ejemplo pue<strong>de</strong> ser<br />

p = &alfabeto[Ol;<br />

for (i = O; i < strl<strong>en</strong>(a1fabeto); i++)<br />

i<br />

printf ("%c",*p);<br />

p = p+l;<br />

I;<br />

Las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong>l interior <strong>de</strong>l bucle se pued<strong>en</strong> sustituir por<br />

printf ("%cc", *p++);<br />

9<br />

El ejemplo anterior con el bucle for pue<strong>de</strong> ser abreviado, haci<strong>en</strong>do uso <strong>de</strong> la característica <strong>de</strong><br />

terminador nulo al final <strong>de</strong> la cad<strong>en</strong>a. Utilizando la s<strong>en</strong>t<strong>en</strong>cia while para realizar el bucle y poni<strong>en</strong>do<br />

la condición <strong>de</strong> terminación <strong>de</strong> nulo o byte cero al final <strong>de</strong> la cad<strong>en</strong>a. Esto elimina la necesidad <strong>de</strong>l bucle<br />

for y su variable <strong>de</strong> control. El bucle for se pue<strong>de</strong> sustituir por<br />

while (*p) printf ("%c",*p++);<br />

mi<strong>en</strong>tras que *p toma un valor <strong>de</strong> carácter distinto <strong>de</strong> cero, el bucle while se ejecuta, el carácter se<br />

imprime y p se increm<strong>en</strong>ta para apuntar al sigui<strong>en</strong>te carácter. Al alcanzar el byte cero al final <strong>de</strong> la<br />

cad<strong>en</strong>a, *p toma el valor <strong>de</strong> I \ O o cero. El valor cero hace que el bucle termine.


338 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

10.8.1. Una aplicación <strong>de</strong> punteros: conversión <strong>de</strong> caracteres<br />

El sigui<strong>en</strong>te programa muestra un puntero que recorre una cad<strong>en</strong>a <strong>de</strong> caracteres y convierte cualquier<br />

carácter <strong>en</strong> minúsculas a caracteres mayúsculas.<br />

/* Utiliza un puntero como índice <strong>de</strong> un array <strong>de</strong> caracteres<br />

y convierte caracteres miniísculas a mayúsculas<br />

*/<br />

#inclu<strong>de</strong> istdio.h><br />

#inclu<strong>de</strong> <br />

void main()<br />

I<br />

i<br />

char *p;<br />

char Cad<strong>en</strong>aTextoí811;<br />

puts ("Introduzca cad<strong>en</strong>a a convertir : " ) ;<br />

gets(Cad<strong>en</strong>aTexto);<br />

/* p apunta al primer carácter <strong>de</strong> la cad<strong>en</strong>a */<br />

p = &Cad<strong>en</strong>aTexto[OJ; /* equivale a p = Cad<strong>en</strong>aTexto */<br />

/* Repetir mi<strong>en</strong>tras *p no sea cero */<br />

while (*p)<br />

i<br />

/* restar 32, constante <strong>de</strong> código ASCII */<br />

if ((*p >= 'a') && (*p


Punteros (apuntadores) 339<br />

10.9. PUNTEROS CONSTANTES FRENTE A PUNTEROS A CONSTANTES<br />

Ya está familiarizado con punteros constantes, como es el caso <strong>de</strong> un nombre <strong>de</strong> un array. Un puntero<br />

constante es un puntero que no se pue<strong>de</strong> cambiar, pero que los <strong>datos</strong> apuntados por el puntero pued<strong>en</strong><br />

ser cambiados. Por otra parte, un puntero a una constante se pue<strong>de</strong> modificar para apuntar a una<br />

constante difer<strong>en</strong>te, pero los <strong>datos</strong> apuntados por el puntero no se pued<strong>en</strong> cambiar.<br />

10.9.1. Punteros constantes<br />

Para crear un puntero constante difer<strong>en</strong>te <strong>de</strong> un nombre <strong>de</strong> un array, se <strong>de</strong>be utilizar el sigui<strong>en</strong>te formato:<br />

*const = ;<br />

Como ejemplo <strong>de</strong> una <strong>de</strong>finición <strong>de</strong> punteros <strong>de</strong> constantes, considér<strong>en</strong>se las sigui<strong>en</strong>tes s<strong>en</strong>t<strong>en</strong>cias:<br />

int x;<br />

int y;<br />

int *const pl = &x;<br />

pl es un puntero <strong>de</strong> constantes que apunta a x, por lo que pl es una constante, pero *pl es una variable.<br />

Por consigui<strong>en</strong>te, se pue<strong>de</strong> cambiar el valor <strong>de</strong> *pl , pero no pl .<br />

Por ejemplo, la sigui<strong>en</strong>te asignación es legal, dado que se cambia el cont<strong>en</strong>ido <strong>de</strong> memoria a don<strong>de</strong><br />

apunta PI, pero no el puntero <strong>en</strong> sí.<br />

"PI = y;<br />

Por otra parte, la sigui<strong>en</strong>te asignación no es legal, ya que se int<strong>en</strong>ta cambiar el valor <strong>de</strong>l puntero<br />

pl = &y;<br />

El sistema para crear un puntero <strong>de</strong> constante a una cad<strong>en</strong>a es:<br />

char *const nombre = "Luis";<br />

nombre no se pue<strong>de</strong> modificar para apuntar a una cad<strong>en</strong>a difer<strong>en</strong>te <strong>en</strong> memoria. Por consigui<strong>en</strong>te,<br />

*nombre = 'C';<br />

es legal, ya que se modifica el dato apuntado por nombre (cambia el primer carácter). Sin embargo, no<br />

es legal:<br />

nombre = &Otra-Cad<strong>en</strong>a;<br />

dado que se int<strong>en</strong>ta modificar el propio puntero<br />

10.9.2. Punteros a constantes<br />

El formato para <strong>de</strong>finir un puntero a una constante es:<br />

const * =<br />

;<br />

Algunos ejemplos:<br />

const int x = 25;<br />

const int y = 50;<br />

const int *pl = &x;


340 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

<strong>en</strong> los que pl se <strong>de</strong>fine como un puntero a la constante x. Los <strong>datos</strong> son constantes y no el puntero; <strong>en</strong><br />

consecu<strong>en</strong>cia, se pue<strong>de</strong> hacer que pl apunte a otra constante.<br />

pl = &y;<br />

Sin embargo, cualquier int<strong>en</strong>to <strong>de</strong> cambiar el cont<strong>en</strong>ido almac<strong>en</strong>ado <strong>en</strong> la posición <strong>de</strong> memoria a<br />

don<strong>de</strong> apunta pl creará un error <strong>de</strong> compilación. Así, la sigui<strong>en</strong>te s<strong>en</strong>t<strong>en</strong>cia no se compilará<br />

correctam<strong>en</strong>te:<br />

"pl = 15;<br />

Nota<br />

Una <strong>de</strong>finición <strong>de</strong> un puntero constante ti<strong>en</strong>e la palabra reservada const <strong>de</strong>lante <strong>de</strong>l nombre <strong>de</strong>l<br />

puntero, mi<strong>en</strong>tras que el puntero a una <strong>de</strong>finición constante requiere que la palabra reservada<br />

COA&& se sitúe antes <strong>de</strong>l tipo <strong>de</strong> dato. Así, la <strong>de</strong>finición <strong>en</strong> el pnmer caso se pue<strong>de</strong> leer como<br />

«punteros constante o <strong>de</strong> constante», mi<strong>en</strong>tras que <strong>en</strong> el segundo caso la <strong>de</strong>finición se lee «puntero<br />

a tipo constante <strong>de</strong> dato».<br />

La creación <strong>de</strong> un puntero a una constante cad<strong>en</strong>a se pue<strong>de</strong> hacer <strong>de</strong>l modo sigui<strong>en</strong>te:<br />

const char *apellido = "Remirez";<br />

En el prototipo <strong>de</strong> la sigui<strong>en</strong>te función se <strong>de</strong>clara el argum<strong>en</strong>to como puntero a una constante:<br />

float cargo (const float "v);<br />

10.9.3. Punteros constantes a constantes<br />

El último caso a consi<strong>de</strong>rar es crear punteros constantes a constantes utilizando el formato sigui<strong>en</strong>te:<br />

const *const =<br />

;<br />

Esta <strong>de</strong>finición se pue<strong>de</strong> leer como


6"<br />

Punteros (apuntadores) 7<br />

I<br />

I<br />

Ejemplo 10.4<br />

Un puntero a una constante es difer<strong>en</strong>te <strong>de</strong> un puntero constante. El sigui<strong>en</strong>te ejemplo muestra las<br />

difer<strong>en</strong>cias.<br />

/*<br />

Este trozo <strong>de</strong> código <strong>de</strong>fine cuatro variables:<br />

un puntero p; un puntero constante cp; un puntero pc a una<br />

constante y un puntero constdnte cpc a una constante<br />

*/<br />

int *p;<br />

++ (*P);<br />

++p;<br />

int *const cp;<br />

++ (*CP);<br />

++cp;<br />

const int * pc;<br />

++ (*pc);<br />

++pc ;<br />

/* puntero a un int */<br />

/* increm<strong>en</strong>to <strong>de</strong>l <strong>en</strong>tero *p *I<br />

/* increm<strong>en</strong>ta un puntero p */<br />

/* puntero constante a un int */<br />

/* increm<strong>en</strong>ta el <strong>en</strong>tero *cp */<br />

/* no válido: puntero cp es constante */<br />

/* puntero a una constante int */<br />

/* no válido: int * pc es constante */<br />

/* increm<strong>en</strong>ta puntero pc */<br />

const int * const cpc; /* puntero constante a constante int */<br />

++ (*cpc); /* no válido: int *cpc es constante */<br />

++cpc; /* no válido: puntero cpc es constante */<br />

Regla<br />

o <strong>en</strong> blanco no es significativo <strong>en</strong> la <strong>de</strong>claracidn <strong>de</strong> punteros. L<br />

son equival<strong>en</strong>tes:<br />

int* p;<br />

int * p;<br />

int *p;<br />

10.10. PUNTEROS COMO ARGUMENTOS DE FUNCIONES<br />

Con frecu<strong>en</strong>cia se <strong>de</strong>sea que una función calcule y <strong>de</strong>vuelva más <strong>de</strong> un valor, o bi<strong>en</strong> se <strong>de</strong>sea que una<br />

función modifique las variables que se pasan como argum<strong>en</strong>tos. Cuando se pasa una variable a una<br />

función @aso por valor) no se pue<strong>de</strong> cambiar el valor <strong>de</strong> esa variable. Sin embargo, si se pasa un puntero<br />

a una variable a una función (paso por direccicín) se pue<strong>de</strong> cambiar el valor <strong>de</strong> la variable.<br />

Cuando una variable es local a una función, se pue<strong>de</strong> hacer la variable visible a otra función<br />

pasándola como argum<strong>en</strong>to. Se pue<strong>de</strong> pasar un puntero a una variable local como argum<strong>en</strong>to y cambiar<br />

la variable <strong>en</strong> la otra función.<br />

Consi<strong>de</strong>re la sigui<strong>en</strong>te <strong>de</strong>finición <strong>de</strong> la función ~ncrern<strong>en</strong>tar~ ( , que increm<strong>en</strong>ta un <strong>en</strong>tero <strong>en</strong> 5:<br />

void Increm<strong>en</strong>tari(int *i)<br />

i<br />

*i += 5;<br />

i<br />

La llamada a esta función se realiza pasando una dirección que itilice esa función. Por ejemplo,<br />

para llamar a la función 1ncrem<strong>en</strong>tar5 ( ) utilice:<br />

int i;<br />

i = 10;<br />

Increm<strong>en</strong>tar5 (&i) ;


342 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Es posible mezclar paso por refer<strong>en</strong>cia y por valor. Por ejemplo, la función f uncl <strong>de</strong>finida como<br />

void funcl(int *s, int t)<br />

{<br />

}<br />

*S = 6;<br />

t = 25;<br />

y la invocación a la función podría ser:<br />

int i, j;<br />

i= 5;<br />

j = 7;<br />

funcl (&i, j) ; /*llamada a funcl */<br />

Cuando se retorna <strong>de</strong> la función tunc1 tras su ejecución, i será igual a 6 y j seguirá si<strong>en</strong>do 7, ya<br />

que se pasó por valor. El paso <strong>de</strong> un nombre <strong>de</strong> array a una función es lo mismo que pasar un puntero<br />

al array. Se pued<strong>en</strong> cambiar cualquiera <strong>de</strong> los elem<strong>en</strong>tos <strong>de</strong>l array. Cuando se pasa un elem<strong>en</strong>to a una<br />

función, sin embargo, el elem<strong>en</strong>to se pasa por valor. En el ejemplo<br />

int lista[] = {l, 2, 31;<br />

func(lista[l], listaL21) ;<br />

ambos elem<strong>en</strong>tos se pasan por valor.<br />

En C, por <strong>de</strong>fecto, el paso <strong>de</strong> parámetros se hace por valor. C no ti<strong>en</strong>e parhetros por refer<strong>en</strong>cia,<br />

hay que emularlo mediante el paso <strong>de</strong> la dirección <strong>de</strong> una variable, utilizando punteros <strong>en</strong> los<br />

argum<strong>en</strong>tos <strong>de</strong> la función.<br />

En el sigui<strong>en</strong>te ejemplo, se crea una <strong>estructura</strong> para apuntar las temperaturas más alta y más baja <strong>de</strong><br />

un día <strong>de</strong>terminado.<br />

struct temperatura {<br />

float alta;<br />

float ba j a ;<br />

1;<br />

Un caso típico podría ser almac<strong>en</strong>ar las lecturas <strong>de</strong> un termómetro conectado <strong>de</strong> algún modo posible<br />

a una computadora. Una función clave <strong>de</strong>l programa lee la temperatura actual y modifica el miembro<br />

a<strong>de</strong>cuado, alta o baja, <strong>en</strong> una <strong>estructura</strong> temperatura <strong>de</strong> la que se pasa la dirección <strong>de</strong>l argum<strong>en</strong>to<br />

a un parámetro puntero.<br />

void registrotemp(struct temperatura *t)<br />

i<br />

float actual ;<br />

i<br />

leertempactual(actud1);<br />

if (actual > t -> alta)<br />

t -> alta = actual;<br />

else if (actual < t -> baja)<br />

t -> baja = actual;<br />

La llamada a la función se pue<strong>de</strong> hacer con estas s<strong>en</strong>t<strong>en</strong>cias:<br />

struct temperatura tmp;<br />

registrotemp(&tmp);


Punteros (apuntadores) 343<br />

10.1 1. PUNTEROS A FUNCIONES<br />

Hasta este mom<strong>en</strong>to se han analizado punteros a <strong>datos</strong>. Es posible <strong>de</strong>clarar punteros a cualquier tipo <strong>de</strong><br />

variables, <strong>estructura</strong> o array. De igual modo, las funciones pued<strong>en</strong> <strong>de</strong>clarar parámetros punteros para<br />

permitir que s<strong>en</strong>t<strong>en</strong>cias pas<strong>en</strong> las direcciones <strong>de</strong> los argum<strong>en</strong>tos a esas funciones.<br />

Es posible también crear punteros que apunt<strong>en</strong> a funciones. En lugar <strong>de</strong> direccionar <strong>datos</strong>, los<br />

punteros <strong>de</strong> funciones apuntan a código ejecutable. Al igual que los <strong>datos</strong>, las funciones se almac<strong>en</strong>an<br />

<strong>en</strong> memoria y ti<strong>en</strong><strong>en</strong> direcciones iniciales. En C se pued<strong>en</strong> asignar las direcciones iniciales <strong>de</strong> funciones<br />

a punteros. Tales funciones se pued<strong>en</strong> llamar <strong>en</strong> un modo indirecto, es <strong>de</strong>cir, mediante un puntero cuyo<br />

valor es igual a la dirección inicial <strong>de</strong> la función <strong>en</strong> cuestión.<br />

La sintaxis g<strong>en</strong>eral para la <strong>de</strong>claración <strong>de</strong> un puntero a una función es:<br />

Tipo-<strong>de</strong>-retorno (*PunteroFuncion) ();<br />

Este formato indica al cornpilador que Punt eroFunci on es un puntero a una función que <strong>de</strong>vuelve<br />

el tipo Tipo-<strong>de</strong>-retorno y ti<strong>en</strong>e una lista <strong>de</strong> parámetros.<br />

Un puntero a una función es simplem<strong>en</strong>te un puntero cuyo valor es la dirección <strong>de</strong>l nombre <strong>de</strong> la<br />

función. Dado que el nombre es, <strong>en</strong> sí mismo, un puntero; un puntero a una función es un puntero a un<br />

puntero constante.<br />

Por ejemplo:<br />

Figura 10.9. Puntero a función.<br />

int f(int); /* <strong>de</strong>clara la función f */<br />

int (*pf)(int);<br />

/* <strong>de</strong>fine puntero pf o. función int con argum<strong>en</strong>to<br />

int */<br />

pf = f; /* asigno. la dirección <strong>de</strong> f a pf */<br />

Ejemplo 10.5<br />

double (*fp) (int n);<br />

float (*p) (int i, int j);<br />

void (*sort) (int* ArrayEnt, unsigned n);<br />

unsigned ("search) (int BuscarClave,int* ArrayEnt,unsigned n);<br />

El primer id<strong>en</strong>tificador, f p , apunta a una función que <strong>de</strong>vuelve un tipo double y ti<strong>en</strong>e un Único<br />

parámetro <strong>de</strong> tipo int . El segundo puntero, p , apunta a una función que <strong>de</strong>vuelve un tipo float y<br />

acepta dos parámetros <strong>de</strong> tipo int . El tercer puntero, sort, es un puntero a una función que<br />

<strong>de</strong>vuelve un tipo void y toma dos parámetros: un puntero a int y un tipo unsigned. Por Último,<br />

search es un puntero a una función que <strong>de</strong>vuelve un tipo unsigned y ti<strong>en</strong>e tres parámetros: un int ,<br />

un puntero a int y un unsigned.<br />

10.11.1. Inicialización <strong>de</strong> un puntero a una función<br />

La sintaxis g<strong>en</strong>eral para inicializar un puntero a una función es:


344 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

PunteroFuncion = unaFuncion<br />

La función asignada <strong>de</strong>be t<strong>en</strong>er el mismo tipo <strong>de</strong> retorno y lista <strong>de</strong> parámetros que el puntero a<br />

función; <strong>en</strong> caso contrario, se producirá un error <strong>de</strong> compilación. Así, por ejemplo, un puntero qf a una<br />

función double:<br />

double calculo (int* v; unsigned n); /* prototipo <strong>de</strong> función */<br />

double (*qf) (int*, unsigned); /* puntero a función */<br />

int r[ll] = {3,5,6,7,1,7,3,34,5,11,44};<br />

double x;<br />

qf = calculo; /* asigna dirección <strong>de</strong> la función */<br />

x = qf(r,ll); /* llamada a la función con el puntero a función */<br />

Algunas <strong>de</strong> las funciones <strong>de</strong> la biblioteca, tales como qsort ( 1, requiere pasar un argum<strong>en</strong>to que<br />

consta <strong>de</strong> un puntero a una función. Se <strong>de</strong>be pasar a qsort un puntero <strong>de</strong> función que apunta a una<br />

función.<br />

Ejemplo 10.6<br />

Se <strong>de</strong>sea ord<strong>en</strong>ar un array <strong>de</strong> números reales, la ord<strong>en</strong>ación se va a realizar con lafincicín qsort ( I .<br />

Esta función ti<strong>en</strong>e un parámetro que es un puntero a función <strong>de</strong>l tipo int ( * ) (const void", const<br />

void* 1. Se necesita un función <strong>de</strong> comparación, que <strong>de</strong>vuelva negativo si primer argum<strong>en</strong>to es m<strong>en</strong>or<br />

que el segundo, O si son iguales y positivo si es mayor. A continuación se escribe el programa:<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int compara-float(const void* a, const void* b); /* prototipo <strong>de</strong> función<br />

<strong>de</strong> comparación */<br />

float v[]= {34.5, -12.3, 4.5, 9.1, -2.5, 18.0, lo., 5.5);<br />

int main( )<br />

t<br />

int j, n;<br />

int (*pf)(const void*,const void*); /* puntero a función */<br />

I<br />

n = sizeof(v)/sizeof(v[O]); /* numero <strong>de</strong> elem<strong>en</strong>tos */<br />

printf ( "\n Numero <strong>de</strong> elem<strong>en</strong>tos : %d\n" , n) ;<br />

pf = compara-float;<br />

qsort( (void*)v,n,sizeof(v[O]),pf);/* Llamada a función <strong>de</strong><br />

biblioteca. */<br />

for (j = O; j < n; j++)<br />

printf('%.2f 'I, vlj]);<br />

puts("\n Pulsa cualquier tecla para continuar. ...");<br />

j = getchar() ;<br />

return O;<br />

int compara-float(const void *a, const void *b)<br />

{ float *x, *y;<br />

x = (float*)a; y = (float*)b;<br />

return(*x - *y);


Punteros (apuntadores) 345<br />

Ejemplo 10.7<br />

Supongamos un puntero p a una función tul como<br />

íloat (*p) (int i, int j) ;<br />

a continuación se pue<strong>de</strong> asignar la dirección <strong>de</strong> la función ejemplo:<br />

float ejemplo(int i, int j)<br />

{<br />

return 3.14159 * i * i + j;<br />

I<br />

al puntero p escribi<strong>en</strong>do<br />

p = ejemplo;<br />

Después <strong>de</strong> esta asignación se pue<strong>de</strong> escribir la sigui<strong>en</strong>te llamada a la función:<br />

(*P) (12,45)<br />

Su efecto es el mismo que<br />

e j emplo ( 12,4 5 )<br />

También se pue<strong>de</strong> omitir el asterisco (así como los paréntesis) <strong>en</strong> la llamada ( *p) ( 12,45 ) :<br />

convirtiéndose <strong>en</strong> esta otra llamada.<br />

p (12,45)<br />

La utilidad <strong>de</strong> lasfunciones a punteros se ve más claram<strong>en</strong>te si se imagina un programa gran<strong>de</strong>, al<br />

principio <strong>de</strong>l cual se <strong>de</strong>sea elegir una <strong>en</strong>tre varias funciones, <strong>de</strong> modo que la función elegida se llama,<br />

<strong>en</strong>tonces, muchas veces. Mediante un puntero, la elección sólo se hace una vez: <strong>de</strong>spués <strong>de</strong> asignar (la<br />

dirección <strong>de</strong>) la función seleccionada a un puntero y a continuación se pue<strong>de</strong> llamar a través <strong>de</strong> ese<br />

puntero.<br />

Los punteros a funciones también permit<strong>en</strong> pasar una funcicín como un argum<strong>en</strong>to a otra función.<br />

Para pasar el nombre <strong>de</strong> una función como un argum<strong>en</strong>to función, se especifica el nombre <strong>de</strong> la función<br />

como argum<strong>en</strong>to. Supongamos que se <strong>de</strong>sea pasar la función m i f unc ( ) a la función suf unc ( ) . El<br />

código sigui<strong>en</strong>te realiza las tareas anteriores:<br />

void sufunc(int (*f) 0) ; /* prototipo <strong>de</strong> sufunc */<br />

int mifunc(int i); /* prototipo <strong>de</strong> mifunc */<br />

void main0<br />

i<br />

1<br />

sufunc (mifunc);<br />

int mifunc(int i)<br />

return 5*i;<br />

1<br />

En la función llamada se <strong>de</strong>clara la función pasada como un puntero función.<br />

void sufunc(int (*f) 0)<br />

{<br />

1<br />

...<br />

j = f(5);<br />

...


+-<br />

346 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Como ejemplo practico veamos cómo escribir una función g<strong>en</strong>eral que calcule la suma <strong>de</strong> algunos<br />

valores, es <strong>de</strong>cir,<br />

f(1) + f(2) + ... + f(n)<br />

para cualquier función f que <strong>de</strong>vuelva el tipo double y con un argum<strong>en</strong>to int . Diseñaremos una<br />

función funcsuma que ti<strong>en</strong>e dos argum<strong>en</strong>tos: n, el número <strong>de</strong> términos <strong>de</strong> la suma, yf, la función<br />

utilizada. Así pues, la función funcsuma se va a llamar dos veces, y va a calcular la suma <strong>de</strong><br />

inversos(k) = 1.0/k<br />

cuadrados(k1 = k<br />

{para k = 1, 2, 3, 4, 5)<br />

{para k = 1, 2, 3}<br />

El programa sigui<strong>en</strong>te muestra la función funcsuma, que utiliza la función f <strong>en</strong> un caso para<br />

inversos y <strong>en</strong> otro para cuadrados.<br />

#inclu<strong>de</strong> <br />

/* prototipos <strong>de</strong> funciones */<br />

double inversos (int k) ;<br />

double cuadrados(int k);<br />

double funcsuma(int n, double (*f)(int k) );<br />

int main( )<br />

i<br />

printf("Suma <strong>de</strong> cinco inversos: %.31f \n",funcsuma(5,inversos));<br />

printf("Suma <strong>de</strong> tres cuadrados: 8.31f \n",funcsuma(3,cuadrados));<br />

return O;<br />

double funcsuma(int n, double (*f) (int k))<br />

1<br />

double s = O;<br />

int i;<br />

for (i = 1; i


10.1 1.2. Aplicación <strong>de</strong> punteros a función para ord<strong>en</strong>ación<br />

Punteros (apuntadores) 347<br />

Algunas <strong>de</strong> las funciones <strong>de</strong> la biblioteca, tal como qsort ( ) o bsearch( ) , requier<strong>en</strong> pasar un<br />

argum<strong>en</strong>to que consta <strong>de</strong> un puntero a una función. Se <strong>de</strong>be pasar a ambas, qsort ( ) y bsearch ( ) , un<br />

puntero <strong>de</strong> función que apunta hacia una función que se <strong>de</strong>be <strong>de</strong>finir. qsort ( ) utiliza el algoritmo <strong>de</strong><br />

ord<strong>en</strong>ación rápida (quicksort) para ord<strong>en</strong>ar un array <strong>de</strong> cualquier tipo <strong>de</strong> dato. bsearch ( ) utiliza la<br />

búsqueda binaria para <strong>de</strong>terminar si un elem<strong>en</strong>to está <strong>en</strong> un array. La función que <strong>de</strong>be <strong>de</strong> proporcionarse<br />

es para realizar comparaciones <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong> array. En el programa sigui<strong>en</strong>te, la función comparar ( )<br />

se pasa a qsort ( y a bsearch ( ) . La función comparar ( ) compara <strong>en</strong>tradas <strong>de</strong>l array tabla y<br />

<strong>de</strong>vuelve (retorna) un número negativo si argl es m<strong>en</strong>or que arg2, <strong>de</strong>vuelve cero si son iguales, o un<br />

número positivo si argl es mayor que arg2 .<br />

El programa sigui<strong>en</strong>te ord<strong>en</strong>a un array <strong>de</strong> números <strong>en</strong>teros y busca si existe un valor clave.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int comparar(const void *argl, const void *arg2);<br />

void main ( )<br />

1<br />

irit i, x;<br />

int tablaLl51;<br />

int *b;<br />

randomize ( 1 ;<br />

/* g<strong>en</strong>era tabla <strong>de</strong> elem<strong>en</strong>tos aleatorios <strong>de</strong> 1 a 100 */<br />

for (i = O; ii15; i++)<br />

tabla[i] = random(100)+1;<br />

printf ("\n\nLista original : ") ;<br />

for (i = O; i < 15; i++)<br />

printf ("%d 'I, tabla[il) ;<br />

/* Ord<strong>en</strong>a tabla utilizando el algoritmo quicksort */<br />

qsort((void *)tabla, (size_t)l5,sixeot(int),comparar);<br />

printf ("\nLista ord<strong>en</strong>ada: " ) ;<br />

for (i = O; i < 15; i++)<br />

printf ("%d ",tabla[il 1 ;<br />

printf ("\n\nClave a buscar: ") ;<br />

scanf ("%d", &x);<br />

/* Realiza una búsqueda binaria <strong>en</strong> el vector ord<strong>en</strong>ado */<br />

b = bsearch(&x,(void *)tabla,(size-t)15,sizeof(int),cornparar);<br />

if (b)<br />

/* clave <strong>en</strong>contrada */<br />

printf("\nEl elem<strong>en</strong>to %d está <strong>en</strong> la tabla',^);<br />

else<br />

printf("\nEl elem<strong>en</strong>to %d no está <strong>en</strong> la tabla",^);<br />

printf("\nPulsd cualquier tech para continuar ");<br />

1 = getch() ;


348 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

/* Comparar dos elem<strong>en</strong>tos <strong>de</strong> la lista */<br />

int comparar(const void *argl, const void *arg2)<br />

{<br />

I<br />

return *(int *) argl - *(int *) arq2;<br />

Recuer<strong>de</strong><br />

Los padmetros <strong>de</strong> la función qsort ( ) y bsearch ( ) son:<br />

&X<br />

Dirección <strong>de</strong> la clave a buscar.<br />

(void * tabla<br />

Array que conti<strong>en</strong>e valores a ord<strong>en</strong>ar.<br />

0 (size-t)lS Número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l army.<br />

sizeof (int)<br />

comparar (<br />

Tamaño <strong>en</strong> bytes <strong>de</strong> cada elem<strong>en</strong>to <strong>de</strong>l array.<br />

Nombre <strong>de</strong> la función que compara dos elem<strong>en</strong>tos <strong>de</strong>l array.<br />

10.1 1.3. Arrays <strong>de</strong> punteros <strong>de</strong> funciones<br />

Ciertas aplicaciones requier<strong>en</strong> disponer <strong>de</strong> numerosas funciones, basadas <strong>en</strong> el cumplimi<strong>en</strong>to <strong>de</strong> ciertas<br />

condiciones. Un método para implem<strong>en</strong>tar tal aplicación es utilizar una s<strong>en</strong>t<strong>en</strong>cia switch con muchos<br />

selectores case. Otra solución es utilizar un array <strong>de</strong> punteros <strong>de</strong> función. Se pue<strong>de</strong> seleccionar una<br />

función <strong>de</strong> la lista y llamarla.<br />

La sintaxis g<strong>en</strong>eral <strong>de</strong> un array <strong>de</strong> punteros <strong>de</strong> función es:<br />

tipoRetorno(*PunteroFunc[LongArray]) ();<br />

I.<br />

Ejemplo 10.8<br />

double (*fp[3]) (int n);<br />

void (*ord<strong>en</strong>ar[MAX-ORD]) (int* ArrdyF:nt, unsigned n) ;<br />

fp apunta a un array <strong>de</strong> funciones; cada miembro <strong>de</strong>vuelve un valor double y ti<strong>en</strong>e un único parametro<br />

<strong>de</strong> tipo int . ord<strong>en</strong>ar es un puntero a un array <strong>de</strong> funciones; cada miembro <strong>de</strong>vuelve un tipo void y<br />

toma dos parámetros: un puntero a int y un uns i gned .<br />

Recuer<strong>de</strong><br />

f unc , nombre <strong>de</strong> un elem<strong>en</strong>to.<br />

func [ 1 es un array.<br />

( * f unc E I es un array <strong>de</strong> punteros.<br />

( * f unc [ 1 ) ( ) es un array <strong>de</strong> punteros a funciones.<br />

int ( *func [ 1 ) ( ) es un array <strong>de</strong> punteros a funciones que <strong>de</strong>vuelv<strong>en</strong> valores int .<br />

Se pue<strong>de</strong> asignar la dirección <strong>de</strong> las funciones al array, proporcionando las funciones que ya han sido<br />

<strong>de</strong>claradas. Un ejemplo es<br />

int funcl(int i, int j);<br />

int func2(int i, int j);<br />

int (*func[]) (int,int) = {funcl, f unc2);


y;<br />

Punteros (apuntadores) 349<br />

10.1 1.4. Una aplicación <strong>de</strong> punteros <strong>de</strong> funciones<br />

El listado sigui<strong>en</strong>te, CALCULA. c , es un programa que simula calculador que pue<strong>de</strong> sumar, restar,<br />

multiplicar o dividir números. Se escribe una expresión simple por teclado y el programa visualiza la<br />

respuesta.<br />

El programa <strong>de</strong>fine cuatros funciones: sumar ( ) , restar ( ) , mult ( ) y div( ) , y un array <strong>de</strong><br />

punteros a función que se inicializa a cada una <strong>de</strong> las funciones. Se pi<strong>de</strong> la operación a realizar, se<br />

busca el índice <strong>de</strong>l puntero a función que le correspon<strong>de</strong> (<strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l operador) y se realiza la<br />

llamada a la función con su puntero.<br />

#inclu<strong>de</strong> <br />

/* prototipos <strong>de</strong> funciones */<br />

float sumar(f1oat x, float y);<br />

float restar(f1oat x, float y);<br />

float mult(f1oat x, float y);<br />

float div(f1oat x, float y);<br />

void main( )<br />

i<br />

char signo, operadores[] = { ' + I , I - ' , '*' I '/'I;<br />

float(*func[])(float, float) = {sumar, restar, mult, div};<br />

int i;<br />

unsigned char t;<br />

float x, y;<br />

i<br />

puts ("\nCalculador <strong>de</strong> expresiones") ;<br />

do i<br />

printf ("\nExpresiÓn:'I);<br />

scanf ("%f %c %f",&x,&signo,&y);<br />

/* búsqueda <strong>de</strong>l operador */<br />

for (i = O; i < 4; i++)<br />

{<br />

if (signo == operadores [ i I )<br />

{<br />

printf("\n%.lf %c %.If = %.2f", x,signo,y,func[i] (x,y));<br />

I<br />

I<br />

printf ("\nOtra expresion?: ") ;<br />

scanf ("%*c%c",&t);t=tolower(t);<br />

}while (t=='s');<br />

float sumar(f1oat x, float y)<br />

i<br />

return x + y;<br />

i<br />

float restar(f1oat x, float y)<br />

t<br />

return x ~<br />

I<br />

float mult(f1oat x, float y)<br />

i<br />

return x * y;<br />

1


350 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

float div(f1odt x, float y)<br />

i<br />

return x / y;<br />

10.12. PUNTEROS A ESTRUCTURAS<br />

Un puntero también pue<strong>de</strong> apuntar a una <strong>estructura</strong>. Se pue<strong>de</strong> <strong>de</strong>clarar un puntero a una <strong>estructura</strong> tal<br />

como se <strong>de</strong>clara un puntero a cualquier otro objeto y se <strong>de</strong>clara un puntero <strong>estructura</strong> tal como se <strong>de</strong>clara<br />

cualquier otra variable <strong>estructura</strong>: especificando un puntero <strong>en</strong> lugar <strong>de</strong>l nombre <strong>de</strong> la variable <strong>estructura</strong>.<br />

struct persona<br />

{<br />

char nombre [30 I ;<br />

int edad;<br />

int altura;<br />

int peso;<br />

1;<br />

struct persona empleado = {"Amigo, Pepe", 47, 182, 85);<br />

struct persona *p; /* se crea un puntero <strong>de</strong> <strong>estructura</strong> */<br />

p = &empleado;<br />

Cuando se refer<strong>en</strong>cia un miembro <strong>de</strong> la <strong>estructura</strong> utilizando el nombre <strong>de</strong> la <strong>estructura</strong>, se especifica<br />

la <strong>estructura</strong> y el nombre <strong>de</strong>l miembro separado por un punto (.). Para refer<strong>en</strong>ciar el nombre <strong>de</strong> una<br />

persona, utilice empleado. nombre. Se refer<strong>en</strong>cia una <strong>estructura</strong> utilizando el puntero <strong>estructura</strong>. Se<br />

utiliza el operador -> para acce<strong>de</strong>r a un miembro <strong>de</strong> ella.<br />

Ejemplo 10.9<br />

En este ejemplo se <strong>de</strong>clara el tipo <strong>estructura</strong> t-persona, que se asocia con el tipo persona para<br />

facilidad <strong>de</strong> escritura. Un array <strong>de</strong> esta <strong>estructura</strong> se iniciali7a con campos al azar y se muestran por<br />

pantalla.<br />

#inclu<strong>de</strong> <br />

struct tsersona<br />

i<br />

char nombre [301 ;<br />

int edad;<br />

int altura;<br />

int peso;<br />

1;<br />

type<strong>de</strong>f struct tsersona persona;<br />

void mostrar_persona(persona *ptr<br />

void main()<br />

i<br />

int i;<br />

persona empleados [ ] = { { "Mort i er, Pepe", 47, 182, 851,<br />

{"García, Luis", 39, 170, 75},<br />

{"Jiménez, Tom&s",18, 175, 801 };<br />

persona *p; /* puntero a <strong>estructura</strong> */<br />

p = empleados;


1<br />

for (i = O; i < 3; i++, p++)<br />

mostrar_persona(p);<br />

void mostrar_persona(persona *ptr)<br />

{<br />

}<br />

printf ("\nNombre: %s",ptr -> nombre) ;<br />

printf ("\tEdad: %d 'l,ptr -> edad);<br />

printf ("\tAltura: %d ",ptr -> altura) ;<br />

printf ("\tPeso: %d\n",ptr -> peso);<br />

Al ejecutar este programa se visualiza la salida sigui<strong>en</strong>te:<br />

Nombre: Mortimer, Pepe Edad: 47 Altura: 180 Peso: 85<br />

Nombre: Garcia, Luis Edad: 39 Altura: 170 Peso: 75<br />

Nombre: Jiménez, Tomás Edad: 18 Altura: 175 Peso: 80<br />

10.13. RESUMEN<br />

Los punteros son una <strong>de</strong> las herrami<strong>en</strong>tas más efici<strong>en</strong>tes<br />

para realizar aplicaciones <strong>en</strong> C. Aunque su<br />

práctica pue<strong>de</strong> resultar difícil y tediosa es, sin lugar a<br />

dudas, una necesidad vital su apr<strong>en</strong>dizaje si <strong>de</strong>sea<br />

obt<strong>en</strong>er el máximo r<strong>en</strong>dimi<strong>en</strong>to <strong>de</strong> sus programas.<br />

En este capítulo habrá apr<strong>en</strong>dido los sigui<strong>en</strong>tes<br />

conceptos:<br />

0 Un puntero es una variable que conti<strong>en</strong>e la<br />

dirección <strong>de</strong> una posición <strong>en</strong> memoria.<br />

Para <strong>de</strong>clarar un puntero se sitúa un asterisco<br />

<strong>en</strong>tre el tipo <strong>de</strong> dato y el nombre <strong>de</strong> la variable,<br />

como <strong>en</strong> int *p.<br />

Para obt<strong>en</strong>er el valor almac<strong>en</strong>ado <strong>en</strong> la dirección<br />

utilizada por el puntero, se utiliza el operador<br />

<strong>de</strong> indirección ( * ) . El valor <strong>de</strong> p es una<br />

dirección <strong>de</strong> memoria y el valor <strong>de</strong> *p es el<br />

dato <strong>en</strong>tero almac<strong>en</strong>ado <strong>en</strong> esa dirección <strong>de</strong><br />

memoria.<br />

Para obt<strong>en</strong>er la dirección <strong>de</strong> una variable<br />

exist<strong>en</strong>te, se utiliza el operador <strong>de</strong> dirección<br />

(&).<br />

Se <strong>de</strong>be <strong>de</strong>clarar un puntero antes <strong>de</strong> su uso.<br />

Un puntero void es un puntero que no se asigna<br />

a un tipo <strong>de</strong> dato especifico y pue<strong>de</strong>, por<br />

consigui<strong>en</strong>te, utilizarse para apuntar a tipos <strong>de</strong><br />

<strong>datos</strong> difer<strong>en</strong>tes <strong>en</strong> diversos lugares <strong>de</strong> su programa.<br />

0 Para inicializar un puntero que no apunta a<br />

nada, se utiliza la constante NULL.<br />

Estableci<strong>en</strong>do un puntero a la dirección <strong>de</strong>l primer<br />

elem<strong>en</strong>to <strong>de</strong> un array, se pue<strong>de</strong> utilizar el<br />

puntero para acce<strong>de</strong>r a cada elem<strong>en</strong>to <strong>de</strong>l array<br />

<strong>de</strong> modo secu<strong>en</strong>cial.<br />

Asi mismo, se han estudiado los conceptos <strong>de</strong><br />

aritmética <strong>de</strong> punteros, punteros a funciones, punteros<br />

a <strong>estructura</strong>s y arrays <strong>de</strong> punteros.


352 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

10.14. EJERCICIOS<br />

10.1,<br />

10.2.<br />

10.3.<br />

10.4.<br />

Encu<strong>en</strong>tsa los errores <strong>en</strong> la sigui<strong>en</strong>te <strong>de</strong>claración<br />

<strong>de</strong> punteros:<br />

10.7. Dada la sigui<strong>en</strong>te función:<br />

int x, *p, I m, double k)<br />

char* b= "Cad<strong>en</strong>a larga" ; {<br />

char* c= IC,;<br />

float xi<br />

void* r = &x;<br />

double* gorta(double* v, int<br />

Dada la sigui<strong>en</strong>te <strong>de</strong>claración, escribir una<br />

como asgum<strong>en</strong>to un punte- 1<br />

o<br />

y muestre por pantalla los<br />

¿Way erras <strong>en</strong> la codificación? ¿De qué<br />

campos.<br />

tipo?<br />

struct boton<br />

Dadas las sigui<strong>en</strong>tes <strong>de</strong>finiciones:<br />

{<br />

double w[151, x, Z;<br />

char* rotulo;<br />

void *r;<br />

int codigo;<br />

1;<br />

¿Es correcta la sigui<strong>en</strong>te llamada a la<br />

función?:<br />

¿Qué difer<strong>en</strong>cias se pued<strong>en</strong> <strong>en</strong>contrar <strong>en</strong>tre un r = gortaíw,l0,12.3);<br />

puntero a constante y ma constante puntero?<br />

Un array unidim<strong>en</strong>sional se pue<strong>de</strong> in<strong>de</strong>xar con<br />

la aritmética <strong>de</strong> punteros. ¿Qué tipo <strong>de</strong> puntero<br />

habrfa que <strong>de</strong>finir para in<strong>de</strong>xar un array<br />

bidirn<strong>en</strong>sional?<br />

o<br />

¿Y estas otras llamadas?:<br />

printf ("%if",*gorta(w,<br />

15,lO. 5)) ;<br />

z = gorta(w,15,12.3);<br />

10.5.<br />

?:<br />

..<br />

for<br />

I<br />

10.9. estrucx<br />

<strong>de</strong> la<br />

10.5.<br />

struct fecha* r;<br />

1 t;


CAPíTULO 11<br />

ASIG NACIÓN DINÁMICA<br />

DE MEMORIA<br />

CONTENIDO<br />

11.1. Gestión didmica <strong>de</strong> la<br />

memoria.<br />

11,s. Funcion <strong>de</strong><br />

memoria mal<br />

11.8. La función free ( ) .<br />

11.4. Funciones <strong>de</strong> asignacion<br />

calloc ( ) y realloc ( ) .<br />

11.6. Asignrtx


Los programas pued<strong>en</strong> crear variables globales o locales. Las variables<br />

<strong>de</strong>claradas globales <strong>en</strong> sus programas se almac<strong>en</strong>an <strong>en</strong> posiciones fijas <strong>de</strong><br />

memoria, <strong>en</strong> la zona conocida como segm<strong>en</strong>to <strong>de</strong> <strong>datos</strong> <strong>de</strong>l programa, y todas<br />

las funciones pued<strong>en</strong> utilizar estas variables. Las variables locales se almac<strong>en</strong>an<br />

<strong>en</strong> la pila (stack) y exist<strong>en</strong> sólo mi<strong>en</strong>tras están activas las funciones que están<br />

<strong>de</strong>claradas. Es posible, también, crear variables static (similares a las globales)<br />

que se almac<strong>en</strong>an <strong>en</strong> posiciones fijas <strong>de</strong> memoria, pero sólo están disponibles <strong>en</strong><br />

el módulo (es <strong>de</strong>cir, el archivo <strong>de</strong> texto) o función <strong>en</strong> que se <strong>de</strong>claran; su espacio<br />

<strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to es el segm<strong>en</strong>to <strong>de</strong> <strong>datos</strong>.<br />

Tbdas estas clases <strong>de</strong> variables compart<strong>en</strong> una característica común: se <strong>de</strong>fin<strong>en</strong><br />

cuando se compila el programa. Esto significa que el cornpilador reserva<br />

(<strong>de</strong>fine) espacio para almac<strong>en</strong>ar valores <strong>de</strong> los tipos <strong>de</strong> <strong>datos</strong> <strong>de</strong>clarados. Es<br />

<strong>de</strong>cir, <strong>en</strong> el caso <strong>de</strong> las variables globales y locales se ha <strong>de</strong> indicar al compilador<br />

exactam<strong>en</strong>te cuántas y <strong>de</strong> qué tipo son las variables a asignar. O sea, el<br />

espacio <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to se reserva <strong>en</strong> el mom<strong>en</strong>to <strong>de</strong> la CompilaciÓn.<br />

Sin embargo, no siempre es posible conocer con antelación a la ejecución<br />

cuanta memoria se <strong>de</strong>be reservar al programa. En C, se asigna memoria <strong>en</strong> el<br />

mom<strong>en</strong>to <strong>de</strong> la ejecución <strong>en</strong> el montículo o montón (heap), mediante las<br />

funciones mallo@( ), realloo( ), calloa( ) y free( ), que asignan y liberan la<br />

memoria <strong>de</strong> una zona d<strong>en</strong>ominada almacén libre.<br />

I<br />

CONCEPTOS CLAVE<br />

O Arraydinámico.<br />

O Array estático.<br />

O Desbordami<strong>en</strong>to <strong>de</strong> memoria.<br />

O Función free.<br />

0 Función malloc.<br />

O Gestión dinámica.<br />

O puritero g<strong>en</strong>4rico.<br />

O Variable apunhda.<br />

355


356 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

11.1. GESTIÓN DINÁMICA DE LA MEMORIA<br />

n<br />

Consi<strong>de</strong>remos un programa que evalúe las calificaciones <strong>de</strong> los estudiantes <strong>de</strong> una asignatura. El<br />

programa almac<strong>en</strong>a cada una <strong>de</strong> las calificaciones <strong>en</strong> los elem<strong>en</strong>tos <strong>de</strong> una lista o tabla (array) y el<br />

tamaño <strong>de</strong>l array <strong>de</strong>be ser lo sufici<strong>en</strong>tem<strong>en</strong>te gran<strong>de</strong> para cont<strong>en</strong>er el total <strong>de</strong> alumnos matriculados <strong>en</strong><br />

la asignatura. Por ejemplo, la s<strong>en</strong>t<strong>en</strong>cia<br />

int asignatura [401 ;<br />

reserva 40 <strong>en</strong>teros, un número fijo <strong>de</strong> elem<strong>en</strong>tos. Los arrays son un método muy eficaz cuando se conoce<br />

su longitud o tamaño <strong>en</strong> el mom<strong>en</strong>to <strong>de</strong> escribir el programa. Sin embargo, pres<strong>en</strong>tan un grave<br />

inconv<strong>en</strong>i<strong>en</strong>te si el tamaño <strong>de</strong>l array sólo se conoce <strong>en</strong> el mom<strong>en</strong>to <strong>de</strong> la ejecución. Las s<strong>en</strong>t<strong>en</strong>cias<br />

sigui<strong>en</strong>tes producirían un error durante la compilación:<br />

scanf ("%da' ,&num-estudiantes);<br />

int asignatura[num-estudiantes] ;<br />

ya que el compilador requiere que el tamaño <strong>de</strong>l array sea constante. Sin embargo, <strong>en</strong> numerosas<br />

ocasiones no se conoce la memoria necesaria hasta el mom<strong>en</strong>to <strong>de</strong> la ejecución. Por ejemplo, si se <strong>de</strong>sea<br />

almac<strong>en</strong>ar una cad<strong>en</strong>a <strong>de</strong> caracteres tecleada por el usuario, no se pue<strong>de</strong> prever, a priori, el tamaño <strong>de</strong>l<br />

array necesario, a m<strong>en</strong>os que se reserve un array <strong>de</strong> gran dim<strong>en</strong>sión y se malgaste memoria cuando no<br />

se utilice. En el ejemplo anterior, si el número <strong>de</strong> alumnos <strong>de</strong> la clase aum<strong>en</strong>ta, se <strong>de</strong>be variar la longitud<br />

<strong>de</strong>l array y volver a compilar el programa. El método para resolver este inconv<strong>en</strong>i<strong>en</strong>te es recurrir a<br />

punteros y a técnicas <strong>de</strong> asignación dinámica <strong>de</strong> memoria.<br />

El espacio <strong>de</strong> la variable asignada dinámicam<strong>en</strong>te se crea durante la ejecución <strong>de</strong>l programa, al<br />

contrario que <strong>en</strong> el caso <strong>de</strong> una variable local cuyo espacio se asigna <strong>en</strong> tiempo <strong>de</strong> compilación. La<br />

asignación dinámica <strong>de</strong> memoria proporciona control directo sobre los requisitos <strong>de</strong> memoria <strong>de</strong> su<br />

programa. El programa pue<strong>de</strong> crear o <strong>de</strong>struir la asignación dinámica <strong>en</strong> cualquier mom<strong>en</strong>to durante la<br />

ejecución. Se pue<strong>de</strong> <strong>de</strong>terminar la cantidad <strong>de</strong> memoria necesaria <strong>en</strong> el mom<strong>en</strong>to <strong>en</strong> que se haga la<br />

asignación. Dep<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> memoria <strong>en</strong> uso, se pued<strong>en</strong> crear variables mayores <strong>de</strong> 64 K.<br />

El código <strong>de</strong>l programa compilado se sitúa <strong>en</strong> segm<strong>en</strong>tos <strong>de</strong> memoria d<strong>en</strong>ominados segm<strong>en</strong>tos <strong>de</strong><br />

código. Los <strong>datos</strong> <strong>de</strong>l programa, tales como variables globales, se sitúan <strong>en</strong> un área d<strong>en</strong>ominada<br />

segm<strong>en</strong>to <strong>de</strong> <strong>datos</strong>. Las variables locales y la información <strong>de</strong> control <strong>de</strong>l programa se sitúan <strong>en</strong> un área<br />

d<strong>en</strong>ominada pila. La memoria que queda se d<strong>en</strong>omina memoria <strong>de</strong>l montículo o almacén libre. Cuando<br />

el programa solicita memoria para una variable dinámica, se asigna el espacio <strong>de</strong> memoria <strong>de</strong>seado<br />

<strong>de</strong>s<strong>de</strong> el montículo.


7<br />

Asignación dinámica <strong>de</strong> memoria 357<br />

11.1.1. Almacén libre (free storel<br />

El mapa <strong>de</strong> memoria <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> un programa gran<strong>de</strong> es muy similar al mostrado <strong>en</strong> la Figura I 1.1.<br />

El diseño exacto <strong>de</strong>p<strong>en</strong><strong>de</strong>rá <strong>de</strong>l mo<strong>de</strong>lo <strong>de</strong> programa que se utilice. Para gran<strong>de</strong>s mo<strong>de</strong>los <strong>de</strong> <strong>datos</strong>, el<br />

almacén libre (heap) se refiere al área <strong>de</strong> memoria que existe d<strong>en</strong>tro <strong>de</strong> la pila <strong>de</strong>l programa. Y el<br />

almacén libre es, es<strong>en</strong>cialm<strong>en</strong>te, toda la memoria que queda libre <strong>de</strong>spués <strong>de</strong> que se carga el programa.<br />

Memoria<br />

alta<br />

SP -<br />

ss *<br />

DS _____)<br />

cs -<br />

Memoria baja<br />

El montículo (almacén libre)<br />

Toda la memoria que queda libre está<br />

disponible <strong>en</strong> asignaciones dinámicas<br />

<strong>de</strong> memoria.<br />

Segm<strong>en</strong>to <strong>de</strong> Pila<br />

La pila crece hacia abajo <strong>en</strong> memoria.<br />

Datos no inicializados.<br />

Datos inicializados.<br />

Segm<strong>en</strong>to <strong>de</strong> código #n.<br />

Segm<strong>en</strong>to <strong>de</strong> código #2.<br />

Segm<strong>en</strong>to <strong>de</strong> código # 1<br />

Cada segm<strong>en</strong>to dato, código o pila<br />

se limita a 64 K.<br />

Figura 11.1. Mapa <strong>de</strong> memoria <strong>de</strong> un programa.<br />

En C las funciones maiioc ( ), reaiioc ( ), caiioc ( ) y free ( asignan y liberan memoria <strong>de</strong><br />

un bloque <strong>de</strong> memoria d<strong>en</strong>ominado el montículo <strong>de</strong>l sistema. Las funciones rnalloc ( ) , cal loc ( ) y<br />

real loc ( ) asignan memoria utilizando asignación dinámica <strong>de</strong>bido a que pue<strong>de</strong> gestionar la memoria<br />

durante la ejecución <strong>de</strong> un programa; estas funciones requier<strong>en</strong>, g<strong>en</strong>eralm<strong>en</strong>te, mol<strong>de</strong>ado (conversión <strong>de</strong><br />

tipos).<br />

I I .2. FUNCIÓN maiioc (<br />

La forma más habitual <strong>de</strong> C para obt<strong>en</strong>er bloques <strong>de</strong> memoria es mediante la llamada a la función<br />

rnalloc ( ) . La función asigna un bloque <strong>de</strong> memoria que es el número <strong>de</strong> bytes pasados como<br />

argum<strong>en</strong>to. malloc ( ) <strong>de</strong>vuelve un puntero, que es la dirección <strong>de</strong>l bloque asignado <strong>de</strong> memoria. El<br />

puntero se utiliza para refer<strong>en</strong>ciar el bloque <strong>de</strong> memoria y <strong>de</strong>vuelve un puntero <strong>de</strong>l tipo void*. La forma<br />

<strong>de</strong> llamar a la función malloc ( ) es:<br />

puntero = malloc(tamaño <strong>en</strong> bytes);<br />

G<strong>en</strong>eralm<strong>en</strong>te se hará una conversión al tipo <strong>de</strong>l puntero:<br />

tipo *puntero;<br />

puntero =(tipo *)malloc(tamaño <strong>en</strong> bytes);


358 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Por ejemplo:<br />

long* p;<br />

p = (long") malloc(32);<br />

El operador unario sizeof se utiliza con mucha frecu<strong>en</strong>cia <strong>en</strong> las funciones <strong>de</strong> asignación <strong>de</strong><br />

memoria. El operador se aplica a un tipo <strong>de</strong> dato (o una variable), el valor resultante es el número <strong>de</strong><br />

bytes que ocupa. Así, si se quiere reservar memoria para un buffer <strong>de</strong> 10 <strong>en</strong>teros:<br />

int *r;<br />

r = (int*) rnalloc(lO*sizeof(int );<br />

Al llamar a la función mailoc ( ) pue<strong>de</strong> ocurrir que no haya memoria disponible, <strong>en</strong> ese caso<br />

malloc ( ) <strong>de</strong>vuelve NULL.<br />

Sintaxis <strong>de</strong> llamada a meilloc ( )<br />

tipo *puntero;<br />

puntero = (tipo*)malloc(tamaño);<br />

La función <strong>de</strong>vuelve la dirección <strong>de</strong> la variable asignada dinánhicarn<strong>en</strong>te, el tipo que<br />

<strong>de</strong>vuelve es void*.<br />

Prototipo que incluye malloc ( 1<br />

void* malloc(size-t n);<br />

Figura 10.2. Sintaxis (formato) <strong>de</strong> la función ma 1 1 or í ) .<br />

En la sintaxis <strong>de</strong> llamada, puntero es el nombre <strong>de</strong> la variable puntero a la que se asigna la dirección<br />

<strong>de</strong>l objeto dato, o se le asigna la dirección <strong>de</strong> memoria <strong>de</strong> un bloque lo sufici<strong>en</strong>tem<strong>en</strong>te gran<strong>de</strong> para<br />

cont<strong>en</strong>er un array <strong>de</strong> n elem<strong>en</strong>tos, o NULL, si falla la operación <strong>de</strong> asignación <strong>de</strong> memoria. El sigui<strong>en</strong>te<br />

código utiliza malloc ( ) para asignar espacio para un valor <strong>en</strong>tero:<br />

int *pEnt;<br />

...<br />

pEnt = (int*) rnalloc(sizeof(int));<br />

La llamada a malloc ( ) asigna espacio para un int (<strong>en</strong>tero) y almac<strong>en</strong>a la dirección <strong>de</strong> la<br />

asignación <strong>en</strong> pEnt . pEnt apunta ahora a la posición <strong>en</strong> el almacén libre (montículo) don<strong>de</strong> se<br />

establece la memoria. La Figura 10.3 muestra como pEnt apunta a la asignación <strong>de</strong>l almacén libre. Así,<br />

por ejemplo, para reservar memoria para un array <strong>de</strong> 100 números reales:<br />

float "BloqueMem;<br />

BloqueMern = (float") rnalloc(100*sizeof(float));<br />

En el ejemplo se <strong>de</strong>clara un puntero d<strong>en</strong>ominado BloqueMern y lo inicializan a la dirección <strong>de</strong>vuelta<br />

por malloc ( ) . Si un bloque <strong>de</strong>l tamaño solicitado está disponible, malloc ( ) <strong>de</strong>vuelve un puntero al<br />

principio <strong>de</strong> un bloque <strong>de</strong> memoria <strong>de</strong>l tamaño especificado. Si no hay bastante espacio <strong>de</strong><br />

almac<strong>en</strong>ami<strong>en</strong>to dinámico para cumplir la petición, malloc ( ) <strong>de</strong>vuelve cero o NULL. La reserva <strong>de</strong> n<br />

caracteres se pue<strong>de</strong> <strong>de</strong>clarar así:<br />

int n;<br />

char *s;<br />

scanf ( "%d" , &n) ;<br />

s = (char*) rnalloc(n*sizeof(char));


Asignación dinámica <strong>de</strong> memoria 359<br />

La función mal I oc ( ) está <strong>de</strong>clarada <strong>en</strong> el archivo <strong>de</strong> cabecera stdl ib . h.<br />

Montículo<br />

(almacén libre)<br />

I valor i r it I -<br />

plht -b<br />

Dirección <strong>de</strong> I ri t<br />

I Código <strong>de</strong> programa<br />

Figura 11.3. Después <strong>de</strong> mdl ~ O ( C ), con el tamaño <strong>de</strong> un <strong>en</strong>tero, pt'rit apunta a la posición <strong>de</strong>l montículo<br />

don<strong>de</strong> se ha asignado espacio para el <strong>en</strong>tero.<br />

I<br />

Ejemplo 11.1<br />

En el sigui<strong>en</strong>te ejemplo se lee una línea <strong>de</strong> caracteres, se reserva memoria para un buffer <strong>de</strong> tantos<br />

caracteres como los leídos y se copia <strong>en</strong> el buffer la cad<strong>en</strong>a.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> /* por el uso <strong>de</strong> strcpyo */<br />

void main()<br />

{<br />

char cad[l21], *ptr;<br />

int lon;<br />

puts ("\nIntroduce una linea <strong>de</strong> texto\n") ;<br />

gets (cad) ;<br />

lon = strl<strong>en</strong>(cad) ;<br />

ptr = (char*) malloc ( (lon+l) "sizeof (char ) ;<br />

1<br />

strcpy (ptr, cad) ;<br />

/* copia cad a nueva área <strong>de</strong> memoria<br />

apuntada por ptr */<br />

printf ("ptr = %s",ptr); /* cad está ahora <strong>en</strong> ptr */<br />

free (ptr); /* libera memoria <strong>de</strong> ptr */<br />

La expresión<br />

ptr = (char*) malloc((lon+l)*sizeof(char));<br />

<strong>de</strong>vuelve un puntero que apunta a una sección <strong>de</strong> memoria capaz <strong>de</strong> cont<strong>en</strong>er la cad<strong>en</strong>a <strong>de</strong> longitud<br />

strl<strong>en</strong> ( ) más un byte extra por el carácter I \ O al final <strong>de</strong> la cad<strong>en</strong>a.


- - -<br />

1<br />

360 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Figura 11.4. Memoria obt<strong>en</strong>ida por función malloc í ) .<br />

8 % , I<br />

Precaución<br />

El almac<strong>en</strong>ami<strong>en</strong>to libre no es una fu<strong>en</strong>te inagotable <strong>de</strong> memoria. Si la función malloc ( ) se<br />

ejecuta con falta <strong>de</strong> memoria, se <strong>de</strong>vuelve un puntero NULL. Es responsabilidad <strong>de</strong>l programador<br />

comprobar siempre el puntero para asegurar que es válido, antes <strong>de</strong> que se asigne un valor al<br />

puntero. Supongamos, por ejemplo, que se <strong>de</strong>sea asignar un array <strong>de</strong> 1.000 números reales <strong>en</strong><br />

doble precisión:<br />

#<strong>de</strong>fine TOPE 1999<br />

double *ptr-lista;<br />

int i;<br />

ptr-lista = (double*)malloc(lOOO*sizeof(double));<br />

if (ptr-lista == NULL)<br />

c<br />

puts ("Error <strong>en</strong> la asignación <strong>de</strong> memoria") ;<br />

return -1; /* int<strong>en</strong>tar recuperar memoria */<br />

}<br />

for (i = O; i < 1000; i++)<br />

ptr-lista[i] = (double)*random(TOPE);<br />

Si no existe espacio <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to sufici<strong>en</strong>te, la función malloc ( ) <strong>de</strong>vuelve NULL. La<br />

escritura <strong>de</strong> un programa totalm<strong>en</strong>te seguro, exige comprobar el valor <strong>de</strong>vuelto por malloc í )<br />

para asegurar que no es NULL. NULL es una constante pre<strong>de</strong>finida <strong>en</strong> G. Se <strong>de</strong>be incluir los<br />

archivos <strong>de</strong> cabecera para obt<strong>en</strong>er la <strong>de</strong>finición <strong>de</strong> NULL.


Asignación dinámica <strong>de</strong> memoria 361<br />

Ejemplo 11.2<br />

El programa TESTMEM comprueba la cantidad <strong>de</strong> memoria que se pue<strong>de</strong> asignar dinúmicam<strong>en</strong>te (estú<br />

disponible). Para ello se llama a malloc ( ) , solicitando <strong>en</strong> cada llamada 1.000 bytes <strong>de</strong> memoria.<br />

/*<br />

TESTMEM: programa para <strong>de</strong>terminar memoria libre.<br />

*/<br />

#inclu<strong>de</strong> <br />

int main()<br />

I<br />

void *p;<br />

int i;<br />

long m = 0;<br />

for (i = I; ; i++)<br />

i<br />

p = malloc(1000);<br />

if (p == NULL) break;<br />

m += 1000;<br />

I<br />

printf ("\nTotal <strong>de</strong> memoria asignada<br />

return O;<br />

I<br />

%d\n",m);<br />

Se asigna repetidam<strong>en</strong>te 1 kB (Kilobytes) hasta que falla la asignación <strong>de</strong> memoria y el bucle se<br />

termina.<br />

11 2.1. Asignación <strong>de</strong> memoria <strong>de</strong> un tamaño <strong>de</strong>sconocido<br />

Se pue<strong>de</strong> invocar a la función malloc ( ) para obt<strong>en</strong>er memoria para un array, incluso si no se conoce<br />

con antelación cuanta memoria requier<strong>en</strong> los elem<strong>en</strong>tos <strong>de</strong>l array. Todo lo que se ha <strong>de</strong> hacer es invocar<br />

a malloc ( ) <strong>en</strong> tiempo <strong>de</strong> ejecución, pasando como argum<strong>en</strong>to el número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array<br />

multiplicado por el tamaño <strong>de</strong>l tipo <strong>de</strong>l array. El número <strong>de</strong> elem<strong>en</strong>tos se pue<strong>de</strong> solicitar al usuario y<br />

leerse <strong>en</strong> tiempo <strong>de</strong> ejecución. Por ejemplo, este segm<strong>en</strong>to <strong>de</strong> código asigna memoria para un array <strong>de</strong><br />

n elem<strong>en</strong>tos <strong>de</strong> tipo double, el valor <strong>de</strong> n se conoce <strong>en</strong> tiempo <strong>de</strong> ejecución:<br />

double *ad;<br />

int n;<br />

printf ("Número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array: 'I) ;<br />

scanf ("%d", &n);<br />

ad = (double*)malloc(n*sizeof(double));<br />

En este otro ejemplo se <strong>de</strong>clara un tipo <strong>de</strong> dato complejo, se solicita cuántos números complejos se<br />

van a utilizar, se reserva memoria para ellos y se comprueba que existe memoria sufici<strong>en</strong>te. Al final,<br />

se le<strong>en</strong> los n números complejos.<br />

struct complejo<br />

i<br />

float x, y;<br />

I;<br />

int n, j;<br />

struct complejo *p;<br />

printf ("Cuantos números complejos: ") ;<br />

scanf ( "%d" , &n) ;


-1<br />

362 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

p = (struct complejo*) malloc(n*sizeof(struct complejo));<br />

if (p == NULL)<br />

i<br />

puts("Fin <strong>de</strong> ejecución. Error <strong>de</strong> asignación <strong>de</strong> memoria.");<br />

exit (-1);<br />

1<br />

for (j = O; jcn; j++,p++)<br />

i<br />

printf("Parte real e imaginaria <strong>de</strong>l complejo %d : ",j);<br />

i<br />

scanf ("%f%f",&p->x,&p->y);<br />

11.2.2. Uso <strong>de</strong> malioc ( ) para arrays multidim<strong>en</strong>sionales<br />

Un array bidim<strong>en</strong>sional es, <strong>en</strong> realidad, un array cuyos elem<strong>en</strong>tos son arrays. Al ser el nombre <strong>de</strong> un<br />

array unidim<strong>en</strong>sional un puntero constante, un array bidim<strong>en</strong>sinal será un puntero a puntero constante<br />

(tipo **). Para asignar memoria a un array multidim<strong>en</strong>sional, se indica cada dim<strong>en</strong>sión <strong>de</strong>l array <strong>de</strong><br />

igual forma que se <strong>de</strong>clara un array unidim<strong>en</strong>sional. En el Ejemplo 1 1.3 se reserva memoria <strong>en</strong> tiempo<br />

<strong>de</strong> ejecución para una matriz <strong>de</strong> n filas y para cada fila m elem<strong>en</strong>tos.<br />

Ejemplo 11.3<br />

/* matriz <strong>de</strong> n filas y cada fila <strong>de</strong> un número variable <strong>de</strong> elem<strong>en</strong>tos */<br />

#inclu<strong>de</strong> <br />

int main()<br />

1<br />

int **p ;<br />

int n,m,i;<br />

do i<br />

printf ("\n Numero <strong>de</strong> filas: 'I); scanf ("%d",&n);<br />

} while (n


___<br />

Asignación dinámica <strong>de</strong> memoria<br />

11.3. LIBERACIÓN DE MEMORIA, FUNCIÓN free ( )<br />

Cuando se ha terminado <strong>de</strong> utilizar un bloque <strong>de</strong> memoria previam<strong>en</strong>te asignado por mal loc ( ) , u otras<br />

funciones <strong>de</strong> asignación, se pue<strong>de</strong> liberar el espacio <strong>de</strong> memoria y <strong>de</strong>jarlo disponible para otros usos,<br />

mediante una llamada a la función free ( ) . El bloque <strong>de</strong> memoria suprimido se <strong>de</strong>vuelve al espacio <strong>de</strong><br />

almac<strong>en</strong>ami<strong>en</strong>to libre, <strong>de</strong> modo que habrá más memoria disponible para asignar otros bloques <strong>de</strong><br />

memoria. El formato <strong>de</strong> la llamada es<br />

free (puntero)<br />

Así, por ejemplo, para las <strong>de</strong>claraciones<br />

1. int *ad;<br />

ad = (int*)malloc(sizeof(int));<br />

2. char *adc;<br />

adc = (char*) malloc (lOO*sizeof (char ) ;<br />

el espacio asignado se pue<strong>de</strong> liberar con las s<strong>en</strong>t<strong>en</strong>cias<br />

Y<br />

free(ad);<br />

free (adc) ;<br />

Sintaxis <strong>de</strong> Wads a free ( 1<br />

tipo *puntero;<br />

...<br />

free (puntero) ;<br />

La variable puntero pue<strong>de</strong> apuntar a una dirección <strong>de</strong> memoria <strong>de</strong> cuaiquier tipo.<br />

Prototipo que incluye free ( 1<br />

void free(void *);<br />

Figura 11.5. Sintaxis (formato) <strong>de</strong> la función Free ( ).<br />

Ejemplo 11.4<br />

En este ejemplo se reserva memoria para un array <strong>de</strong> 10 <strong>estructura</strong>s; <strong>de</strong>spués se libera la memoria<br />

reservada.<br />

struct gato *pgato; /* <strong>de</strong>clara puntero a la <strong>estructura</strong> gato */<br />

pgato = (struct gato*)malloc(lO*sizeof(struct gato));<br />

if (pgato == NULL)<br />

puts ("Memoria agotada") ;<br />

else<br />

i<br />

...<br />

free (pgato); /* Liberar memoria asignada a pgato */<br />

1


364 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

11.4. FUNCIONES DE ASIGNACIÓN DE MEMORIA calloc ( ) y realloc ( )<br />

A<strong>de</strong>más <strong>de</strong> la función malloc ( ) para obt<strong>en</strong>er bloques <strong>de</strong> memoria, hay otras dos funciones que<br />

permit<strong>en</strong> obt<strong>en</strong>er memoria libre <strong>en</strong> tiempo <strong>de</strong> ejecución, éstas son calloc ( )y real loc ( ) . Con ambas<br />

se pue<strong>de</strong> asignar memoria, como con mal loc ( ) , cambia la forma <strong>de</strong> transmitir el número <strong>de</strong> bytes <strong>de</strong><br />

memoria requeridos. Ambas <strong>de</strong>vuelv<strong>en</strong> un puntero al bloque asignado <strong>de</strong> memoria. El puntero se utiliza<br />

para refer<strong>en</strong>ciar el bloque <strong>de</strong> memoria. El puntero que <strong>de</strong>vuelv<strong>en</strong> es <strong>de</strong>l tipo void* .<br />

11.4.1. Función calloc ( )<br />

La forma <strong>de</strong> llamar a la función calloc ( ) es:<br />

puntero = calloc(nÚmero elem<strong>en</strong>tos,tamaño <strong>de</strong> cada elem<strong>en</strong>to);<br />

G<strong>en</strong>eralm<strong>en</strong>te se hará una conversión al tipo <strong>de</strong>l puntero:<br />

tipo *puntero;<br />

puntero =(tipo*)calloc(numero elern<strong>en</strong>tos,tamaño <strong>de</strong> cada elem<strong>en</strong>to);<br />

El tamaño <strong>de</strong> cada elem<strong>en</strong>to se expresa <strong>en</strong> bytes, se utiliza para obt<strong>en</strong>erlo el operador s izeof. Por<br />

ejemplo, se quiere reservar memoria para 5 <strong>datos</strong> <strong>de</strong> tipo double:<br />

#<strong>de</strong>fine N 5<br />

double* pd;<br />

pd = (double*) calloc(N,sizeof(double );<br />

En este otro ejemplo se reserva memoria para una cad<strong>en</strong>a variable:<br />

char *c, B[121];<br />

puts('1ntroduce una línea <strong>de</strong> caracteres.");<br />

gets (B);<br />

/* Se reserva memoria para el número <strong>de</strong> caracteres + 1 para el carácter<br />

fin <strong>de</strong> cad<strong>en</strong>a.<br />

*/<br />

c = (char*) calloc(strl<strong>en</strong>(B)+l,sizeof (char));<br />

strcpy(c,B);<br />

Al llamar a la función calloc ( ) pue<strong>de</strong> ocurrir que no haya memoria disponible, <strong>en</strong> ese caso<br />

calloc ( ) <strong>de</strong>vuelve NULL.<br />

'<br />

Sintaxis <strong>de</strong> llamada a caiioc ( )<br />

tipo *puntero;<br />

intnumelem<strong>en</strong>tos;<br />

...<br />

puntero = (tipo*)calloc (numelem<strong>en</strong>toc,tamaño <strong>de</strong> tipo);<br />

La función <strong>de</strong>vuelve la dirección <strong>de</strong> la variable asignada dinániicam<strong>en</strong>te, el tipo que<br />

<strong>de</strong>vuelve es void*.<br />

ti<strong>en</strong>e caiioc ( )<br />

void* calloc(size-t n,size-t t);<br />

Figura 11.6. Sintaxis (formato) <strong>de</strong> la función calloc í i


-<br />

Asignación dinámica <strong>de</strong> memoria 365<br />

En la sintaxis <strong>de</strong> llamada, puntero es el nombre <strong>de</strong> la variable puntero al que se asigna la dirección<br />

<strong>de</strong> memoria <strong>de</strong> un bloque <strong>de</strong> numelem<strong>en</strong>tos, o NULL si falla la operación <strong>de</strong> asignación <strong>de</strong> memoria.<br />

La función calloc ( ) está <strong>de</strong>clarada <strong>en</strong> el archivo <strong>de</strong> cabecera st dl ib. h, por lo que será necesario<br />

incluir ese archivo <strong>de</strong> cabecera <strong>en</strong> todo programa que llame a la función. Se pue<strong>de</strong> reservar memoria<br />

dinámicam<strong>en</strong>te para cualquier tipo <strong>de</strong> dato, incluy<strong>en</strong>do char, int , float, arrays, <strong>estructura</strong>s e<br />

id<strong>en</strong>tificadores <strong>de</strong> type<strong>de</strong> f.<br />

En el sigui<strong>en</strong>te programa se consi<strong>de</strong>ra una secu<strong>en</strong>cia <strong>de</strong> números reales, con una variable puntero a<br />

float se procesa un array <strong>de</strong> longitud variable, <strong>de</strong> modo que se pue<strong>de</strong> ajustar la cantidad <strong>de</strong> memoria<br />

necesaria para el número <strong>de</strong> valores durante la ejecución <strong>de</strong>l programa.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main (void)<br />

t<br />

float *pf = NULL;<br />

int num, i;<br />

}<br />

do {<br />

printf("NÚmero <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l vector: ");<br />

scanf ("%d", &num);<br />

}while (num < 1);<br />

/* Asigna memoria: num*tamaño bytes */<br />

pf = (float *) calloc(num, sizeof(f1oat));<br />

if (pf == NULL)<br />

i<br />

puts('Error <strong>en</strong> la asignación <strong>de</strong> memoria.");<br />

return 1;<br />

1<br />

printf ("\Introduce%d valores ",num);<br />

for (izo; i


366 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 11.5<br />

Reservar memoria para una cad<strong>en</strong>a y a continuación, ampliar para otra cad<strong>en</strong>a más larga.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main( )<br />

i<br />

char *cad<strong>en</strong>a;<br />

int tam;<br />

tam = (strl<strong>en</strong>("Primavera") +l) "sizeof(char);<br />

cad<strong>en</strong>a = (char*)malloc(tam);<br />

strcpy (cad<strong>en</strong>a, "Primavera') ;<br />

puts (cad<strong>en</strong>a) ;<br />

/* Amplia el bloque <strong>de</strong> memoria */<br />

1<br />

tam += (strl<strong>en</strong>(" <strong>en</strong> Lupiana\n") +1) "sizeof(char);<br />

cad<strong>en</strong>a = (char * ) realloc (cad<strong>en</strong>a, tam) ;<br />

strcat (cad<strong>en</strong>a,'I <strong>en</strong> Lupiana\n") ;<br />

puts(cad<strong>en</strong>a);<br />

/* liberación <strong>de</strong> memoria */<br />

free (cad<strong>en</strong>a) ;<br />

return O;<br />

El segundo argum<strong>en</strong>to <strong>de</strong> realloc ( ) , es el tamaño total que va a t<strong>en</strong>er el bloque <strong>de</strong> memoria libre.<br />

Si se pasa cero (O) como tamaño se libera el bloque <strong>de</strong> memoria al que está apuntando el puntero primer<br />

argum<strong>en</strong>to, y la función <strong>de</strong>vuelve NULL. En el sigui<strong>en</strong>te ejemplo se reserva memoria con calloc ( ) y<br />

<strong>de</strong>spués se libera con realloc ( ) .<br />

#<strong>de</strong>fine N 10<br />

long* pl;<br />

pl = (long*) calloc (N, sizeof (long ) ;<br />

...<br />

pl = realloc (pl, O) ;<br />

El puntero <strong>de</strong>l primer argum<strong>en</strong>to <strong>de</strong> realloc ( ) pue<strong>de</strong> t<strong>en</strong>er el valor <strong>de</strong> NULL, <strong>en</strong> este caso la<br />

función realloc ( ) reserva tanta memoria como la indicada por el segundo argum<strong>en</strong>to, <strong>en</strong> <strong>de</strong>finitiva,<br />

actúa como malloc ( ) .<br />

Ejemplo 11.6<br />

En este ejemplo se le<strong>en</strong> dos cad<strong>en</strong>as <strong>de</strong> caracteres; si la segunda cad<strong>en</strong>a comi<strong>en</strong>za por COPIA .se aña<strong>de</strong><br />

a la primera. La memoria se reserva con real 1 oc ( ) .<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main( )<br />

{<br />

char *Cl=NULL,*C2=NULL, B[121];<br />

char *clave ="COPIA";


Asignación dinámica <strong>de</strong> memoria 367<br />

1<br />

int tam;<br />

puts ("\n\t Primera cad<strong>en</strong>a I' ) ;<br />

gets (B);<br />

tam = (strl<strong>en</strong>(B)+l)*sizeof(char);<br />

C1 = (char*) realloc (Cl, tam) ;<br />

strcpy(C1,B);<br />

puts ("\n\t Segunda cad<strong>en</strong>a " ) ;<br />

gets (B);<br />

tam = (strl<strong>en</strong>(B)+l)*sizeof(char);<br />

C2 = (char*) realloc (C2, tam) ;<br />

strcpy (C2, B) ;<br />

/* Compara los primeros caracteres <strong>de</strong> C2 con clave.<br />

La comparación se realiza con la función strcmpo */<br />

if (strl<strong>en</strong>(c1ave)


368 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Hay que t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta que la expansión <strong>de</strong> memoria que realiza realloc ( ) pue<strong>de</strong> hacerla <strong>en</strong><br />

otra dirección <strong>de</strong> memoria <strong>de</strong> la que conti<strong>en</strong>e la variable puntero transmitida como primer argum<strong>en</strong>to.<br />

En cualquier caso, realloc ( ) copia los <strong>datos</strong> refer<strong>en</strong>ciados por puntero <strong>en</strong> la memoria expandida.<br />

La función real ioc ( ) , al igual que las <strong>de</strong>más funciones <strong>de</strong> asignación <strong>de</strong> memoria, está <strong>de</strong>clarada<br />

<strong>en</strong> el archivo <strong>de</strong> cabecera stdlib. h.<br />

11.5. ASIGNACIÓN DE MEMORIA PARA ARRAYS<br />

La gestión <strong>de</strong> listas y tablas mediante arrays es una <strong>de</strong> las operaciones más usuales <strong>en</strong> cualquier<br />

programa. La asignación <strong>de</strong> memoria para arrays es, <strong>en</strong> consecu<strong>en</strong>cia, una <strong>de</strong> las tareas que es preciso<br />

conocer <strong>en</strong> profundidad.<br />

El listado <strong>de</strong> ASIGCADS . c muestra cómo se pue<strong>de</strong> utilizar la función malloc ( para asignar<br />

memoria a un array <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> longitud variable.<br />

?<br />

~~ ~<br />

Ejemplo 11.7<br />

El programa ASIGCADS . c lee n líneas <strong>de</strong> texto, reserva memoria según la longitud <strong>de</strong> la línea leída,<br />

cu<strong>en</strong>ta las vocales <strong>de</strong> cada línea e imprime cada línea y el número <strong>de</strong> vocales que ti<strong>en</strong>e.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine N 10<br />

void salida(char*[l, int*);<br />

void <strong>en</strong>trada(char*[l);<br />

int vocales (char*) ;<br />

int main ( )<br />

{<br />

char *cad[Nl;<br />

int j, voc[Nl ;<br />

<strong>en</strong>trada (cad) ;<br />

/* Cu<strong>en</strong>ta <strong>de</strong> vocales por cada linea */<br />

for (j = O; j


Asignación dinámica <strong>de</strong> memoria -1369 E<br />

}<br />

int vocales (char* c)<br />

i<br />

int k, j;<br />

/* Cu<strong>en</strong>ta vocales <strong>de</strong> la cad<strong>en</strong>a c */<br />

for (j=k = O; jistrl<strong>en</strong>(c); jtt)<br />

switch (tolower(*(c+j)))<br />

i<br />

case 'a':;<br />

case 'e':;<br />

case 'i' :;<br />

case 'o':;<br />

case 'u': k+t;<br />

i<br />

return k;<br />

1<br />

void sallda(char* cd[l, int* v )<br />

int j;<br />

puts ("\n\tSalida <strong>de</strong> las lineas junto dl numero <strong>de</strong> vocales") ;<br />

for (j = O; jiN; j++)<br />

i<br />

printf ("%s : %2d\n",cd[jl,v[jl);<br />

1<br />

El programa <strong>de</strong>clara char *cad IN] como array <strong>de</strong> punteros a char, <strong>de</strong> tal forma que <strong>en</strong> la función<br />

<strong>en</strong>trada ( ) se reserva memoria, con mailoc ( ) , para cada línea <strong>de</strong> texto.<br />

11.5.1. Asignación <strong>de</strong> memoria interactivam<strong>en</strong>te<br />

El programa ASIGMEM. c muestra cómo se pue<strong>de</strong> invocar a calloc í ) para asignar memoria para un<br />

array. Cuando se ejecuta el programa, se pi<strong>de</strong> al usuario teclear el tamaño <strong>de</strong> un array, y si se contesta<br />

a<strong>de</strong>cuadam<strong>en</strong>te el programa g<strong>en</strong>era un array <strong>de</strong> números <strong>en</strong>teros aleatorios. A su vez, g<strong>en</strong>era otro array<br />

con los mismos valores pero sin duplicida<strong>de</strong>s; este segundo array se crea dinámicam<strong>en</strong>te con la función<br />

realloc ( ) . La estrategia para reservar memoria es llamar a redlloc ( ) para expandir el array cada 10<br />

valores; es <strong>de</strong>cir, primero se asigna memoria para 10 valores y cuando se ha completado se asignan<br />

otros 10 y así sucesivam<strong>en</strong>te.<br />

#inclu<strong>de</strong> istdio.h><br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine S 10<br />

#<strong>de</strong>fine NUM 99<br />

struct array<br />

i<br />

int *v; /* puntero al array */<br />

int n; /* numero <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l iirrdy */<br />

1;<br />

type<strong>de</strong>f struct array vector'; /* dcclaracion <strong>de</strong>l nuevo tipo: vector */<br />

void g<strong>en</strong>-array(vector* inic); /* q<strong>en</strong>erci drray con n valores */<br />

void nuevo-array(vector inic, vector* nd); /* g<strong>en</strong>era nuevo<br />

vcctor sin diipli.cados */


370 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

void escribe-array(vector w);<br />

int main0<br />

i<br />

vector prim, <strong>de</strong>st;<br />

do i<br />

printf("\nNumero <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array: ");<br />

scanf ("%d", &prim.n);<br />

}while (prim.nv = (int*)calloc(inic->n,sizeof(int)); /*reserva memoria */<br />

for (k = O; k< inic->n; k++)<br />

inic->v[kl = random(NUM)+l; /* g<strong>en</strong>era valores <strong>en</strong>teros <strong>de</strong> 1 a NUM */<br />

1<br />

void escribe-array(vector w)<br />

i<br />

int k;<br />

printf("\n\t Valores que conti<strong>en</strong>e el vector\n");<br />

for (k = O; k< w.n; k++)<br />

printf('%d%c',~.v[k],(ktl)&19==0 ?'\n':' ');/*cada 19 <strong>en</strong>teros salta<br />

<strong>de</strong> linea*/<br />

1<br />

void nuevo-array(vector inic, vector* nd)<br />

i<br />

int k, tam;<br />

/* Reserva inicial <strong>de</strong> memoria para 10 valores */<br />

nd->v = NULL;<br />

tam = sizeof(int)*S;<br />

nd->v = (int*) realloc (nd->v, tam) ;<br />

/* copia el primer elem<strong>en</strong>to */<br />

nd->v[O] = inic.v[O];<br />

nd->n = 1;<br />

/* copia los <strong>de</strong>mas elem<strong>en</strong>tos si no estan ya <strong>en</strong> el array.<br />

Cu<strong>en</strong>ta los elem<strong>en</strong>tos copiados para reservar memoria */<br />

for (k = 1; k< inic.n; kt+)<br />

int j , dup;<br />

j=dup= O;<br />

while ( (jn)&si<br />

!dup)<br />

i<br />

dup = inic.v[kl~=nd->v[j++l;<br />

i<br />

I


-. -<br />

if ( !dup)<br />

i<br />

if (nd->nBS == O) /* amplid memoria */<br />

I<br />

tarn += sizeof(int)*S;<br />

nd->v =(int*)realloc(nd->v,tam);<br />

Asignación dinámica <strong>de</strong> memoria 371<br />

/*<br />

asigna el elem<strong>en</strong>to. Los indices <strong>en</strong> C estan <strong>en</strong> el rango <strong>de</strong> O a n-1, por<br />

esa raion se dsiqna y <strong>de</strong>spues se increm<strong>en</strong>ta.<br />

*/<br />

nd->v[nd->n++l = inic.v[kl;<br />

i<br />

11.5.2. Asignación <strong>de</strong> memoria para un array <strong>de</strong> <strong>estructura</strong>s<br />

El programa ASIGNA ES.^ <strong>de</strong>fine varios mo<strong>de</strong>los <strong>de</strong> <strong>estructura</strong>s para repres<strong>en</strong>tar un curso <strong>de</strong><br />

perfeccionami<strong>en</strong>to, al que asist<strong>en</strong> varios alumnos <strong>de</strong> diversos <strong>de</strong>partam<strong>en</strong>tos <strong>de</strong> una empresa. Se <strong>de</strong>clara<br />

una <strong>estructura</strong> persona, una <strong>estructura</strong> alumno, otra profesor y la <strong>estructura</strong> curso. Un alumno es<br />

una persona y los campos <strong>de</strong>pdrtam<strong>en</strong>to y nivel. El profesor es una persona y el campo expe<br />

años <strong>de</strong> experi<strong>en</strong>cia. El curso consta <strong>de</strong> N alumnos y un profesor, a<strong>de</strong>más <strong>de</strong>l número <strong>de</strong> días <strong>de</strong><br />

duración y la <strong>de</strong>scripción <strong>de</strong>l curso. El programa utiliza funciones <strong>de</strong> asignación <strong>de</strong> memoria dinámica<br />

para asignar memoria que cont<strong>en</strong>ga las cad<strong>en</strong>as <strong>de</strong> caracteres y un array <strong>de</strong> N <strong>estructura</strong>s al umno; <strong>de</strong>fine<br />

una función que recibe una cad<strong>en</strong>a y reserva memoria para cont<strong>en</strong>er la cad<strong>en</strong>a; la función <strong>de</strong> biblioteca<br />

strcpy( ) se utiliza para copiar una constante <strong>de</strong> cad<strong>en</strong>a <strong>en</strong> la memoria reservada. El programa da<br />

<strong>en</strong>trada a los <strong>datos</strong> referidos anteriorm<strong>en</strong>te y visualiza el cont<strong>en</strong>ido <strong>de</strong>l curso.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

type<strong>de</strong>f struct persona<br />

I<br />

char* nom;<br />

int edad;<br />

char* dir;<br />

i PERSONA;<br />

type<strong>de</strong>f struct dlumno<br />

PERSONA p;<br />

char* <strong>de</strong>par;<br />

short nivel;<br />

}ALUMNO ;<br />

type<strong>de</strong>f struct profesor<br />

i<br />

PERSONA p;<br />

short expe;<br />

} PROFESOR;<br />

struct curso


372 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

ALUMNO* ptral;<br />

PROFESOR* pf;<br />

char* <strong>de</strong>scrip;<br />

short dias;<br />

short n; /* Numero <strong>de</strong> dlumnos <strong>de</strong>l curso */<br />

I;<br />

char* asigcad(void) ;<br />

PERSONA* asigper(void);<br />

PROFESOR* asigprof(void);<br />

ALUMNO* asigalms(short n);<br />

int main()<br />

t<br />

struct curso dom;<br />

int J ;<br />

1<br />

printf("\n\tCurso <strong>de</strong> perfeccionami<strong>en</strong>to.\nDescripcion <strong>de</strong>l curso: ");<br />

dom.<strong>de</strong>scrip = asigcado;<br />

printf ("Dias lectivos <strong>de</strong>l curso: ") ;<br />

scanf ("%d%*c", &dom.dias);<br />

printf("\t Datos <strong>de</strong>l profesor <strong>de</strong>l curso.\n");<br />

dom.pf = asigprof();<br />

printf('\t Numero <strong>de</strong> alumnos <strong>de</strong>l curso: ");<br />

scanf ("%d%*c",&dom.n) ;<br />

dom.ptra1 = asigalms(dom.n);<br />

/* Mustra <strong>de</strong> los <strong>datos</strong> <strong>de</strong>l curso */<br />

printf ("\n\n\t\t Curso: %s\n",dom.<strong>de</strong>scrip);<br />

puts ("\t\t-- ") ;<br />

print f ( "\ t Prof esor : %s\n", dorn. pf ->p. nom) ;<br />

printf('\tRelacion <strong>de</strong> asist<strong>en</strong>tes al curso\n");<br />

for (j = O; jp.nom);<br />

1<br />

return O;<br />

char* asigcad ( )<br />

i<br />

char b[1211, *cd;<br />

gets (b);<br />

cd = (char*) malloc ( (strl<strong>en</strong>(b) +1) *sizeof(char ) ;<br />

if (cd == NULL)<br />

í<br />

puts("\n\t!! Error <strong>de</strong> dsignacion <strong>de</strong> memoria, fin <strong>de</strong> ejecucion.! !");<br />

exit(-1);<br />

i<br />

I<br />

strcpy (cd, b) ;<br />

return cd;<br />

PERSONA" asigpero<br />

i<br />

PERSONA* p;<br />

p = (PERSONA*)malloc(sizeof (PERSONA ) ;


~-<br />

Asignación dinámica <strong>de</strong> memoria<br />

printf ("\nNombre: ") ; p->nom = asigcado ;<br />

printf ("Edad: ") ; scanf ("%d%*c", &p->edad);<br />

printf ("Direccion: ") ; p->dir = asigcad() ;<br />

return p;<br />

1<br />

PROFESOR* asigprofo<br />

i<br />

PROFESOR* t;<br />

t =(PROFESOR*)malloc(sizeof(PERSONA));<br />

t ->p = *asigper();<br />

printf ("\nAfios <strong>de</strong> experi<strong>en</strong>cia: ") ;<br />

scanf ("%d%*c", &t->expe);<br />

return t ;<br />

i<br />

ALUMNO* asigalms(short n)<br />

{<br />

}<br />

int j;<br />

ALUMNO* a;<br />

a = (ALUMNO*)calloc(n,sizeof(ALUMNO));<br />

if (a == NULL)<br />

{<br />

puts('\n\t! !Error <strong>de</strong> asignacion <strong>de</strong> memoria, fin <strong>de</strong> ejecucion.!!');<br />

exit (-1);<br />

1<br />

/* Entrada <strong>de</strong> <strong>datos</strong> <strong>de</strong> cada alumno */<br />

for (j=O; jp = *asigper();<br />

printf("Departam<strong>en</strong>to al que pert<strong>en</strong>ece: ");<br />

(a+j)-><strong>de</strong>par = asigcado;<br />

printf ("Nivel <strong>en</strong> que se <strong>en</strong>cu<strong>en</strong>tra: ") ;<br />

scanf ("%d%*c",&(a+j) ->nivel);<br />

i<br />

return a;<br />

11.6. ARRAYS DINÁMICOS<br />

Un nombre <strong>de</strong> un array es realm<strong>en</strong>te un puntero constante que se asigna <strong>en</strong> tiempo <strong>de</strong> compilación:<br />

float m[30]; /* m es un puntero constante a un bloque <strong>de</strong> 30 €loat*/<br />

float" const p = (float*)m~lloc(30*sizeofo);<br />

m y p son punteros constantes a bloques <strong>de</strong> 30 números reales (float). La <strong>de</strong>claración <strong>de</strong> m se d<strong>en</strong>omina<br />

ligadura estática <strong>de</strong>bido a que se asigna <strong>en</strong> tiempo <strong>de</strong> compilación; el símbolo se <strong>en</strong>laza a la memoria<br />

asignada aunque el array no se utiliza nunca durante la ejecución <strong>de</strong>l programa.<br />

Por el contrario, se pue<strong>de</strong> utilizar un puntero no constante para posponer la asignación <strong>de</strong> memoria<br />

hasta que el programa se esté ejecutando. Este tipo <strong>de</strong> <strong>en</strong>lace o ligadura se d<strong>en</strong>omina ligadura dinámica<br />

o ligadura <strong>en</strong> tiempo <strong>de</strong> ejecución<br />

float" p = (float*)rnal~oc(3Oxsizeof(float));<br />

Un array que se <strong>de</strong>clara <strong>de</strong> este modo se d<strong>en</strong>omina array dinámico.


374 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Comparar los dos métodos <strong>de</strong> <strong>de</strong>finición <strong>de</strong> un array<br />

0 float m[301; /* array estático */<br />

float* p=(float*)malloc(30*sizeof(flo,=it)); /* array dinámico*/<br />

El array estático m se crea <strong>en</strong> tiempo <strong>de</strong> compilación; su memoria permanece asignada durante<br />

toda la ejecución <strong>de</strong>l programa. El arrav dinámico se crea <strong>en</strong> tiempo <strong>de</strong> ejecución; su memoria se asigna<br />

sólo cuando se ejecuta su <strong>de</strong>claración. No obstante, la memoria asignada al array p se libera tan pronto<br />

como se invoca a la función free ( ) , <strong>de</strong> este modo<br />

free (p) ;<br />

11.7. REGLAS DE FUNCIONAMIENTO DE LA ASlGNAClÓN DE MEMORIA<br />

Como ya se ha com<strong>en</strong>tado se pue<strong>de</strong> asignar espacio para cualquier objeto dato <strong>de</strong> C. Las reglas para<br />

utilizar las funciones malloc ( ) , cal 1 oc ( ) , realloc ( ) y free ( ) como medio para obt<strong>en</strong>erAiberar<br />

espacio libre <strong>de</strong> memoria son las sigui<strong>en</strong>tes:<br />

1. El prototipo <strong>de</strong> las funciones esta <strong>en</strong> stdl ib. h.<br />

#inclu<strong>de</strong>


Asignación dinámica <strong>de</strong> memoria 375<br />

6. Las funciones <strong>de</strong> asignación <strong>de</strong> memoria <strong>de</strong>vuelv<strong>en</strong> NULL si no han podido reservar la memoria<br />

requerida.<br />

double" v;<br />

v = malloc(l000*sizeof(double));<br />

if (v == NULL)<br />

i<br />

puts ("Error <strong>de</strong> asignación <strong>de</strong> memoria. " ) ;<br />

exit(-1);<br />

}<br />

7. Se pue<strong>de</strong> utilizar cualquier función <strong>de</strong> asignación <strong>de</strong> memoria para reservar espacio <strong>de</strong> objetos<br />

más complejos, tales como <strong>estructura</strong>s, arrays, <strong>en</strong> el almac<strong>en</strong>ami<strong>en</strong>to libre.<br />

#inclu<strong>de</strong> <br />

struct complejo<br />

float x,y;<br />

1;<br />

void main()<br />

I<br />

struct complejo* pz;<br />

/* Puntero a <strong>estructura</strong> complejo */<br />

int n;<br />

scanf /"%d",&n);<br />

/* Asigna memoria para un array <strong>de</strong> tipo complejo */<br />

pz = (struct complejo *)calloc(n,sizeof(struct complejo));<br />

if (pz == NULL)<br />

i<br />

puts ("Error <strong>de</strong> asignación <strong>de</strong> memoria.") ;<br />

exit(-1);<br />

1<br />

I<br />

8. Se pued<strong>en</strong> crear arrays multidim<strong>en</strong>sionales <strong>de</strong> objetos con las funciones <strong>de</strong> asignación <strong>de</strong><br />

memoria. Para un array bidim<strong>en</strong>sional n x m, se asigna <strong>en</strong> primer lugar memoria para un array <strong>de</strong><br />

punteros (<strong>de</strong> IZ elem<strong>en</strong>tos), y <strong>de</strong>spués se asigna memoria para cada fila (m elem<strong>en</strong>tos) con un<br />

bucle <strong>de</strong>s<strong>de</strong> O a n- 1.<br />

#inclu<strong>de</strong> <br />

double **mat;<br />

int n,m,i;<br />

mat = (double**)malloc(n*sizeof(double*));/* array <strong>de</strong> punteros */<br />

for (i=O; i


376 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

11 -8. RESUMEN<br />

La asignación dinámica <strong>de</strong> memoria permite utilizar<br />

tanta memoria como se necesite. Se pue<strong>de</strong> asignar<br />

espacio a una variable <strong>en</strong> el almac<strong>en</strong>ami<strong>en</strong>to libre<br />

cuando se necesite y se libera la memoria cuando se<br />

<strong>de</strong>see.<br />

En C se utilizan las funciones malloc ( ) ,<br />

calloc ( ) , realloc ( ) y free( ) para asignar<br />

y liberar memoria. Las funciones malloc (1,<br />

calloc ( ) , realloc ( f permit<strong>en</strong> asignar memoria<br />

para cuaIquier tipo <strong>de</strong> dato especificado (un int,<br />

un float, una <strong>estructura</strong>, un array o cualquier otro<br />

tipo <strong>de</strong> dato).<br />

Cuando se termina <strong>de</strong> utilizar un bloque <strong>de</strong><br />

memoria, se pue<strong>de</strong> liberar con la función free ( ) . La<br />

memoria libre se <strong>de</strong>vuelve al almac<strong>en</strong>ami<strong>en</strong>to libre,<br />

<strong>de</strong> modo que quedará más memoria disponible para<br />

asignar otros bloques <strong>de</strong> memoria.<br />

El sigui<strong>en</strong>te ejemplo asigna un array y llama a la<br />

función free ( ) que libera el espacio ocupado <strong>en</strong><br />

memoria:<br />

type<strong>de</strong>f struct animal<br />

t<br />

...<br />

)ANIMAL:<br />

ANIMAL* pperro;<br />

pperro 5<br />

(mIw*)malloc(5*sizeof(ANIMAL)<br />

if (pperro == NULL)<br />

puts(''jFa1ta memoria!") ;<br />

else<br />

{<br />

1<br />

/* uso <strong>de</strong> pperro */<br />

) ;<br />

free(pperr0); /* libera espacio<br />

<strong>de</strong> pperro */<br />

I<br />

I<br />

I<br />

1<br />

I.<br />

I<br />

t<br />

11.9. EJERCICIOS<br />

11.1. Encu<strong>en</strong>tre los errores <strong>en</strong> las sigui<strong>en</strong>tes <strong>de</strong>claraciones<br />

y s<strong>en</strong>t<strong>en</strong>cias.<br />

int n, *p;<br />

char** dob= "Cad<strong>en</strong>a <strong>de</strong> dos<br />

punteros" ;<br />

p = n*malloc(sizeof(int));<br />

11.2. Dada la sigui<strong>en</strong>te <strong>de</strong>claración, <strong>de</strong>finir un puntero<br />

b a la <strong>estructura</strong>, reservar memoria dinámicam<strong>en</strong>te<br />

para una <strong>estructura</strong> asignando su<br />

dirección a b.<br />

struct boton<br />

1.<br />

char* rotulo;<br />

int codigo;<br />

1;<br />

11.3. Una vez asignada memoria al puntero b <strong>de</strong>l<br />

Ejercicio 1 1.2 escribir s<strong>en</strong>t<strong>en</strong>cias para leer los<br />

campos rotulo y codigo.<br />

11.4. Un array unidim<strong>en</strong>sional pue<strong>de</strong> consi<strong>de</strong>rarse<br />

una constante puntero. ¿Cómo pue<strong>de</strong> consi<strong>de</strong>rarse<br />

un array bidim<strong>en</strong>sional?, ¿y un array <strong>de</strong><br />

tres dim<strong>en</strong>siones?<br />

11.5. Declara una <strong>estructura</strong> para repres<strong>en</strong>tar un<br />

punto <strong>en</strong> el espacio tridim<strong>en</strong>sional. Declara un<br />

puntero a la <strong>estructura</strong> para que t<strong>en</strong>ga la<br />

dirección <strong>de</strong> un array dinámico <strong>de</strong> n <strong>estructura</strong>s<br />

punto. Utiliza la función calloc ( ) para<br />

asignar memoria al array y comprueba que se<br />

ha podido asignar la memoria requerida.<br />

11.6. ¿Qué difer<strong>en</strong>cias exist<strong>en</strong> <strong>en</strong>tre las funciones<br />

malloc ( ) , calloc ( ) y realloc ( ) ?<br />

11.7. Dada la <strong>de</strong>claración <strong>de</strong> la <strong>estructura</strong> punto<br />

(Ejercicio 11.5) escribe una función que<br />

<strong>de</strong>vuelva la dirección <strong>de</strong> un array dinámico <strong>de</strong><br />

n puntos <strong>en</strong> el espacio tridim<strong>en</strong>sional. Los<br />

valores <strong>de</strong> los <strong>datos</strong> se le<strong>en</strong> <strong>de</strong>l dispositivo <strong>de</strong><br />

<strong>en</strong>trada (teclado).<br />

k<br />

c<br />

L


P-<br />

Asignación dinámica <strong>de</strong> memoria 377<br />

11.8.<br />

11.9.<br />

Dada la <strong>de</strong>claración <strong>de</strong>l array <strong>de</strong> punteros:<br />

#<strong>de</strong>fine N 4<br />

char *[NI ;<br />

Escriba las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> código para leer<br />

N lineas <strong>de</strong> caracteres y asignar cada línea a<br />

un elem<strong>en</strong>to <strong>de</strong>l array.<br />

Escriba una funci6n que reciba el array dinámico<br />

creado <strong>en</strong> el Ejercicio 11.7 y amplíe el<br />

array <strong>en</strong> otros m puntos <strong>de</strong>l espacio.<br />

11.10. Escriba una función que reciba las N líneas<br />

<strong>de</strong>claraciones?:<br />

char *c[15] ;<br />

char **c,<br />

char c[15J 1121;<br />

11 .I O. PROBLEMAS<br />

En todos los problemas, utilice siempre que sea<br />

posible punteros para acce<strong>de</strong>r a los elem<strong>en</strong>tos <strong>de</strong> los<br />

arrays, tanto numéricos como cad<strong>en</strong>as <strong>de</strong> caracteres.<br />

11.1. Escriba un programa para leer n cad<strong>en</strong>as <strong>de</strong><br />

caracteres. Cada cad<strong>en</strong>a ti<strong>en</strong>e una longitud<br />

variable y está formada por cualquier carácter.<br />

La memoria que ocupa cada cad<strong>en</strong>a se ha <strong>de</strong><br />

ajustar al tamaño que ti<strong>en</strong>e. Una vez leídas las<br />

cad<strong>en</strong>as se <strong>de</strong>be <strong>de</strong> realizar un proceso que<br />

consiste <strong>en</strong> eliminar todos los blancos, siempre<br />

mant<strong>en</strong>i<strong>en</strong>do el espacio ocupado ajustado al<br />

número <strong>de</strong> caracteres. El programa <strong>de</strong>be<br />

mostrar las cad<strong>en</strong>as leídas y las cad<strong>en</strong>as<br />

transformadas.<br />

11.2, Se <strong>de</strong>sea escribir un programa para leer<br />

números gran<strong>de</strong>s (<strong>de</strong> tantos dígitos que no<br />

<strong>en</strong>tran <strong>en</strong> variables long) y obt<strong>en</strong>er la suma<br />

<strong>de</strong> ellos. El almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> un número<br />

gran<strong>de</strong> se ha <strong>de</strong> hacer <strong>en</strong> una <strong>estructura</strong> que<br />

t<strong>en</strong>ga un array dinámico y otro campo con el<br />

número <strong>de</strong> dígitos. La suma <strong>de</strong> dos números<br />

gran<strong>de</strong>s dará como resultado otro número<br />

gran<strong>de</strong> repres<strong>en</strong>tado <strong>en</strong> su correspondi<strong>en</strong>te<br />

<strong>estructura</strong>.<br />

11.3. En una competición <strong>de</strong> ciclismo se pres<strong>en</strong>tan n<br />

ciclistas. Cada participante se repres<strong>en</strong>ta por el<br />

nombre, club, los puntos obt<strong>en</strong>idos y prueba <strong>en</strong><br />

que participará <strong>en</strong> la competición. La competi-<br />

ción es por eliminación. Hay prueba <strong>de</strong> dos<br />

e elimina. En la <strong>de</strong><br />

velocidad participan 4 ciclistas, el más rápido<br />

obti<strong>en</strong>e 4 puntos el segundo 1 y el cuarto se elimina.<br />

Las pruebas se van alternando, empezando<br />

por velocidad. Los ciclistas participantes<br />

<strong>en</strong> una prueba se elig<strong>en</strong> al azar <strong>en</strong>tre los que<br />

<strong>en</strong> m<strong>en</strong>os pruebas han participado. El juego<br />

termina cuando no quedan ciclistas para alguna<br />

<strong>de</strong> las dos pruebas. Se ha <strong>de</strong> mant<strong>en</strong>er arrays<br />

dinámicos con los ciclistas participantes y los<br />

eliminados. El ciclista ganador será el que más<br />

puntos t<strong>en</strong>ga.<br />

11.4. Se ti<strong>en</strong>e una matriz <strong>de</strong> 20x20 elem<strong>en</strong>tos <strong>en</strong>teros.<br />

En la matriz hay un elem<strong>en</strong>to repetido<br />

muchas veces. Se quiere g<strong>en</strong>erar otra matriz <strong>de</strong><br />

20 filas y que <strong>en</strong> cada fila estén ~610 los elem<strong>en</strong>tos<br />

no repetidos. Escribir un programa que<br />

t<strong>en</strong>ga como <strong>en</strong>trada la matriz <strong>de</strong> 20x20, g<strong>en</strong>ere<br />

la matriz dinámica pedida y se muestre <strong>en</strong> pantalla.<br />

11.5. Escriba un programa para g<strong>en</strong>erar una matriz<br />

simétrica con nítmeros aleatorios <strong>de</strong> 1 a 9. El<br />

usuario introduce el tamaño <strong>de</strong> cada dim<strong>en</strong>sión<br />

<strong>de</strong> la matriz y el programa reserva memoria<br />

libre para el tamaño requerido.


CAPíTULO 12<br />

CADENAS<br />

CONTENIDO<br />

12.1. Concepto <strong>de</strong> cad<strong>en</strong>a.<br />

12.2. Lectura <strong>de</strong> cad<strong>en</strong>as.<br />

12.3. La biblioteca string. h.<br />

12.4. Arrays y cad<strong>en</strong>as como<br />

parámetros <strong>de</strong> funciones.<br />

12.5. Asignación <strong>de</strong> cad<strong>en</strong>as.<br />

12.6. Longitud y concat<strong>en</strong>ación<br />

<strong>de</strong> cad<strong>en</strong>as.<br />

12.7. Comparación <strong>de</strong> cad<strong>en</strong>as.<br />

18.8. Inversión <strong>de</strong> cad<strong>en</strong>as.<br />

12.9. Conversión <strong>de</strong> cad<strong>en</strong>as.<br />

12.10. Conversión <strong>de</strong> cad<strong>en</strong>as a<br />

números.<br />

12.1 1. Búsqueda <strong>de</strong> caracteres y<br />

cad<strong>en</strong>as.<br />

12.12. Resum<strong>en</strong>.<br />

12.13. Ejercicios.<br />

12.14. Problemas.<br />

378


1<br />

INTRODWCCI~N<br />

El l<strong>en</strong>guaje C no ti<strong>en</strong>e <strong>datos</strong> pre<strong>de</strong>finidos tipo cad<strong>en</strong>a (string). En su lugar 6,<br />

manipula cad<strong>en</strong>as mediante arrays <strong>de</strong> caracteres que terminan con el carhter<br />

nulo ASCII (W’). Una cad<strong>en</strong>a se consi<strong>de</strong>ra como un array unidim<strong>en</strong>sional <strong>de</strong><br />

tipo char o unsigned char. En este capítulo se estudiarán temas tales como:<br />

I<br />

O cad<strong>en</strong>as<strong>en</strong>C;<br />

O lectura y salida <strong>de</strong> cad<strong>en</strong>as;<br />

O uso <strong>de</strong> funciones <strong>de</strong> cad<strong>en</strong>a <strong>de</strong> la biblioteca estándar;<br />

O asignación <strong>de</strong> cad<strong>en</strong>as;<br />

O operaciones diversas <strong>de</strong> cad<strong>en</strong>a (longitud, concat<strong>en</strong>ación, comparación y<br />

conversión);<br />

O localización <strong>de</strong> caracteres y subcad<strong>en</strong>as;<br />

O inversión <strong>de</strong> los caracteres <strong>de</strong> una cad<strong>en</strong>a.<br />

CONCEPTOS CLAVE<br />

O Asigmación. O Comparación.<br />

O Biblioteca string..h. O Conversión.<br />

O Cad<strong>en</strong>a. O F’unciones <strong>de</strong> cad<strong>en</strong>a.<br />

O Cad<strong>en</strong>avacía. O Inversión.<br />

O Carácter nulo (NULL, ’ \O ’ ) . O String.<br />

379 I


380 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

12.1. CONCEPTO DE CADENA<br />

Una cad<strong>en</strong>a (también llamada constante <strong>de</strong> cad<strong>en</strong>a o literal <strong>de</strong> cad<strong>en</strong>a) es un tipo <strong>de</strong> dato compuesto,<br />

un array <strong>de</strong> caracteres (char), terminado por un carácter nulo (' \ O '), NULL (Fig. 12.1). Un ejemplo es<br />

" ABC ".<br />

Cuando la cad<strong>en</strong>a aparece d<strong>en</strong>tro <strong>de</strong> un programa se verá como si se almac<strong>en</strong>arán cuatro elem<strong>en</strong>tos:<br />

I A ' , ' B ' , ' c I y ' \ O ' . En consecu<strong>en</strong>cia, se consi<strong>de</strong>rará que la cad<strong>en</strong>a "ABC" es un array <strong>de</strong> cuatro<br />

elem<strong>en</strong>tos <strong>de</strong> tipo char. El valor real <strong>de</strong> esta cad<strong>en</strong>a es la dirección <strong>de</strong> su primer carácter y su tipo es<br />

un puntero a char. Aplicando el operador * a un puntero a char se obti<strong>en</strong>e el carácter que forma su<br />

cont<strong>en</strong>ido; es posible también utilizar aritmética <strong>de</strong> direcciones con cad<strong>en</strong>as:<br />

* "ABC 11 es igual a 'A'<br />

* ( "ABC" + 1) es igual a 'B'<br />

* ("ABC" + 2) es igual a 'C'<br />

* ( "ABC" + 3) es igual a '\O'<br />

De igual forma, utilizando el subíndice <strong>de</strong>l array se pue<strong>de</strong> escribir:<br />

es igual a<br />

es igual a<br />

es igual a<br />

es igual a<br />

'A'<br />

'B'<br />

IC'<br />

#\O'<br />

(a) La cad<strong>en</strong>a <strong>de</strong> t e s t<br />

(b La cad<strong>en</strong>a <strong>de</strong> test\o<br />

Figura 12.1. (a) array <strong>de</strong> caracteres; (b) cad<strong>en</strong>a <strong>de</strong> caracteres.<br />

<strong>de</strong> una cad<strong>en</strong>a <strong>en</strong> C es siempre i<br />

la longitud <strong>de</strong> la cad<strong>en</strong>a<br />

Ejemplos<br />

1. char cad [ ] = "Lupiana";<br />

cad ti<strong>en</strong>e ocho caracteres; 'L', 'u', 'p', 'i', 'a', ln,, 'a'<br />

y '\O'<br />

2. printf ("%s", cad) ;<br />

el sistema copiará caracteres <strong>de</strong> cad a stdout (pantalla) hasta que el carácter NULL, ' \ O ', se<br />

<strong>en</strong>cu<strong>en</strong>tre.<br />

3. scanf (''%SI', cad) ;<br />

el sistema copiará caracteres <strong>de</strong>s<strong>de</strong> stdin (teclado) a cad hasta que se <strong>en</strong>cu<strong>en</strong>tre un carácter<br />

espacio <strong>en</strong> blanco o fin <strong>de</strong> línea. El usuario ha <strong>de</strong> asegurarse que el buffer cad esté <strong>de</strong>finido como<br />

una cad<strong>en</strong>a <strong>de</strong> caracteres lo sufici<strong>en</strong>te gran<strong>de</strong> para cont<strong>en</strong>er la <strong>en</strong>trada.


Cad<strong>en</strong>as 381<br />

Las funciones <strong>de</strong>claradas <strong>en</strong> el archivo <strong>de</strong> cabecera atring. h> se utilizan para manipular<br />

cad<strong>en</strong>as.<br />

12.1.1. Declaración <strong>de</strong> variables <strong>de</strong> cad<strong>en</strong>a<br />

Las cad<strong>en</strong>as se <strong>de</strong>claran como los restantes tipos <strong>de</strong> arrays. El operador postfijo [ 1 conti<strong>en</strong>e el tamaño<br />

máximo <strong>de</strong>l objeto. El tipo base, naturalm<strong>en</strong>te, es char, o bi<strong>en</strong> unsigned char:<br />

char texto [ 811 ; /* una línea <strong>de</strong> caracteres <strong>de</strong> texto */<br />

char ord<strong>en</strong> [ 4 O ] ; /* cad<strong>en</strong>a utilizada para recibir una ord<strong>en</strong> <strong>de</strong>l<br />

teclado */<br />

unsigned char <strong>datos</strong>; /* pue<strong>de</strong> cont<strong>en</strong>er cualquier carácter ASCII */<br />

El tipo unsigned char pue<strong>de</strong> ser <strong>de</strong> interés <strong>en</strong> aquellos casos <strong>en</strong> que los caracteres especiales<br />

pres<strong>en</strong>tes puedan t<strong>en</strong>er el bit <strong>de</strong> ord<strong>en</strong> alto activado. Si el carácter se consi<strong>de</strong>ra con signo, el bit <strong>de</strong> mayor<br />

peso (ord<strong>en</strong> alto) se interpreta como hit <strong>de</strong> signo y se pue<strong>de</strong> propagar a la posición <strong>de</strong> mayor ord<strong>en</strong><br />

(peso) <strong>de</strong>l nuevo tipo.<br />

Observe que el tamaño <strong>de</strong> la cad<strong>en</strong>a ha <strong>de</strong> incluir el carácter I \ 0 . En consecu<strong>en</strong>cia, para <strong>de</strong>finir un<br />

array <strong>de</strong> caracteres que cont<strong>en</strong>ga la cad<strong>en</strong>a "ARCDEF" , escriba<br />

char UnaCad<strong>en</strong>a [ 7 ] ;<br />

<strong>en</strong>contrar una <strong>de</strong>claración como ésta:<br />

s. Es un puntero a un car&%er (el<br />

asignada,<br />

<strong>de</strong> una<br />

12.1.2. Inicialización <strong>de</strong> variables <strong>de</strong> cad<strong>en</strong>a<br />

Todos los tipos <strong>de</strong> arrays requier<strong>en</strong> una inicialización (iniciación) que consiste <strong>en</strong> una lista <strong>de</strong> valores<br />

separados por comas y <strong>en</strong>cerrados <strong>en</strong>tre llaves.<br />

char texto[81] = "Esto es una cad<strong>en</strong>d.";<br />

char texto<strong>de</strong>mo[255] = "Esta es una cad<strong>en</strong>a muy larga";<br />

char cad<strong>en</strong>atest[] = "¿Cuál es la longitud <strong>de</strong> esta cad<strong>en</strong>a?";<br />

Las cad<strong>en</strong>as texto y texto<strong>de</strong>mo pued<strong>en</strong> cont<strong>en</strong>er 80 y 254 caracteres respectivam<strong>en</strong>te más el<br />

carácter nulo. La tercera cad<strong>en</strong>a, cad<strong>en</strong>atest, se <strong>de</strong>clara con una especificación <strong>de</strong> tipo incompleta y<br />

se completa sólo con el inicializador. Dado que <strong>en</strong> el literal hay 36 caracteres y el compilador aña<strong>de</strong> el<br />

carácter I \O', un total <strong>de</strong> 37 caracteres se asignarán a cad<strong>en</strong>atest.<br />

Ahora bi<strong>en</strong>, una cad<strong>en</strong>a no se pue<strong>de</strong> inicializar fuera <strong>de</strong> la <strong>de</strong>claración. Por ejemplo, si trata <strong>de</strong> hacer<br />

Unacad<strong>en</strong>a = "ABC" ;<br />

C le dará un error al compilar. La razón es que un id<strong>en</strong>tificador <strong>de</strong> cad<strong>en</strong>a, como cualquier<br />

id<strong>en</strong>tificador <strong>de</strong> array se trata como un valor <strong>de</strong> dirección, como un puntero constante. Cómo se pue<strong>de</strong><br />

inicializar una cad<strong>en</strong>a fuera <strong>de</strong> la <strong>de</strong>cluración? Más a<strong>de</strong>lante se verá, pero po<strong>de</strong>mos indicar que será<br />

necesario utilizar una función <strong>de</strong> cad<strong>en</strong>a d<strong>en</strong>ominada strcpy ( ) .


382 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 12.1<br />

Las cad<strong>en</strong>as terminan con el carácter nulo. Así <strong>en</strong> el sigui<strong>en</strong>te programa se muestra que el carácter<br />

NULL ( ' \ 0 'I se aña<strong>de</strong> a la cad<strong>en</strong>a:<br />

#inclu<strong>de</strong> <br />

int main( )<br />

{<br />

i<br />

char S [ 1 = "ABCD";<br />

for (int i = O; i < 5; i++)<br />

print f ("S[%dl = %c\n", i , S [ i 1 ) ;<br />

return O;<br />

Ej ecu c i Ón<br />

S[O] = A<br />

S[1] = B<br />

S[21 = C<br />

S[3] = D<br />

S[4] =<br />

Com<strong>en</strong>tario: Cuando el carácter NUL,L se mdndd imprimir, no escribe nada.<br />

12.2. LECTURA DE CADENAS<br />

La lectura usual <strong>de</strong> <strong>datos</strong> se realiza con la función scanf ( ) , cuando se aplica a <strong>datos</strong> cad<strong>en</strong>a el código<br />

<strong>de</strong> formato es %s. La función da por terminada la cad<strong>en</strong>a cuando <strong>en</strong>cu<strong>en</strong>tra un espacio (un blanco) o fin<br />

<strong>de</strong> línea. Esto pue<strong>de</strong> producir anomalías al no po<strong>de</strong>r captar cad<strong>en</strong>as con blancos <strong>en</strong>tre caracteres. Así, por<br />

ejemplo, trate <strong>de</strong> ejecutar el sigui<strong>en</strong>te programa:<br />

/* Este programa muestra cómo scanfolee <strong>datos</strong> cad<strong>en</strong>a */<br />

#inclu<strong>de</strong> <br />

void main()<br />

i<br />

char nombre [ 301 ; /* Define array <strong>de</strong> caracteres */<br />

i<br />

scanf ("%s", nombre) ;<br />

print f ("%s \n", nombre) ;<br />

/* Leer la cad<strong>en</strong>a */<br />

/* Escribir la cad<strong>en</strong>a nombre */<br />

El programa <strong>de</strong>fine nombre como un array <strong>de</strong> caracteres <strong>de</strong> 30 elem<strong>en</strong>tos. Suponga que introduce<br />

la <strong>en</strong>trada Pepe Margolles, cuando ejecuta el programa se visualizará <strong>en</strong> pantalla Pepe. Es <strong>de</strong>cir, la<br />

palabra Margolles no se ha asignado a la variable cad<strong>en</strong>a nombre. La razón es que la función<br />

scanf ( ) termina la operación <strong>de</strong> lectura siempre que se <strong>en</strong>cu<strong>en</strong>tra un espacio <strong>en</strong> blanco o fin <strong>de</strong> línea.<br />

Así pues, ¿,cuál será la mejor forma para lectura <strong>de</strong> cad<strong>en</strong>as, cuando estas cad<strong>en</strong>as conti<strong>en</strong><strong>en</strong> más <strong>de</strong><br />

una palabra (caso muy usual)? El método recom<strong>en</strong>dado será utilizar una función d<strong>en</strong>ominada gets ( 1.<br />

La función gets ( )permitirá leer la cad<strong>en</strong>a completa, incluy<strong>en</strong>do cualquier espacio <strong>en</strong> blanco, termina<br />

al leer el carácter <strong>de</strong> fin <strong>de</strong> línea.<br />

El prototipo <strong>de</strong> la función está <strong>en</strong> el archivo st d LO. h . La función asigna la cad<strong>en</strong>a al argum<strong>en</strong>to<br />

transmitido a la función, que será un array <strong>de</strong> caracteres o un puntero (char*) a memoria libre, con un<br />

número <strong>de</strong> elem<strong>en</strong>tos sufici<strong>en</strong>te para guardar la cad<strong>en</strong>a leída. Si ha habido un error <strong>en</strong> la lectura <strong>de</strong> la<br />

cad<strong>en</strong>a, <strong>de</strong>vuelve NULL.<br />

/* Lectura <strong>de</strong> caracteres hastd fin <strong>de</strong> líned */<br />

i<br />

1<br />

I<br />

!


Cad<strong>en</strong>as 383<br />

char b[81] ;<br />

gets (b);<br />

Ejemplo 12.2<br />

Entrada y salida <strong>de</strong> cad<strong>en</strong>as. Lectura <strong>de</strong> palabrus <strong>de</strong> 79 curucteres <strong>de</strong> rncúcima longitud <strong>en</strong> una memoria<br />

intermedia (bufer) <strong>de</strong> 80 caracteres.<br />

#inclu<strong>de</strong> <br />

void main ( 1<br />

I<br />

char palabra[80];<br />

do i<br />

scanf ("%SI', palabra) ;<br />

if ( ! feof (stdin )<br />

print f ( I' \ t \ 'I %s \ 'I \ n" , pal abr a ) ;<br />

1 while (!feof(stdin));<br />

1<br />

Al ejecutar este programa el número <strong>de</strong> veces que se repite el bucle while <strong>de</strong>p<strong>en</strong><strong>de</strong>rá <strong>de</strong>l número<br />

<strong>de</strong> palabras introducidas, incluido el carácter <strong>de</strong> control que termina el bucle control -z.<br />

Ejecución<br />

Hoy es 1 <strong>de</strong> Enero <strong>de</strong>l 2000.<br />

Mañana es Domingo.<br />

It Hoy 'I<br />

"es"<br />

1<br />

I' <strong>de</strong> I'<br />

I' Enero"<br />

'I <strong>de</strong> 1 'I<br />

"2000.<br />

"Mañana"<br />

II e II<br />

"Domingo. I'<br />

El bucle anterior se ejecuta I1 veces, una vez por cada palabra introducida (incluy<strong>en</strong>do Controlz<br />

que <strong>de</strong>ti<strong>en</strong>e el bucle). Cada palabra <strong>de</strong> la <strong>en</strong>trada (stdin) hace eco <strong>en</strong> la salida (stdout). El flujo <strong>de</strong><br />

salida no «se limpia» hasta que el flujo <strong>de</strong> <strong>en</strong>trada <strong>en</strong>cu<strong>en</strong>tra el final <strong>de</strong> la línea.<br />

Cada cad<strong>en</strong>a se imprime <strong>en</strong>cerrada <strong>en</strong>tre comillas. No será fin <strong>de</strong> archivo (f eof ( ) distinto <strong>de</strong> cero)<br />

mi<strong>en</strong>tras que no se pulse Control-Z (<strong>en</strong> Windows/DOS), que <strong>en</strong>vía el carácter final <strong>de</strong> archivo <strong>de</strong>l<br />

flujo estándar <strong>de</strong> <strong>en</strong>trada stdin.<br />

Advert<strong>en</strong>cia<br />

Los signos <strong>de</strong> puntuación, apóstrofes, comas, puntos, etc., se incluy<strong>en</strong> <strong>en</strong> las cad<strong>en</strong>as, pero no así<br />

los caracteres espacios <strong>en</strong> blanco (blancos, tabulaciones, nuevas líneas, etc.).


384 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 12.3<br />

El sigui<strong>en</strong>te programa solicita introducir un nombre, comprueba la operación y lo escribe <strong>en</strong> pantalla.<br />

#inclu<strong>de</strong> <br />

int main()<br />

char nombre[80];<br />

printf ("\nIntroduzca su nombre: ") ;<br />

if (gets(nombre)! = NULL)<br />

printf ("Hola %s ¿cómo esth usted?",nombre) ;<br />

return O;<br />

i<br />

si al ejecutarlo se introduce ia cad<strong>en</strong>a M a d Mar tind, el array nombre almac<strong>en</strong>ará los caracteres<br />

sigui<strong>en</strong>tes:<br />

nombre<br />

Mara M a r t i n =i.'\o<br />

Y.<br />

Ejemplo 11.4<br />

El sigui<strong>en</strong>te programa lee y escribe el nombre, dirección y teléfono <strong>de</strong> un usuario.<br />

#inclu<strong>de</strong> <br />

void main()<br />

{<br />

char Nombre [ 32 ] ;<br />

char Calle [32] ;<br />

char Ciudad[271;<br />

char Provincia[27];<br />

char CodigoPostal151 ;<br />

char Telefono[lO] ;<br />

print f<br />

print f<br />

printf<br />

printf<br />

print f<br />

print f<br />

/* vis<br />

'\nNombre: ") ; gets (Nombre);<br />

"\nCalle: ") ; gets (Calle);<br />

"\nCiudad: ") ; gets (Ciudad);<br />

"\nProvincid: ") ; gets (Provincia);<br />

"\nCodigo Postal: ");<br />

"\nTelefono: ") ; gets ('relefono);<br />

alizar cad<strong>en</strong>as */<br />

gets(CodigoPostd1);<br />

1<br />

printf ("\n\n%s\t Bs\n",Nombre,Calle) ;<br />

printf ("%s\t %s\n",Ciudad, Provincid) ;<br />

printf ("Bs \L %s\n",CodigoPoctal,Tel~f~no);<br />

O La llamada get c ( cad) lee todos los caracteres hasta <strong>en</strong>contrar el carácter fin <strong>de</strong> línea, \n I ,<br />

que <strong>en</strong> la cad<strong>en</strong>a cad se sustituye por ' \ O I .


Cad<strong>en</strong>as 385<br />

12.2.1. Función getchar ( )<br />

La función getchar ( ) se utiliza para leer carácter a carácter. La llamada a getchar ( ) <strong>de</strong>vuelve el<br />

carácter sigui<strong>en</strong>te <strong>de</strong>l flujo <strong>de</strong> <strong>en</strong>trada stdin. En caso <strong>de</strong> error, o <strong>de</strong> <strong>en</strong>contrar el fin <strong>de</strong> archivo, <strong>de</strong>vuelve<br />

EOF (macro <strong>de</strong>finida <strong>en</strong> stdio. h).<br />

Ejemplo 12.5<br />

El sigui<strong>en</strong>te programa cu<strong>en</strong>ta las ocurr<strong>en</strong>cias <strong>de</strong> la letra ' t ' <strong>de</strong>lflujo <strong>de</strong> <strong>en</strong>trada. Se diseña un bucle<br />

while que continúa ejecutándose mi<strong>en</strong>tras que la.finci6n getchar ( ) lee caracteres y se asignan a car.<br />

#inclu<strong>de</strong> <br />

int main()<br />

{<br />

1<br />

int car;<br />

int cu<strong>en</strong>ta = O;<br />

while ( (car = getchar ( ) ) ! =EOF)<br />

if (car == 't') ++cu<strong>en</strong>ta;<br />

printf ("\n%d letras t \n", cu<strong>en</strong>ta) ;<br />

return O;<br />

Nota<br />

La salida <strong>de</strong>l bucle es con Control - z.<br />

12.2.2. Función putchar ( )<br />

La función opuesta <strong>de</strong> getchar ( ) es putchar ( ) . La función putchar ( se utiliza para escribir <strong>en</strong> la<br />

salida (stdout) carácter a carácter. El carácter que se escribe es el transmitido como argum<strong>en</strong>to. Esta<br />

función (realm<strong>en</strong>te es una macro <strong>de</strong>finida <strong>en</strong> stdio . h) ti<strong>en</strong>e como prototipo:<br />

int putchar(int ch);<br />

Ejercicio 12.1<br />

El sigui<strong>en</strong>te programa hace «eco>> <strong>de</strong>lpujo <strong>de</strong> <strong>en</strong>trada y convierte las palabras <strong>en</strong> palabras iguales que<br />

comi<strong>en</strong>zan con letra mayúscula. Es <strong>de</strong>cir, si la <strong>en</strong>trada es "poblado <strong>de</strong> peñas rubias" se ha <strong>de</strong><br />

convertir <strong>en</strong> "Poblado De Peñas Rubias". Para realizar esa operación se recurre a la función<br />

toupper (car) que <strong>de</strong>vuelve el equival<strong>en</strong>te mayúscula <strong>de</strong> car si car es una letra minúscula. El archivo<br />

<strong>de</strong> cabecera necesario para po<strong>de</strong>r utilizar la función toupper (car) es .<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main()<br />

i<br />

char car, pre = I\n';<br />

while ( (carzgetchar ( ) ) ! =EOF)<br />

{<br />

if (pre == ' ' / I pre == '\n')<br />

putchar(toupper(car));<br />

else


386 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

putchar (car);<br />

pre = car;<br />

1<br />

return O;<br />

J<br />

Ejeciución<br />

poblado <strong>de</strong> peñas rubias con capital <strong>en</strong> Lupiana<br />

Poblado De Peñas Rubias Con Capital En Lupiana<br />

Análisis<br />

La variable pre conti<strong>en</strong>e el carácter leído anteriorm<strong>en</strong>te. El algoritmo se basa <strong>en</strong> el hecho <strong>de</strong> que si pre<br />

es un blanco o el carácter nueva línea, <strong>en</strong>tonces el carácter sigui<strong>en</strong>te car será el primer carácter <strong>de</strong> la<br />

sigui<strong>en</strong>te palabra. En consecu<strong>en</strong>cia, car, se reemplaza por su carácter mayúscula equival<strong>en</strong>te: car +<br />

'A' - 'a'.<br />

12.2.3. Función puts ( )<br />

I<br />

La función puts ( ) escribe <strong>en</strong> la salida una cad<strong>en</strong>a <strong>de</strong> caracteres, incluy<strong>en</strong>do el carácter fin <strong>de</strong> línea<br />

por los que situa el puntero <strong>de</strong> salida <strong>en</strong> la sigui<strong>en</strong>te línea. Es la función recíproca <strong>de</strong> gets ( ) ; si<br />

gets ( ) capta una cad<strong>en</strong>a hasta fin <strong>de</strong> línea, puts ( escribe una cad<strong>en</strong>a y el fin <strong>de</strong> línea. El prototipo<br />

<strong>de</strong> la función se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> stdio :<br />

int puts(const char *s);<br />

Ejercicio 12.2<br />

El programa sigui<strong>en</strong>te lee una frase y escribe <strong>en</strong> pantalla tantas líneas como palabras ti<strong>en</strong>e la frase;<br />

cada línea que escribe, a partir <strong>de</strong> la primera, sin la última palabra <strong>de</strong> la línea anterior:<br />

Análisis<br />

La función sgtepal ( ) explora los caracteres pasados <strong>en</strong> p hasta que <strong>en</strong>cu<strong>en</strong>tra el primer blanco<br />

(separador <strong>de</strong> palabras). La exploración se realiza <strong>de</strong> <strong>de</strong>recha a izquierda, <strong>en</strong> la posición <strong>de</strong>l blanco<br />

asigna \O para indicar fin <strong>de</strong> cad<strong>en</strong>a.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

void sgtepal(char* p);<br />

void main()<br />

i<br />

char linea [ 811 ;<br />

printf("\n\tIntroduce una linea <strong>de</strong> caracteres.\n");<br />

gets (linea) ;<br />

while (*linea)<br />

i<br />

puts (linea) ;<br />

sgtepal (linea) ;<br />

1<br />

1<br />

void sgtepal(char* p)<br />

{<br />

I


a<br />

Cad<strong>en</strong>as 387<br />

int j; J;<br />

j = strl<strong>en</strong>(p)-1;<br />

while(j>O && p[j]!=’ I ’))<br />

I--;<br />

p[jl p[11 = ‘\O’;<br />

i<br />

Ejecu cíón<br />

Introduce una linea <strong>de</strong> caracteres.<br />

Erase una vez la Mancha<br />

Erase una vez la Mancha<br />

Erase una vez La<br />

Erase una vez<br />

Erase una<br />

Erase<br />

i 12.2.4. Funciones getch( ( ) y getche ( )<br />

Estas dos funciones no pert<strong>en</strong>ec<strong>en</strong> a ANSI C, sin embargo, se incorporan por estar <strong>en</strong> casi todos los<br />

compiladores <strong>de</strong> C. Ambas funciones le<strong>en</strong> un carácter tecleado sin esperar el retorno <strong>de</strong> carro. La<br />

difer<strong>en</strong>cia <strong>en</strong>tre ellas resi<strong>de</strong> <strong>en</strong> que con getch ( ) el carácter tecleado no se visualiza <strong>en</strong> pantalla (no<br />

hace eco <strong>en</strong> la pantalla), y con getche ( ) si hay eco <strong>en</strong> la pantalla. La llamada a cada una <strong>de</strong> ellas:<br />

car = getch() ;<br />

car = getche() ;<br />

El prototipo <strong>de</strong> ambas funciones se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> al archivo conio . h<br />

int getch(void);<br />

int getche (void) ;<br />

Ejemplo 12.6<br />

La sigui<strong>en</strong>te funcicín <strong>de</strong>vuelve el carácter s o N :<br />

I<br />

: #inclu<strong>de</strong> <br />

i<br />

#inclu<strong>de</strong> <br />

int respuesta ( )<br />

i<br />

char car;<br />

do<br />

car = toupper(getche0 1;<br />

while (car != ’S‘ && car !=’NI);<br />

return car;<br />

12.3. LA BIBLIOTECA STRING. H<br />

La biblioteca estándar <strong>de</strong> C conti<strong>en</strong>e la biblioteca <strong>de</strong> cad<strong>en</strong>a STRING. 11, que incorpora las funciones <strong>de</strong><br />

manipulación <strong>de</strong> cad<strong>en</strong>as utilizadas más frecu<strong>en</strong>tem<strong>en</strong>te. El archivo <strong>de</strong> cabecera STDIO. H también


388 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

soporta E/S <strong>de</strong> cad<strong>en</strong>as. Algunos fabricantes <strong>de</strong> C también incorporan otras bibliotecas para manipular<br />

cad<strong>en</strong>as, pero como no son estándur no se consi<strong>de</strong>rarán <strong>en</strong> esta sección. Las funciones <strong>de</strong> cad<strong>en</strong>a ti<strong>en</strong><strong>en</strong><br />

argum<strong>en</strong>tos <strong>de</strong>clarados <strong>de</strong> forma similar a:<br />

char *SI; o bi<strong>en</strong>, const char *s1;<br />

Esto significa que la función espera una cad<strong>en</strong>a que pue<strong>de</strong> o no modificarse. Cuando se utiliza la<br />

función, se pue<strong>de</strong> usar un puntero a char o se pue<strong>de</strong> especificar el nombre <strong>de</strong> una variable array char.<br />

Cuando se pasa un array a una función, C pasa automáticam<strong>en</strong>te la dirección <strong>de</strong>l array char. La Tabla<br />

12.1 resume algunas <strong>de</strong> las funciones <strong>de</strong> cad<strong>en</strong>a más usuales.<br />

Tabla 12.1. Funciones <strong>de</strong> .cct ring. h>.<br />

Función<br />

memcpy ( 1<br />

strcat<br />

strchr ( )<br />

strcmp ( )<br />

strcmpi ( )<br />

strcpy ( 1<br />

strcspn()<br />

strl<strong>en</strong> ( )<br />

strncat ( )<br />

s t rncmp<br />

st rnset<br />

strpbrk ( )<br />

Cabecera <strong>de</strong> la función y prototipo<br />

void* memcpy(void* sl, const void* s2, size-t n);<br />

Reemplaza los primeros n bytes <strong>de</strong> * s 1 con los primeros n bytes <strong>de</strong> * s 2. Devuelve s 1.<br />

char *strcat(char *<strong>de</strong>stino, const char *fu<strong>en</strong>te);<br />

Aña<strong>de</strong> la cad<strong>en</strong>a fu<strong>en</strong>te al final <strong>de</strong> <strong>de</strong>stino. concat<strong>en</strong>a. Devuelve la cad<strong>en</strong>a <strong>de</strong>stino.<br />

char* strchr(char* SI, int ch);<br />

Devuelve un puntero a la primera ocurr<strong>en</strong>cia <strong>de</strong> ch <strong>en</strong> sl. Devuelve NULL si ch no está <strong>en</strong><br />

sl.<br />

int strcmp(const char *sí, const char *s2);<br />

Compara alfabéticam<strong>en</strong>te la cad<strong>en</strong>a s í a s2 y <strong>de</strong>vuelve:<br />

O si sl = s2<br />

O si sl > s2<br />

int strcmpi(const char *s1, const char *s2);<br />

Igual que strcmp ( ) , pero sin distinguir <strong>en</strong>tre mayúsculas y minúsculas.<br />

char *strcpy(char *<strong>de</strong>stino, const char *fu<strong>en</strong>te);<br />

Copia la cad<strong>en</strong>a,fu<strong>en</strong>te a la cad<strong>en</strong>a <strong>de</strong>stino. Devuelve la cad<strong>en</strong>a <strong>de</strong>stino.<br />

size-t strcspn(const char* SI, const char* s2);<br />

Devuelve la longitud <strong>de</strong> la subcad<strong>en</strong>a más larga <strong>de</strong> s 1 que comi<strong>en</strong>za con el carácter s 1 [ O ]<br />

y no conti<strong>en</strong>e ninguno <strong>de</strong> los caracteres <strong>de</strong> la cad<strong>en</strong>a s 2.<br />

size-t strl<strong>en</strong> (const char *s)<br />

Devuelve la longitud <strong>de</strong> la cad<strong>en</strong>a s.<br />

char* strncat(char* SI, const char"s2, size-t n);<br />

Aña<strong>de</strong> los primeros n caracteres <strong>de</strong> s 2 a s 1. Devuelve s 1. Si n >= s t r 1 <strong>en</strong> ( s 2 ) ,<br />

<strong>en</strong>tonces strncat (SI, s2, n) ti<strong>en</strong>e el mismo efecto que strcat (SI, s2) .<br />

int strncmp(const char* SI, const char* s2, size-t n);<br />

Compara s 1 con la subcad<strong>en</strong>a formada por los primeros n caracteres <strong>de</strong> s 2. Devuelve un<br />

<strong>en</strong>tero negativo, cero o un <strong>en</strong>tero positivo, según que sl lexicográficam<strong>en</strong>te sea m<strong>en</strong>or, igual<br />

omayorquelasubcad<strong>en</strong>as2.Sin 2 strl<strong>en</strong>(s2),<strong>en</strong>tonces strncmp(s1, s2, n)<br />

y strcmp (sl, s2 ) ti<strong>en</strong><strong>en</strong> el mismo efecto.<br />

char *strnset(char *s, int ch, size-t n);<br />

Copia n veces el carácter ch <strong>en</strong> la cad<strong>en</strong>a s a partir <strong>de</strong> la posición inicial <strong>de</strong> s (s [ O ] ). El<br />

máximo <strong>de</strong> caracteres que copia es la longitud <strong>de</strong> s .<br />

char* strpbrk(const char* sl, const char* s2);<br />

Devuelve la dirección <strong>de</strong> la primera ocurr<strong>en</strong>cia <strong>en</strong> s 1 <strong>de</strong> cualquiera <strong>de</strong> los caracteres <strong>de</strong> s 2,<br />

Devuelve NULL si ninguno <strong>de</strong> los caracteres <strong>de</strong> s2 aparece <strong>en</strong> sl.


Cad<strong>en</strong>as 389<br />

strrchr (<br />

strspn()<br />

strstr ( )<br />

strtok ( )<br />

char* strrchr(const char* s, int c);<br />

Devuelve un puntero a la Última ocurr<strong>en</strong>cia <strong>de</strong> c <strong>en</strong> s. Devuelve NULL si c no está <strong>en</strong> s. La<br />

búsqueda la hace <strong>en</strong> s<strong>en</strong>tido inverso, <strong>de</strong>s<strong>de</strong> el final <strong>de</strong> la cad<strong>en</strong>a al pnmer carácter, hasta que<br />

<strong>en</strong>cu<strong>en</strong>tra el carácter c.<br />

size-t strspn(const char* sl, const char* s2);<br />

Devuelve la longitud <strong>de</strong> la subcad<strong>en</strong>a izquierda (sl [ O ] ) . . . ) más larga <strong>de</strong> sl que<br />

conti<strong>en</strong>e únicam<strong>en</strong>te caracteres <strong>de</strong> la cad<strong>en</strong>a s2.<br />

char *strstr(const char *s1, const char *s2);<br />

Busca la cad<strong>en</strong>a s2 <strong>en</strong> si y <strong>de</strong>vuelve un puntero a los caracteres don<strong>de</strong> se <strong>en</strong>cu<strong>en</strong>tra s2<br />

char* strtok(char* sl, const char* s2);<br />

Analiza la cad<strong>en</strong>a s 1 <strong>en</strong> tok<strong>en</strong>s (compon<strong>en</strong>tes léxicos), éstos <strong>de</strong>limitados por caracteres <strong>de</strong><br />

la cad<strong>en</strong>a s2. La llamada inicial a s t rtok ( s 1, s2 ) <strong>de</strong>vuelve la dirección <strong>de</strong>l primer<br />

tok<strong>en</strong> y sitúa NULL al final <strong>de</strong>l tok<strong>en</strong>. Después <strong>de</strong> la llamada inicial, cada llamada sucesiva<br />

a strtok (NULL, s2 ) <strong>de</strong>vuelve un puntero al sigui<strong>en</strong>te tok<strong>en</strong> <strong>en</strong>contrado <strong>en</strong> sl. Estas<br />

llamadas cambian la cad<strong>en</strong>a sl, reemplazando cada separador con el carácter NULL.<br />

12.3.1. La palabra reservada const<br />

Las funciones <strong>de</strong> cad<strong>en</strong>a <strong>de</strong>claradas <strong>en</strong> , recogidas <strong>en</strong> la Tabla 12.1 y algunas otras,<br />

incluy<strong>en</strong> la palabra reservada const. La v<strong>en</strong>taja <strong>de</strong> esta palabra reservada es que se pue<strong>de</strong> ver<br />

rápidam<strong>en</strong>te la difer<strong>en</strong>cia <strong>en</strong>tre los parámetros <strong>de</strong> <strong>en</strong>trada y salida. Por ejemplo, el segundo parámetro<br />

fu<strong>en</strong>te <strong>de</strong> s t rcpy repres<strong>en</strong>ta el área fu<strong>en</strong>te; se utiliza sólo para copiar caracteres <strong>de</strong> ella, <strong>de</strong> modo que<br />

este área no se modificará. La palabra reservada const se utiliza para esta tarea. Se consi<strong>de</strong>ra un<br />

parámetro <strong>de</strong> <strong>en</strong>trada, ya que la función recibe <strong>datos</strong> a través <strong>de</strong> ella. En contraste, el primer parámetro<br />

<strong>de</strong>stino <strong>de</strong> s t rcpy es el área <strong>de</strong> <strong>de</strong>stino, la cual se sobreescribirá y, por consigui<strong>en</strong>te, no se <strong>de</strong>be utilizar<br />

const para ello. En este caso, el parámetro correspondi<strong>en</strong>te se d<strong>en</strong>omina parárnetro <strong>de</strong> salida, ya que<br />

los <strong>datos</strong> se escrib<strong>en</strong> <strong>en</strong> el área <strong>de</strong> <strong>de</strong>stino.<br />

12.4. ARRAYS Y CADENAS COMO PARÁMETROS DE FUNCIONES<br />

En los arrays y cad<strong>en</strong>as siempre se pasa la dirección <strong>de</strong>l objeto, un puntero al primer elem<strong>en</strong>to <strong>de</strong>l array.<br />

En la función, las refer<strong>en</strong>cias a los elem<strong>en</strong>tos individuales se hac<strong>en</strong> por indirección <strong>de</strong> la dirección <strong>de</strong>l<br />

objeto. Considérese el programa PASARRAY. C, que impíem<strong>en</strong>ta una función Longitud( 1 que calcula<br />

la longitud <strong>de</strong> una cad<strong>en</strong>a terminada <strong>en</strong> nulo. El parámetro cad se <strong>de</strong>clara como un array <strong>de</strong> caracteres<br />

<strong>de</strong> tamaño <strong>de</strong>sconocido.<br />

/* PASAl3RAY.C */<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int longitud(char cad[] ) ;<br />

void main (void)<br />

i<br />

char* cd = "Cualquier mom<strong>en</strong>to es bu<strong>en</strong>o para la felicidad";<br />

printf ("\nLongitud <strong>de</strong> la cad<strong>en</strong>a \"%s\":%d\n",cd,<br />

longitud(cd) ) ;<br />

puts ("Pulse cualquier tecla para continuar ") ;<br />

getch( 1 ;<br />

1<br />

int longitud(char cad[])<br />

t<br />

int cu<strong>en</strong>ta = O;


390 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

while (cad[cu<strong>en</strong>ta++] != '\O');<br />

return cu<strong>en</strong>ta;<br />

En la función main ( ) se reserva memoria para la constante cad<strong>en</strong>a cd, a la función longitud ( ) se<br />

transmite la dirección <strong>de</strong> la cad<strong>en</strong>a. El cuerpo <strong>de</strong>l bucle while d<strong>en</strong>tro <strong>de</strong> la función cu<strong>en</strong>ta los caracteres<br />

no nulos y termina cuando se <strong>en</strong>cu<strong>en</strong>tra el byte nulo al final <strong>de</strong> la cad<strong>en</strong>a.<br />

!<br />

Ejercicio 12.3<br />

EL programa sigui<strong>en</strong>te extrae n caracteres <strong>de</strong> una cad<strong>en</strong>a introducida por el usuario.<br />

Análisis<br />

La extracción <strong>de</strong> caracteres se realiza <strong>en</strong> una función que ti<strong>en</strong>e como primer argum<strong>en</strong>to la subcad<strong>en</strong>a a<br />

extraer, como segundo argum<strong>en</strong>to la cad<strong>en</strong>a fu<strong>en</strong>te y el tercero el número <strong>de</strong> caracteres a extraer. Se<br />

utilizan los punteros para pasar arrays a la función.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int extraer(char *<strong>de</strong>st, const char *fu<strong>en</strong>te, int num-cars);<br />

void main (void)<br />

{<br />

char s1[811;<br />

char* s2;<br />

int n;<br />

printf ("\n\tCad<strong>en</strong>a a analizar ?:") ;<br />

gets (sl);<br />

do i<br />

printf("Numero <strong>de</strong> caracteres a extraer: ");<br />

scanf ("%d",&n);<br />

}while(nil I 1 n>strl<strong>en</strong>(sl );<br />

s2 = malloc ( (n+l)*sizeof(char ) ;<br />

extraer(s2,sl,n);<br />

printf ("Cad<strong>en</strong>a extraida \"%s\"", s2) ;<br />

puts ("\nPulse intro para continuar") ;<br />

getch ( 1 ;<br />

1<br />

int extraer(char *<strong>de</strong>st, const char *fu<strong>en</strong>te, int num-cars)<br />

t<br />

int cu<strong>en</strong>ta;<br />

I<br />

*<strong>de</strong>st++ = *fu<strong>en</strong>te++;<br />

*<strong>de</strong>st = '\O';<br />

Observe que <strong>en</strong> las <strong>de</strong>claraciones <strong>de</strong> parámetros, ninguno está <strong>de</strong>finido como array, sino como<br />

punteros <strong>de</strong> tipo char. En la línea<br />

*<strong>de</strong>st++ = *fu<strong>en</strong>te++;<br />

los punteros se utilizan para acce<strong>de</strong>r a las cad<strong>en</strong>as fu<strong>en</strong>te y <strong>de</strong>stino, respectivam<strong>en</strong>te. En la llamada a la<br />

función extraer ( se pasa la dirección <strong>de</strong> las cat<br />

I


Cad<strong>en</strong>as 391<br />

DE CADENAS<br />

C soporta dos métodos para asignar cad<strong>en</strong>as. Uno <strong>de</strong> ellos ya se ha visto anteriorm<strong>en</strong>te cuando se<br />

inicializaban las variables <strong>de</strong> cad<strong>en</strong>a. La sintaxis utilizada:<br />

char VarCad<strong>en</strong>a [LongCad<strong>en</strong>a] = ConstanteCdd<strong>en</strong>a;<br />

Ejemplo 12.7<br />

Inicializa dos arrays <strong>de</strong> caracteres con cad<strong>en</strong>as constantes.<br />

char Cad<strong>en</strong>a[81] = "C maneja efici<strong>en</strong>tem<strong>en</strong>te las cad<strong>en</strong>as";<br />

char nombre[] = "Luis Martin Cebo";<br />

El segundo método para asignación <strong>de</strong> una cad<strong>en</strong>a a otra es utilizar la función strcpy ( ) . La<br />

función strcpy ( ) copia los caracteres <strong>de</strong> la cad<strong>en</strong>a fu<strong>en</strong>te a la cad<strong>en</strong>a <strong>de</strong>stino. La función supone que<br />

la cad<strong>en</strong>a <strong>de</strong>stino ti<strong>en</strong>e espacio sufici<strong>en</strong>te para cont<strong>en</strong>er toda la cad<strong>en</strong>a fu<strong>en</strong>te. El prototipo <strong>de</strong> la función:<br />

char* strcpy(char* <strong>de</strong>stino, const char* fu<strong>en</strong>te);<br />

Ejemplo 12.8<br />

Una vez <strong>de</strong>finido un array <strong>de</strong> caracteres, se le asigna una cad<strong>en</strong>a constante.<br />

char nombre [ 411 ;<br />

strcpy(nombre, "Cad<strong>en</strong>a a copiar") ;<br />

La función strcpy( ) copia "Cad<strong>en</strong>a a copiar" <strong>en</strong> la cad<strong>en</strong>a nombre y aña<strong>de</strong> un carácter nulo<br />

al final <strong>de</strong> la cad<strong>en</strong>a resultante. El sigui<strong>en</strong>te programa muestra una aplicación <strong>de</strong> s t rcpy ( ) .<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

void main (void)<br />

i<br />

char s [loo] = "Bu<strong>en</strong>os días Mr. Palacios", t [loo];<br />

strcw(t, s);<br />

strcpy (t+12,"Mr. C") ;<br />

printf ("\n%s\n%s",s, t) ;<br />

1<br />

Al ejecutarse el programa produce la salida:<br />

Bu<strong>en</strong>os días Mr. Palacios<br />

Bu<strong>en</strong>os días Mr. C<br />

La expresión t+12 obti<strong>en</strong>e la dirección <strong>de</strong> la cad<strong>en</strong>a t <strong>en</strong> Mr . Palacios. En esa dirección copia<br />

Mr . C y aña<strong>de</strong> el carácter nulo ( ' \ O ' ).<br />

12.5.1. La función strncpy ( )<br />

El prototipo <strong>de</strong> la función strncpy es<br />

char* strncpy(char* <strong>de</strong>stino, const char* fu<strong>en</strong>te, size-t num);<br />

y su propósito es copiar num caracteres <strong>de</strong> la cad<strong>en</strong>a fu<strong>en</strong>te a la cad<strong>en</strong>a <strong>de</strong>stino. La función realiza<br />

truncami<strong>en</strong>to o rell<strong>en</strong>ado <strong>de</strong> caracteres si es necesario.


392 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 12.9<br />

Estas s<strong>en</strong>t<strong>en</strong>cias copia 4 caracteres <strong>de</strong> una cad<strong>en</strong>a <strong>en</strong> otra.<br />

char cadl [ I = "Pascal";<br />

char cad2 [ I = "Hola mundo";<br />

strncpy(cad1, cad2, 4);<br />

La variable cadl conti<strong>en</strong>e ahora la cad<strong>en</strong>a "Hola" .<br />

Consdqjo<br />

Los punteros pued<strong>en</strong> manipular las partes posteriores <strong>de</strong> una cad<strong>en</strong>a, asignando la dirección<br />

<strong>de</strong>l primer carácter a manipular,<br />

char* p = cadl;<br />

G "Hola mundo" ;<br />

p += 5; /* p apunta a la cad<strong>en</strong>a "mundo" */<br />

strcpy (cada, p) ;<br />

puts (cad21 ;<br />

La s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> salida visualiza la cad<strong>en</strong>a "mundo".<br />

'!<br />

li<br />

12.6. LONGITUD Y CONCATENACIÓN DE CADENAS<br />

Muchas operaciones <strong>de</strong> cad<strong>en</strong>a requier<strong>en</strong> conocer el número <strong>de</strong> caracteres <strong>de</strong> una cad<strong>en</strong>a (longitud), así<br />

como la unión (concat<strong>en</strong>ación) <strong>de</strong> cad<strong>en</strong>as.<br />

12.6.1. La función strl<strong>en</strong> ( 1<br />

La función stri<strong>en</strong> ( ) calcula el número <strong>de</strong> caracteres <strong>de</strong>l parámetro cad<strong>en</strong>a, excluy<strong>en</strong>do el carácter<br />

nulo <strong>de</strong> terminación <strong>de</strong> la cad<strong>en</strong>a. El prototipo <strong>de</strong> la función es<br />

size-t strl<strong>en</strong>(const char* cad<strong>en</strong>a)<br />

El tipo <strong>de</strong> resultado size-t repres<strong>en</strong>ta un tipo <strong>en</strong>tero g<strong>en</strong>eral.<br />

char cad[] = ''1234567890";<br />

unsigned i;<br />

i = strl<strong>en</strong>(cad) ;<br />

Estas s<strong>en</strong>t<strong>en</strong>cias asignan 1 O a la variable i.<br />

Ejemplo<br />

Este programa muestra por pantalla la longitud <strong>de</strong> varias cad<strong>en</strong>as.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

void main (void)<br />

1<br />

char s[] = "IJKLMN";<br />

char buf er [ 81 I ;


Cad<strong>en</strong>as 393<br />

1<br />

printf("strl<strong>en</strong>(%s) = %d\n",s,strl<strong>en</strong>(s)) ;<br />

printf ("strl<strong>en</strong>(\"\") = %d\n",strl<strong>en</strong>("") ) ;<br />

printf ("Introduzca una cad<strong>en</strong>d: 'I) ;<br />

gets (bufer);<br />

printf ("strl<strong>en</strong>(%s) = %d",buter,strl<strong>en</strong>(bufer) ) ;<br />

Ejecución<br />

strl<strong>en</strong>(1JKLMN) = 6<br />

strl<strong>en</strong>("") = O<br />

Introduzca una cad<strong>en</strong>a: Sierra <strong>de</strong> Horche<br />

strl<strong>en</strong>(Sierra <strong>de</strong> Horche) = 16<br />

12.6.2. Las funciones strcat ( ) y strncat ( )<br />

En muchas ocasiones se necesita construir una cad<strong>en</strong>a, añadi<strong>en</strong>do una cad<strong>en</strong>a a otra cad<strong>en</strong>a, operación<br />

que se conoce como concat<strong>en</strong>acicín. Las funciones strcat ( ) y strncat ( ) realizan operaciones <strong>de</strong><br />

concat<strong>en</strong>ación. strcat ( ) aña<strong>de</strong> el cont<strong>en</strong>ido <strong>de</strong> la cad<strong>en</strong>a fu<strong>en</strong>te a la cad<strong>en</strong>a <strong>de</strong>stino, <strong>de</strong>volvi<strong>en</strong>do un<br />

puntero a la cad<strong>en</strong>a <strong>de</strong>stino. Su prototipo es:<br />

char* strcat (char" <strong>de</strong>stino, const chdr* fu<strong>en</strong>te) ;<br />

Ejemplo 12.10<br />

Copia una constante cad<strong>en</strong>a y a continuacicín concat<strong>en</strong>a con otru cad<strong>en</strong>a.<br />

char cad<strong>en</strong>a [ 81 I ;<br />

strcpy (cad<strong>en</strong>a, "Borland");<br />

strcat (cad<strong>en</strong>a, "C");<br />

La variable cad<strong>en</strong>a conti<strong>en</strong>e ahora "Rorland C".<br />

Es posible limitar el número <strong>de</strong> caracteres a concat<strong>en</strong>ar utilizando la función strncat ( ) . La<br />

función strncat ( ) aña<strong>de</strong> num caracteres <strong>de</strong> la cad<strong>en</strong>a fu<strong>en</strong>te a la cad<strong>en</strong>a <strong>de</strong>stino y <strong>de</strong>vuelve el puntero<br />

a la cad<strong>en</strong>a <strong>de</strong>stino. Su prototipo es<br />

char* strncat(char* <strong>de</strong>stino, const char* fu<strong>en</strong>te, size-t num)<br />

y cuando se invoca con una llamada tal como<br />

strncat(t, s, n);<br />

n repres<strong>en</strong>ta los primeros n caracteres <strong>de</strong> s que se van a unir a t, a m<strong>en</strong>os que se <strong>en</strong>cu<strong>en</strong>tre un carácter<br />

nulo, <strong>en</strong> cuyo mom<strong>en</strong>to se termina el proceso.<br />

Ejemplo 12.1 1<br />

Concat<strong>en</strong>ar 4 caracteres.<br />

char cadl[81] = "Hola soy yo 'I;<br />

char cad2[41] = "Luis Merino";<br />

strncat(cad1, cad2, 4);


394 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

La variable Cddl conti<strong>en</strong>e ahora "Hola soy yo 1,uis'l.<br />

Ni la función s t L cat ( ) , ni s t rncd t ( coinprueba que la cad<strong>en</strong>a <strong>de</strong>stino t<strong>en</strong>ga sufici<strong>en</strong>te espacio<br />

para la cad<strong>en</strong>a resultante. Por ejemplo:<br />

char sll] = "ABCDREGH"; /* reservci espdclo pard 8+1 cdracteres */<br />

char s2 [ ] = "XYZ"; /* reserva espdcio para 3+1 cdrdcteres */<br />

strcdt (sl,s2); /* produce resultddo extraños por no haber espdcio<br />

para la concat<strong>en</strong>dción sl con s2 */<br />

Ejercicio 12.4<br />

El programa aña<strong>de</strong> la cad<strong>en</strong>a s2 al final <strong>de</strong> la cad<strong>en</strong>a si. Reserva memoria dinámicam<strong>en</strong>te, <strong>en</strong> tiempo<br />

<strong>de</strong> ejecución.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

void main (void)<br />

I<br />

char* sl = "ABCDEFGH";<br />

char s2 [ ] = "XYZ";<br />

\<br />

i<br />

printf ("\nAntes <strong>de</strong> strcat (sl, sa) : \n");<br />

printf("\tsl = L"ó-1, longitud = %d\n",sl,strlcn(sl));<br />

printf("\ts2 = [%SI, longit-ud = %d\n",s2,strl<strong>en</strong>(s2));<br />

/* amplia memorid pdrd la cad<strong>en</strong>d resultante <strong>de</strong> la concat<strong>en</strong>ación */<br />

sl = realloc(s1, (strl<strong>en</strong>(sl)tstrl<strong>en</strong>(s2)+1)*size~~(char));<br />

printf ("\tsl = [%SI, longitud = 'Od (amp1 id memoria) \n",<br />

sl,st.rl<strong>en</strong>(si));<br />

strccit (sl, s2 ) ;<br />

puts ("Despues <strong>de</strong> strcat (s3, s2) ") ;<br />

printf ("\tsl = [%SI, lonqii-ud = Xd\n',sl,strl<strong>en</strong><br />

printf ("\ts2 = [%SI, longit-iid 7 'kd\n", s%,stri<strong>en</strong><br />

Ejecución<br />

Antes <strong>de</strong> strcat(sl,s2):<br />

sl = [ABCDEFGH], longitud = 8<br />

s2 = [XYZ], longitud = 3<br />

sl = [ABCDEFGH], longitud = 8 (amplia memoria)<br />

Despues <strong>de</strong> strcat(sl,s2)<br />

sl = [ABCDFEGHXYZ], longitud = 11<br />

s2 = [XYZ], longitud = 3<br />

12.7. COMPARACI~N DE CADENAS<br />

Dado que las cad<strong>en</strong>as son arrays <strong>de</strong> caracteres, la biblioteca STK I DIG. H proporciona un conjunto <strong>de</strong><br />

funciones que comparan cad<strong>en</strong>as. Estas funciones comparan los caracteres <strong>de</strong> dos cad<strong>en</strong>as utilizando<br />

el valor ASCII <strong>de</strong> cada carácter. Las funciones son strcmp(1 , stricmp( ), strncmp() y<br />

strnicmp ( )-.


Cad<strong>en</strong>as 395<br />

12.7.1. La función strcmp ( )<br />

Si se <strong>de</strong>sea <strong>de</strong>terminar si una cad<strong>en</strong>a es igual a otra, mayor o m<strong>en</strong>or que otra, se <strong>de</strong>be utilizar la función<br />

strcmp ( ) . La comparación siempre es alfabética. strcmp ( ) compara su primer parámetro con su<br />

segundo, y <strong>de</strong>vuelve O si las dos cad<strong>en</strong>as son idénticas; un valor m<strong>en</strong>or que cero si la cad<strong>en</strong>a 1 es m<strong>en</strong>or<br />

que la cad<strong>en</strong>a 2; o un valor mayor que cero si la cad<strong>en</strong>a 1 es mayor que la cad<strong>en</strong>a 2 (los términos «mayor<br />

que» y «m<strong>en</strong>or que» se refier<strong>en</strong> a la ord<strong>en</strong>ación alfabética <strong>de</strong> las cad<strong>en</strong>as). Por ejemplo, Alicante es<br />

m<strong>en</strong>or que Sevilla. Así, la letra A es m<strong>en</strong>or que la letra a, la letra Z es m<strong>en</strong>or que la letra a. El prototipo<br />

<strong>de</strong> la función strcmp ( ) es<br />

int strcmp(const char* cddi, const chdr* cad2) ;<br />

La función compara las cad<strong>en</strong>as cadl y cad2. El resultado <strong>en</strong>tero es:<br />

< O si cadl<br />

= O si cadl<br />

> O si cadl<br />

es m<strong>en</strong>or que<br />

es igual u<br />

es mayor que<br />

cad2<br />

cad2<br />

cad2<br />

Ejemplo 12.12<br />

Resultados <strong>de</strong> realizar comparaciones <strong>de</strong> cad<strong>en</strong>as.<br />

char cadl [ ] = "Microsoft C";<br />

char cad2 [I = "Microsoft Visiial C"<br />

int i;<br />

i = strcmp(cad1, cad2); /* i, toma un valor negativo */<br />

strcmp ("Waterloo","Windows") < O<br />

strcmp ("Mortimer', "Mortim") > O<br />

{Devuelve un valor negativo}<br />

{Devuelve un valor positivo}<br />

strcmp ("Jertru", "Jertru") = o {Devuelve cero)<br />

La comparación se realiza examinando los primeros caracteres <strong>de</strong> cadl y cad2; a continuación los<br />

sigui<strong>en</strong>tes caracteres y así sucesivam<strong>en</strong>te. Este proceso termina cuando:<br />

se <strong>en</strong>cu<strong>en</strong>tran dos caracteres distintos <strong>de</strong>l mismo ord<strong>en</strong>: cddl 1 i I y cad2 [ i J ;<br />

0 se <strong>en</strong>cu<strong>en</strong>tra el carácter nulo <strong>en</strong> cad I i I o cad2 [ i 1<br />

Water loo es m<strong>en</strong>or que Windows<br />

Mort imer es mayor que Mortim-carácter nulo<br />

Jertru es igual que Jertru<br />

12.7.2. La función stricmp ( )<br />

La función stricmp ( ) compara las cad<strong>en</strong>as cadl y cdd2 sin hacer distinción <strong>en</strong>tre mayúsculas y<br />

minúsculas. El prototipo es<br />

int stricmp (const char* c-adl, const char* cad2) ;<br />

Ejemplo 12.13<br />

Comparucicín <strong>de</strong> dos cad<strong>en</strong>as. con in<strong>de</strong>peridtmciu <strong>de</strong> que sean letras mayúsculas o minúsculas.<br />

char cadl [I = "Turbo C";<br />

char cad2 [I = "TURBO C";<br />

int i;<br />

i = stricmp(cad1, cad2);


I -<br />

-<br />

396 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Asigna O a la variable i ya que al no distinguir <strong>en</strong>tre mayúsculas y minúsculas las dos cad<strong>en</strong>as son<br />

iguales.<br />

12.7.3. La función strncrnp ( )<br />

La función strncmp ( 1 compara los num caracteres mas a la izquierda <strong>de</strong> las dos cad<strong>en</strong>as cad1 y cad2.<br />

El prototipo es<br />

int strncmp(const char* cadl, const char* cad2, size-t num) ;<br />

y el resultado <strong>de</strong> la comparación será (consi<strong>de</strong>rando los num primeros caracteres):<br />

< O<br />

= O<br />

si<br />

si<br />

> O si<br />

cad1 es m<strong>en</strong>or que cad2<br />

cadl es igual que cad2<br />

cadl es mayor que cad2<br />

1<br />

!<br />

Ejemplo 12.14<br />

Comparar los 7 primeros caracteres <strong>de</strong> dos cad<strong>en</strong>as.<br />

char cad<strong>en</strong>a1 [ ]<br />

char cad<strong>en</strong>a2 [ ]<br />

int i;<br />

= "Turbo C";<br />

= "Turbo Prolog"<br />

i = strncmp(cad<strong>en</strong>a1, cad<strong>en</strong>a2, 7);<br />

Esta s<strong>en</strong>t<strong>en</strong>cia asigna un número negativo a la variable i, ya que "Turbo C" es m<strong>en</strong>or que "Turbo<br />

En el caso <strong>de</strong> comparar los 5 primeros caracteres:<br />

i = strncmp(cad<strong>en</strong>a1, cad<strong>en</strong>a2, 5);<br />

esta s<strong>en</strong>t<strong>en</strong>cia asigna un cero a la variable i, ya que "Turbo" es igual que "Turbo".<br />

12.7.4. La función strnicrnp ( )<br />

La función strnicmp ( 1 compara los caracteres nurn a la izquierda <strong>en</strong> las dos cad<strong>en</strong>as, cadl y cad2,<br />

sin distinguir <strong>en</strong>tre mayúsculas y minúsculas. El prototipo es<br />

int strnicmp(const char* cadl, const char* cad2, size-t num) ;<br />

El resultado será (consi<strong>de</strong>rando num primeros caracteres):<br />

o si cadl es mayor que cad2<br />

Ejemplo 12.15<br />

Comparación <strong>de</strong> los 5 primeros caracteres. sin distincicín <strong>en</strong>tre mayúsculas y minúsculas<br />

char cad<strong>en</strong>a1 [ I = "Turbo C";<br />

char cad<strong>en</strong>a2 [ I = "TURBO C";<br />

int i;<br />

i = strnicmp(cad<strong>en</strong>a1, cad<strong>en</strong>a2, 5);<br />

Esta s<strong>en</strong>t<strong>en</strong>cia asigna O a la variable i, ya que las cad<strong>en</strong>as "Turbo" y "TURBO" difier<strong>en</strong> sólo <strong>en</strong> que<br />

son mayúsculas o minúsculas.


Cad<strong>en</strong>as 397<br />

12.8. INVERSIÓN DE CADENAS<br />

La biblioteca STRING. H incluye la función strrev ( ) que sirve para invertir los caracteres <strong>de</strong> una<br />

cad<strong>en</strong>a. Su prototipo es:<br />

char *strrev(char *s);<br />

strrev( ) invierte el ord<strong>en</strong> <strong>de</strong> los caracteres <strong>de</strong> la cad<strong>en</strong>a especificada <strong>en</strong> el argum<strong>en</strong>to s; <strong>de</strong>vuelve un<br />

puntero a la cad<strong>en</strong>a resultante.<br />

Ejemplo 12.16<br />

Muestra <strong>de</strong> inversión <strong>de</strong> cad<strong>en</strong>as.<br />

char cad<strong>en</strong>a[] = "Hola";<br />

strrev (cad<strong>en</strong>a);<br />

puts (cad<strong>en</strong>a); /* visualizd lld10H'l */<br />

El programa sigui<strong>en</strong>te invierte el ord<strong>en</strong> <strong>de</strong> la cad<strong>en</strong>a ola mundo<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main (void)<br />

{<br />

I<br />

char *cad<strong>en</strong>a = "Hola mundo";<br />

strrev (cad<strong>en</strong>a) ;<br />

printf("\nCad<strong>en</strong>a inversa: %s\n", cad<strong>en</strong>a);<br />

return O;<br />

Estas dos s<strong>en</strong>t<strong>en</strong>cias<br />

strrev (cad<strong>en</strong>a) ;<br />

printf("\nCad<strong>en</strong>a inversa: Xs\n", cad<strong>en</strong>a);<br />

se podrían haber sustituido por<br />

printf ("\nCad<strong>en</strong>a inversa: %s\n", strrev(cad<strong>en</strong>a) ) ;<br />

12.9. CONVERSIÓN DE CADENAS<br />

La biblioteca STRING. H <strong>de</strong> la mayoría <strong>de</strong> los compiladores C suele incluir funciones para convertir los<br />

caracteres <strong>de</strong> una cad<strong>en</strong>a a letras mayúsculas y minúsculas respectivam<strong>en</strong>te. Estas funciones se llaman<br />

striwr ( ) y strupr ( ) <strong>en</strong> compiladores <strong>de</strong> AT&T y Borland, mi<strong>en</strong>tras que <strong>en</strong> Microsoft se d<strong>en</strong>ominan<br />

-strlwr ( ) y -strupr ( 1.<br />

12.9.1. Función strupr ( )<br />

La función strupr ( convierte las letras minúsculas <strong>de</strong> una cad<strong>en</strong>a a mayúsculas. Su prototipo es:<br />

char *strupr(char *SI;


Y<br />

398 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 12.17<br />

Este programa convierte los caracteres <strong>en</strong> minúsculas <strong>de</strong> una cad<strong>en</strong>a a mayúsculas; se escribe la cad<strong>en</strong>a<br />

por pantalla.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong>


Iu<br />

-<br />

Cad<strong>en</strong>as 399<br />

1<br />

char ctr[81], ictr [Ul];<br />

puts ("\n\tintroducir und frase h,istci ~ I J C sea capicud.") ;<br />

do i<br />

gets (ctr);<br />

strupr (ctr); /* Todos I os cdrC1cteres <strong>en</strong> mayúsculas */<br />

strcpy (ictr, ctr) ;<br />

strrev(ctr); /* Invierte Id ccid<strong>en</strong>a */<br />

1 while (strcmp(ctr,ictr));/*termina el bucle cuando son iguales */<br />

printf ("\nCad<strong>en</strong>a%s es palíndromo", ictr) ;<br />

return O;<br />

12.10. CONVERSIÓN DE CADENAS A NÚMEROS<br />

Es muy frecu<strong>en</strong>te t<strong>en</strong>er que convertir números almac<strong>en</strong>ados <strong>en</strong> cad<strong>en</strong>as <strong>de</strong> caracteres a tipos <strong>de</strong> <strong>datos</strong><br />

numéricos. C proporciona las funciones atoi ( ) , atof ( ) y ato1 ( ), que realizan estas conversiones.<br />

Estas tres funciones se incluy<strong>en</strong> <strong>en</strong> la biblioteca STriI, I R. H, por lo que ha <strong>de</strong> incluir <strong>en</strong> su programa la<br />

directiva<br />

#inclu<strong>de</strong> <br />

12.10.1. Función atoi ( )<br />

La función atoi ( ) convierte una cad<strong>en</strong>a a un valor <strong>en</strong>tero. Su prototipo es:<br />

int atoi(const char "cad);<br />

atoi ( ) convierte la cad<strong>en</strong>a apuntada por cad a un valor <strong>en</strong>tero. La cad<strong>en</strong>a <strong>de</strong>be t<strong>en</strong>er la repreu<strong>en</strong>tación<br />

<strong>de</strong> un valor <strong>en</strong>tero y el formato sigui<strong>en</strong>te:<br />

[espacio <strong>en</strong> blanco1 [siqnol idddl<br />

[espacio <strong>en</strong> blarico]<br />

[signo1<br />

idddl ~<br />

Una cad<strong>en</strong>a que se pue<strong>de</strong> convertir a un <strong>en</strong>tero e\:<br />

" 12 3 2 I'<br />

= rad~ria oyc iondl <strong>de</strong> t~~buldciones y espdcios<br />

= un signo opcional para el valor<br />

c;idcrid <strong>de</strong> dígilos<br />

Sin embargo, la cad<strong>en</strong>a sigui<strong>en</strong>te no se pue<strong>de</strong> convertir a un valor <strong>en</strong>tero:<br />

"-1234596.495"<br />

La cad<strong>en</strong>a anterior se pue<strong>de</strong> convertir a un número <strong>de</strong> coma flotante con la función atof ( ) .<br />

Si la cad<strong>en</strong>a no se pue<strong>de</strong> convertir, atoi ( ) <strong>de</strong>vuelve cero.<br />

Ejemplo 12.19<br />

Convierte los dígitos <strong>de</strong> una cad<strong>en</strong>a <strong>en</strong> un valor <strong>en</strong>tero.<br />

char *cad<strong>en</strong>a = "453423";<br />

int valor;<br />

vdlor = atoi(cad<strong>en</strong>d);


400 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

12.10.2. Función atof ( )<br />

La función atof ( ) convierte una cad<strong>en</strong>a a un valor <strong>de</strong> coma flotante. Su prototipo es:<br />

double atof(const char *cad);<br />

atof ( ) convierte la cad<strong>en</strong>a apuntada por cad a un valor double <strong>en</strong> coma flotante. La cad<strong>en</strong>a <strong>de</strong><br />

caracteres <strong>de</strong>be t<strong>en</strong>er una repres<strong>en</strong>tación <strong>de</strong> caracteres <strong>de</strong> un número <strong>de</strong> coma flotante. La conversión<br />

termina cuando se <strong>en</strong>cu<strong>en</strong>tre un carácter no reconocido. Su formato es:<br />

[espacio <strong>en</strong> blanco] [signo] [ddd] [.I [dddl [e/El [signo] [dddl<br />

Ejemplo 12.20<br />

Convierte los dígitos <strong>de</strong> una cad<strong>en</strong>a a un número <strong>de</strong> tipo double.<br />

char *cad<strong>en</strong>a = "545.7345";<br />

double valor;<br />

valor = atof(cad<strong>en</strong>a);<br />

12.10.3. Función ato1 ( )<br />

La función ato1 ( ) convierte una cad<strong>en</strong>a a un valor largo (long). Su prototipo es:<br />

long atol(const char *cad);<br />

La cad<strong>en</strong>a a convertir <strong>de</strong>be t<strong>en</strong>er un formato <strong>de</strong> valor <strong>en</strong>tero largo:<br />

[espacio <strong>en</strong> blanco] [signo] [dddl<br />

Ejemplo 12.21<br />

Una cad<strong>en</strong>a que ti<strong>en</strong>e dígitos consecutivos se convierte <strong>en</strong> <strong>en</strong>tero largo.<br />

char *cad<strong>en</strong>a = "45743212'';<br />

long valor;<br />

valor = atol(cad<strong>en</strong>a);<br />

12.10.4. Entrada <strong>de</strong> números y cad<strong>en</strong>as<br />

Un programa pue<strong>de</strong> necesitar una <strong>en</strong>trada que consista <strong>en</strong> un valor numérico y a continuación una<br />

cad<strong>en</strong>a <strong>de</strong> caracteres. La <strong>en</strong>trada <strong>de</strong>l valor numérico se pue<strong>de</strong> hacer con scanf ( y la cad<strong>en</strong>a con<br />

gets ().<br />

Ejemplo 12.22<br />

Lectura <strong>de</strong> un <strong>en</strong>tero largo y a continuacicín una cad<strong>en</strong>a.<br />

long i nt k;<br />

char cad[811;<br />

printf ("Metros cuadrados: ") ; scanf ("%ld",&k) ;


Cad<strong>en</strong>as 401<br />

printf ("Nombre <strong>de</strong> la finca: 'I) ; gets (cad) ;<br />

Al ejecutarse este fragm<strong>en</strong>to <strong>de</strong> código, <strong>en</strong> pantalla sale<br />

Metros cuadrados: 1980756<br />

Nombre <strong>de</strong> la finca:<br />

No se pue<strong>de</strong> introducir el nombre <strong>de</strong> la finca, el programa le asigna la cad<strong>en</strong>a vacía. ¿Por qué?: al<br />

teclear 19 8 O 7 5 6 y retorno <strong>de</strong> carro se asigna la cantidad a k y queda <strong>en</strong> el buffer interno el carácter fin<br />

<strong>de</strong> línea, que es el carácter <strong>en</strong> que termina la captación <strong>de</strong> una cad<strong>en</strong>a por gets í 1, por lo que no se le<br />

asigna ningún carácter a cad. Para solucionar este problema t<strong>en</strong>emos dos alternativas, la primera:<br />

printf ("Metros cuadrados: " ) ; scanf ("%ld%*c',hk) ;<br />

printf ("Nombre <strong>de</strong> la finca: " ) ; gets (cad) ;<br />

Después <strong>de</strong> captar el número,%*c , hace que se lea el sigui<strong>en</strong>te carácter y no se asigne, así se queda<br />

el buffer <strong>de</strong> <strong>en</strong>trada vacío y gets (cad) pue<strong>de</strong> captar la cad<strong>en</strong>a que se teclee. La segunda alternativa es<br />

leer el valor numérico como una cad<strong>en</strong>a <strong>de</strong> dígitos y <strong>de</strong>spués transformarlo con atol (cad) a <strong>en</strong>tero<br />

largo.<br />

printf ("Metros cuadrados: 'I) ; gets (cad);<br />

k = atol (cad);<br />

printf ("Nombre <strong>de</strong> la finca: ") ; gets (cad);<br />

12.11. BÚSQUEDA DE CARACTERES Y CADENAS<br />

La biblioteca STRING. H conti<strong>en</strong>e un número <strong>de</strong> funciones que permit<strong>en</strong> localizar caracteres <strong>en</strong> cad<strong>en</strong>as<br />

y subcad<strong>en</strong>as <strong>en</strong> cad<strong>en</strong>as.<br />

Funciones <strong>de</strong> búsqueda strchr strrchr<br />

<strong>de</strong> caracteres s trc spn s t rpbrk<br />

strspn<br />

Funciones <strong>de</strong> búsqueda strstr strtok<br />

<strong>de</strong> cad<strong>en</strong>as<br />

12.11.1. La función strchr()<br />

El prototipo <strong>de</strong> la función strchr ( ) es<br />

char *strchr(const char *s, int c);<br />

strchr ( 1 permite buscar caracteres y patrones <strong>de</strong> caracteres <strong>en</strong> cad<strong>en</strong>as; localiza la primera<br />

ocurr<strong>en</strong>cia <strong>de</strong> un carácter c <strong>en</strong> una cad<strong>en</strong>a s. La búsqueda termina <strong>en</strong> la primera ocurr<strong>en</strong>cia <strong>de</strong> un carácter<br />

coincid<strong>en</strong>te.<br />

Ejemplo 12.23<br />

Búsqueda <strong>de</strong>l carácter ' v ' <strong>en</strong> una cad<strong>en</strong>a.<br />

char cad[81] = "C l<strong>en</strong>guaje <strong>de</strong> medio nivel";<br />

char *cadPtr;<br />

cadPtr = strchr(cad, 'v');


P<br />

1<br />

402 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

12.11.2. La función strrchr ( )<br />

La función strrchr ( ) localiza la Última ocurr<strong>en</strong>cia <strong>de</strong>l patrón c <strong>en</strong> la cad<strong>en</strong>a s. La búsqueda se realiza<br />

<strong>en</strong> s<strong>en</strong>tido inverso, <strong>de</strong>s<strong>de</strong> el último carácter <strong>de</strong> la cad<strong>en</strong>a al primero; termina con la primera ocurr<strong>en</strong>cia<br />

<strong>de</strong> un carácter coincid<strong>en</strong>te. Si no se <strong>en</strong>cu<strong>en</strong>tra el carácter c <strong>en</strong> la cad<strong>en</strong>a s, la función produce un<br />

resultado NULL. Su prototipo es<br />

char *strrchr(const char *s, int c);<br />

I<br />

carúcter buscado<br />

cudtma <strong>de</strong> búsquedu<br />

Ejemplo 12.24<br />

Búsqueda <strong>en</strong> ord<strong>en</strong> inverso <strong>de</strong>l curúcter 'x' <strong>en</strong> un ma cad<strong>en</strong>a y escribe la cad<strong>en</strong>a que evtá a<br />

continuación.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int mdin (void)<br />

i<br />

char *cad<strong>en</strong>a = 'I-x-";<br />

char * resiu 1 Lado ;<br />

}<br />

resultado = strrchrícad<strong>en</strong>d, 'x');<br />

printf ("Cad<strong>en</strong>a <strong>de</strong>vuelta: %s\n", resultado) ;<br />

return O;<br />

12.11.3. La función strspn()<br />

La función strspn( <strong>de</strong>vuelve el número <strong>de</strong> caracteres <strong>de</strong> la parte iLquierda <strong>de</strong> una cad<strong>en</strong>a SI que<br />

coinci<strong>de</strong> con cualquier carácter <strong>de</strong> la cad<strong>en</strong>a patrcín s2. El prototipo <strong>de</strong> strspn ( ) es<br />

size-t strspn(const char *c1, corist ch,ir "~2);<br />

Ejemplo 12.25<br />

El sigui<strong>en</strong>te ej<strong>en</strong>iplo busca el segm<strong>en</strong>to <strong>de</strong> (ud<strong>en</strong>al yue ti<strong>en</strong>e un subconjunto <strong>de</strong> cdd<strong>en</strong>a2 .<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main (void)<br />

i<br />

char *cad<strong>en</strong>a1 = "3c~1293456";<br />

char *cad<strong>en</strong>a2 "abcl23";<br />

int longitud;<br />

longitud = ctrspn (cad<strong>en</strong>al, ccid<strong>en</strong>a2 ) ;<br />

printf ('Longitud = %d", 1 oriq i i iid) ;<br />

return O;


Cad<strong>en</strong>as 403<br />

Ejecución<br />

Longitud = 4<br />

Este resultado se obti<strong>en</strong>e porque el primer carácter <strong>de</strong> cad<strong>en</strong>a1 es 3 y pert<strong>en</strong>ece a cad<strong>en</strong>a2, los<br />

tres caracteres sigui<strong>en</strong>tes a12 pert<strong>en</strong>ec<strong>en</strong> a cad<strong>en</strong>a2.<br />

12.11.4. La función strcspn( )<br />

La función strcspn( ) <strong>en</strong>cu<strong>en</strong>tra el índice <strong>de</strong>l primer carácter <strong>de</strong> la primera cad<strong>en</strong>a sl que está <strong>en</strong> el<br />

conjunto <strong>de</strong> caracteres especificado <strong>en</strong> la segunda cad<strong>en</strong>a s?. El prototipo <strong>de</strong> strcspn es:<br />

size-t strcspn (const char *sl, const char *s?) ;<br />

Ejemplo 12.26<br />

Búsqueda <strong>de</strong> la primera posición <strong>de</strong>l carácter I d I<br />

o I w <strong>en</strong> uiia ca<strong>de</strong>iia.<br />

char cad<strong>en</strong>a [ ] = "Los mano1 os <strong>de</strong> Cdrchelejo";<br />

int i;<br />

1 = strcspn(cad<strong>en</strong>a, "dw");<br />

El ejemplo anterior asigna 12 (posición <strong>de</strong>l carácter d <strong>en</strong> cad<strong>en</strong>d) a la variable i.<br />

12.1 1.5. La función strpbrk ( )<br />

La función strpbrk ( ) recorre una cad<strong>en</strong>a buscando caracteres pert<strong>en</strong>eci<strong>en</strong>tes a un conjunto <strong>de</strong><br />

caracteres especificado. El prototipo es<br />

char *strpbrk(const char *sl, const char *s2);<br />

Esta función <strong>de</strong>vuelve un puntero a la primera ocurr<strong>en</strong>cia <strong>de</strong> cualquier carácter <strong>de</strong> s2 <strong>en</strong> SI. Si las<br />

dos cad<strong>en</strong>as no ti<strong>en</strong><strong>en</strong> caracteres comunes se <strong>de</strong>vuelve NULL.<br />

Ejemplo 12.27<br />

Encu<strong>en</strong>tra la dirección <strong>en</strong> cad <strong>de</strong>l primer carácter eiicontrado que pert<strong>en</strong>ezca a subcad.<br />

char *cad = "Hello Dolly, hey Julio";<br />

char "subcad = "hy";<br />

char *ptr;<br />

ptr = strpbrk(cad, subcad);<br />

printf ("\n%s\n",ptr) ;<br />

El segm<strong>en</strong>to <strong>de</strong> programa visualiza ''y, hey Julio" , ya que <strong>en</strong>cu<strong>en</strong>tra "y'' <strong>en</strong> la cad<strong>en</strong>a antes<br />

que la "h" .


404 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

12.11.6. La función strstr( )<br />

La biblioteca STRING. H conti<strong>en</strong>e las funciones strstr ( ) y strtok ( ) , que permit<strong>en</strong> localizar una<br />

subcad<strong>en</strong>a <strong>en</strong> una cad<strong>en</strong>a o bi<strong>en</strong> romper una cad<strong>en</strong>a <strong>en</strong> subcad<strong>en</strong>as. La función strstr ( ) busca una<br />

cad<strong>en</strong>a d<strong>en</strong>tro <strong>de</strong> otra cad<strong>en</strong>a. El prototipo <strong>de</strong> la función es<br />

char *strstr(const char *sl, const char " ~ 2 ) ;<br />

La función <strong>de</strong>vuelve un puntero al primer carácter <strong>de</strong> la cad<strong>en</strong>a s 1 que coinci<strong>de</strong> con la cad<strong>en</strong>a s 2. Si la<br />

subcad<strong>en</strong>a s2 no está <strong>en</strong> la cad<strong>en</strong>a SI, la función <strong>de</strong>vuelve NUI~L.<br />

Ejemplo 12.28<br />

Búsqueda <strong>de</strong> la cad<strong>en</strong>a I' 4 i 6 " <strong>en</strong> c ad1 .<br />

char *cad1 = "123456789";<br />

chdr *cad2 = "456";<br />

char *resultado;<br />

resultado = strstr(cad1, cdd2);<br />

pr intf ("\n&s\n", resultado) ;<br />

El segm<strong>en</strong>to <strong>de</strong> programa anterior visualiza 4 5 6'1 8 9<br />

12.1 1.7. La función strtok ( )<br />

!<br />

La función strtok( ) permite romper una cad<strong>en</strong>a <strong>en</strong> subcad<strong>en</strong>as, basada <strong>en</strong> un conjunto especificado<br />

<strong>de</strong> caracteres <strong>de</strong> separación. Su prototipo es<br />

char *strtok(char *sZ, const char *s3);<br />

strtok ( ) lee la cad<strong>en</strong>a SI como una serie <strong>de</strong> cero o inás símbolos y la cad<strong>en</strong>a s2 como el conjunto <strong>de</strong><br />

caracteres que se utilizan como separadores <strong>de</strong> los símbolos <strong>de</strong> la cad<strong>en</strong>a s 1. Los símbolos <strong>en</strong> la cad<strong>en</strong>a<br />

SI pued<strong>en</strong> <strong>en</strong>contrarse separados por un carácter o más <strong>de</strong>l conjunto <strong>de</strong> caracteres separadores <strong>de</strong> la<br />

cad<strong>en</strong>a s2. La segunda y posteriores llamadas a strtok() ha <strong>de</strong> hacerse con el primer argum<strong>en</strong>to a<br />

NULL cuando <strong>de</strong>vuelva NUL~T, .<br />

Ejercicio 11.6<br />

Este programa rompe una cad<strong>en</strong>a <strong>en</strong> subcad<strong>en</strong>as y se imprime cada una <strong>de</strong> ellas.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

int main0<br />

i<br />

char *cad = "Pepe Luis + Canovds * Mdr~cosII;<br />

char "separador = 'I+*";<br />

char *ptr = cad;<br />

printf ("\n%s\n", cad) ;<br />

ptr = strtok(cad, separador);<br />

/* Anterior llamada, <strong>de</strong>vuelve dirección d primer<br />

carácter y sitúa un NULL <strong>en</strong> el pr-imer carácter<br />

coincid<strong>en</strong>te con algún cdráct.er <strong>de</strong> c2


Cad<strong>en</strong>as 405<br />

*/<br />

printf ("\tSe rompe <strong>en</strong> tres subcad<strong>en</strong>as") ;<br />

while (ptr)<br />

i<br />

print f ( 'I \ n % s I' , p t r ) ;<br />

ptr = strtok(NULL, separador);<br />

/*Devuelve dirección primer carácter<br />

(a partir <strong>de</strong> subcad<strong>en</strong>a anterior) y situa NULL <strong>en</strong><br />

primer carácter coincid<strong>en</strong>te con alguno <strong>de</strong> s2 */<br />

i<br />

return O;<br />

Al ejecutar este programa se visualiza:<br />

Pepe Luis + Canovas * Marcos<br />

Se rompe <strong>en</strong> tres subcad<strong>en</strong>as<br />

Pepe Luis<br />

Canovas<br />

Marcos<br />

12.12. RESUMEN<br />

En este capítulo se han examinado las funciones <strong>de</strong><br />

manipulación <strong>de</strong> cad<strong>en</strong>as incluidas <strong>en</strong> el archivo <strong>de</strong><br />

cabecera STRING. H. Los temas tratados han sido:<br />

0 Las cad<strong>en</strong>as <strong>en</strong> C son mays <strong>de</strong> caracteres que<br />

terminan con el carácter nulo (el carácter O <strong>de</strong><br />

ASCII).<br />

La <strong>en</strong>trada <strong>de</strong> cad<strong>en</strong>as requiere el uso <strong>de</strong> la función<br />

gets ().<br />

* La biblioteca STRING. H conti<strong>en</strong>e numerosas<br />

funciones <strong>de</strong> manipulación <strong>de</strong> cad<strong>en</strong>as; <strong>en</strong>tre<br />

ellas, se <strong>de</strong>stacan las funciones que soportan<br />

asignación, concat<strong>en</strong>ación, conversión, inversión<br />

y búsqueda.<br />

C soporta dos métodos <strong>de</strong> asignación <strong>de</strong> cad<strong>en</strong>as.<br />

El primer método, asigna una cad<strong>en</strong>a a otra,<br />

cuando se <strong>de</strong>clara esta Última. El segundo método,<br />

utiliza la función strcpy ( 1, que pue<strong>de</strong><br />

asignar una cad<strong>en</strong>a a otra <strong>en</strong> cualquier etapa <strong>de</strong>l<br />

programa.<br />

La función strl<strong>en</strong>( ) <strong>de</strong>vuelve la longitud <strong>de</strong><br />

una cad<strong>en</strong>a.<br />

Las funciones strcat ( ) y strncat ( ) permit<strong>en</strong><br />

concat<strong>en</strong>ar dos cad<strong>en</strong>as. La función<br />

strncat ( ) permite especificar el número <strong>de</strong><br />

caracteres a concat<strong>en</strong>ar.<br />

strcmp ( y strfcmp ( ) realizan una comparación<br />

<strong>de</strong> dos cad<strong>en</strong>as, sin t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta<br />

mayúsculas y minúsculas. La función<br />

strncmp0 es una variante <strong>de</strong> la función<br />

strcmp ( 1, que utiliza un número especificado<br />

<strong>de</strong> caracteres al comparar las cad<strong>en</strong>as. La función<br />

strnicmp ( ) es una versión <strong>de</strong> la función<br />

strncmp ( 1 que realiza una versión con in<strong>de</strong>p<strong>en</strong>d<strong>en</strong>cia<br />

<strong>de</strong>l tamaño <strong>de</strong> las letras.<br />

Las funciones strlwr ( ) y strupr ( ) convierte<br />

los caracteres <strong>de</strong> una cad<strong>en</strong>a <strong>en</strong> letras<br />

minúsculas y mayúsculas respectivam<strong>en</strong>te.<br />

La función strrev ( 1 invierte el ord<strong>en</strong> <strong>de</strong><br />

caracteres <strong>en</strong> una cad<strong>en</strong>a.<br />

Las funciones strchr ( ), strspn ( ),<br />

strcspn ( y strpbrk ( ) permit<strong>en</strong> buscar<br />

caracteres y patrones <strong>de</strong> caracteres <strong>en</strong> cad<strong>en</strong>as.<br />

La función strstr ( busca una cad<strong>en</strong>a <strong>en</strong> otra<br />

cad<strong>en</strong>a. La función strtok ( ) rompe (divi<strong>de</strong>)<br />

una cad<strong>en</strong>a <strong>en</strong> cad<strong>en</strong>as más pequeñas (subcad<strong>en</strong>as)<br />

que se separan por caracteres separadores<br />

especificados.<br />

Asimismo, se han <strong>de</strong>scrito las funciones <strong>de</strong> conversión<br />

<strong>de</strong> cad<strong>en</strong>as <strong>de</strong> tipo numérico a <strong>datos</strong> <strong>de</strong> tipo<br />

numérico. C proporciona las sigui<strong>en</strong>tes funciones <strong>de</strong><br />

Las funciones strcmp0, stricrnp0, conversión: atoi(s), atoi(s) yatof(s),<br />

strncmp ( ) y strnicmp ( ) permit<strong>en</strong> realizar que conviert<strong>en</strong> el argum<strong>en</strong>to s (cad<strong>en</strong>a) a <strong>en</strong>teros,<br />

diversos tipos <strong>de</strong> comparaciones. Las funciones <strong>en</strong>teros largos y reales <strong>de</strong> coma flotante.


406 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

12.13. EJERCICIOS<br />

12.1. T<strong>en</strong>i<strong>en</strong>do <strong>en</strong> cu<strong>en</strong>ta el sigui<strong>en</strong>te segm<strong>en</strong>to <strong>de</strong><br />

código, indicar los errores y la forma <strong>de</strong> corregirlos.<br />

char *b = "Descanso activo";<br />

char *p = b;<br />

char c [ ] = "Para recuperar";<br />

char* cd;<br />

cd = C;<br />

cd = "Asigna cad<strong>en</strong>a";<br />

12.2. Se quiere leer <strong>de</strong>l dispositivo estándar <strong>de</strong> <strong>en</strong>trada<br />

las n códigos <strong>de</strong> asignaturas <strong>de</strong> la carrera <strong>de</strong><br />

Sociología. Escribe un segm<strong>en</strong>to <strong>de</strong> código<br />

para realizar este proceso.<br />

12.3. Para <strong>en</strong>trada <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres, qué<br />

difer<strong>en</strong>cia existe <strong>en</strong>tre s canf ( 'I % s I' ,<br />

cad<strong>en</strong>a) y gets (cad<strong>en</strong>a).¿En qÚe casos<br />

será mejor utilizar una u otra?<br />

12.4. En el sigui<strong>en</strong>te código C se lee un número real<br />

y una cad<strong>en</strong>a <strong>de</strong> caracteres. ¿Qué problemas<br />

surg<strong>en</strong> y por qué? ¿Cómo resolverlo?<br />

float x;<br />

char nom[ól] ;<br />

printf ('Distancia <strong>en</strong> Km: " ) ;<br />

scanf ("%f",&x);<br />

print f ( "Nombre <strong>de</strong>l pueblo: I' ) ;<br />

gets (nom) ;<br />

12.5. Define un array <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres para<br />

po<strong>de</strong>r leer un texto compuesto por un máximo<br />

<strong>de</strong> 80 líneas. Escribe una función para leer el<br />

texto; la función <strong>de</strong>be <strong>de</strong> t<strong>en</strong>er dos argum<strong>en</strong>tos,<br />

uno el texto y el segundo el número <strong>de</strong> líneas.<br />

12.6. Escribir una función que t<strong>en</strong>ga como <strong>en</strong>trada<br />

una cad<strong>en</strong>a y <strong>de</strong>vuelva el número <strong>de</strong> vocales,<br />

<strong>de</strong> consonantes y <strong>de</strong> dígitos <strong>de</strong> la cad<strong>en</strong>a.<br />

12.7. ¿Qué difer<strong>en</strong>cias y analogías exist<strong>en</strong> <strong>en</strong>tre las<br />

variables c 1, c 2, c 3 ? La <strong>de</strong>claración es:<br />

char **c1;<br />

char *c2 [lo1 ;<br />

char *c3 [lo] [211 ;<br />

12.8. Escribe una función que obt<strong>en</strong>ga una cad<strong>en</strong>a<br />

<strong>de</strong>l dispositivo <strong>de</strong> <strong>en</strong>trada, <strong>de</strong> igual forma que<br />

char* gets (char*). Utilizar para ello<br />

getchar ( ).<br />

12.9. Escribir una función que obt<strong>en</strong>ga una cad<strong>en</strong>a<br />

<strong>de</strong>l dispositivo estándar <strong>de</strong> <strong>en</strong>trada. La cad<strong>en</strong>a<br />

termina con el carácter <strong>de</strong> fin <strong>de</strong> línea, o bi<strong>en</strong><br />

cuando se han leído n caracteres. La función<br />

<strong>de</strong>vuelve un puntero a la cad<strong>en</strong>a leída, o EOF<br />

si se alcanzó el fin <strong>de</strong> fichero. El prototipo <strong>de</strong><br />

la función <strong>de</strong>be <strong>de</strong> ser:<br />

char*lee-linea(char*c,int n);<br />

12.10. La función atoi ( ) transforma una cad<strong>en</strong>a<br />

formada por dígitos <strong>de</strong>cimales <strong>en</strong> el equival<strong>en</strong>te<br />

número <strong>en</strong>tero. Escribir una función que<br />

transforme una cad<strong>en</strong>a formada por dígitos<br />

hexa<strong>de</strong>cimales <strong>en</strong> un <strong>en</strong>tero largo.<br />

12.11. Escribir una función para tranformar un<br />

número <strong>en</strong>tero <strong>en</strong> una cad<strong>en</strong>a <strong>de</strong> caracteres<br />

formada por los dígitos <strong>de</strong>l número <strong>en</strong>tero.<br />

12.12. Escribir una función para tranformar un<br />

número real <strong>en</strong> una cad<strong>en</strong>a <strong>de</strong> caracteres que<br />

sea la repres<strong>en</strong>tación <strong>de</strong>cimal <strong>de</strong>l número real.


1<br />

Cad<strong>en</strong>as 407<br />

12.14. PROBLEMAS<br />

12.1. Escribir un programa que lea un texto <strong>de</strong> como<br />

máximo 60 líneas, cada línea con un máximo<br />

<strong>de</strong> 80 caracteres. Una vez leído el texto intercambiar<br />

la línea <strong>de</strong> mayor longitud por la línea<br />

<strong>de</strong> m<strong>en</strong>or longitud.<br />

12.2. Escribir un programa que lea una línea <strong>de</strong> texto<br />

y escriba <strong>en</strong> pantalla las palabras <strong>de</strong> que<br />

consta la línea. Utilizar las funciones <strong>de</strong><br />

string-h.<br />

12.3. Se ti<strong>en</strong>e un texto formado por un máximo <strong>de</strong><br />

30 líneas, <strong>de</strong>l cual se quiere saber el número <strong>de</strong><br />

apariciones <strong>de</strong> la palabra CLAVE. Escribir un<br />

programa que lea el texto y la palabra CLAVE,<br />

<strong>de</strong>termine el número <strong>de</strong> apariciones <strong>de</strong> CLAVE<br />

<strong>en</strong> el texto.<br />

12.4. Se ti<strong>en</strong>e un texto <strong>de</strong> 40 líneas. Las líneas ti<strong>en</strong><strong>en</strong><br />

un número <strong>de</strong> caracteres variable. Escribir un<br />

programa para almac<strong>en</strong>ar el texto <strong>en</strong> una<br />

matriz <strong>de</strong> líneas, ajustada la longitud <strong>de</strong> cada<br />

línea al número <strong>de</strong> caracteres. El programa<br />

<strong>de</strong>be <strong>de</strong> leer el texto, almac<strong>en</strong>arlo <strong>en</strong> la <strong>estructura</strong><br />

matricial y escribir por pantalla las líneas<br />

<strong>en</strong> ord<strong>en</strong> creci<strong>en</strong>te <strong>de</strong> su longitud.<br />

12.5. Escribir un programa que lea líneas <strong>de</strong> texto,<br />

obt<strong>en</strong>ga las palabras <strong>de</strong> cada línea y las escriba<br />

<strong>en</strong> pantalla <strong>en</strong> ord<strong>en</strong> alfabético. Se pue<strong>de</strong> consi<strong>de</strong>rar<br />

que el máximo número <strong>de</strong> palabras por<br />

línea es 28.<br />

12.6. Se quiere leer un texto <strong>de</strong> como máximo 30<br />

líneas. Se quiere que el texto se muestre <strong>de</strong> tal<br />

forma que aparezcan las líneas <strong>en</strong> ord<strong>en</strong> aífabético.<br />

12.7. Se sabe que <strong>en</strong> las líneas <strong>de</strong> que forma un texto<br />

hay valores numéricos <strong>en</strong>teros, repres<strong>en</strong>tan<br />

los Kg <strong>de</strong> patatas recogidos <strong>en</strong> una finca. Los<br />

valores numéricos están separados <strong>de</strong> las<br />

palabras por un blanco, o el carácter fin <strong>de</strong><br />

línea. Escribir un programa que lea el texto y<br />

obt<strong>en</strong>ga la suma <strong>de</strong> los valores numéricos.<br />

12.8. Escribir un programa que lea una cad<strong>en</strong>a clave<br />

y un texto <strong>de</strong> como máximo 50 líneas. El<br />

programa <strong>de</strong>be <strong>de</strong> eliminar las líneas que cont<strong>en</strong>gan<br />

la clave.<br />

12.9. Se quiere sumar números gran<strong>de</strong>s, tan gran<strong>de</strong>s<br />

que no pued<strong>en</strong> almac<strong>en</strong>arse <strong>en</strong> variables<br />

<strong>de</strong> tipo long. Por lo que se ha p<strong>en</strong>sado <strong>en</strong><br />

introducir cada número como una cad<strong>en</strong>a <strong>de</strong><br />

caracteres y realizar la suma extray<strong>en</strong>do los<br />

dígitos <strong>de</strong> ambas cad<strong>en</strong>as. Hay que t<strong>en</strong>er <strong>en</strong><br />

cu<strong>en</strong>ta que la cad<strong>en</strong>a suma pue<strong>de</strong> t<strong>en</strong>er un<br />

carácter más que la máxima longitud <strong>de</strong> los<br />

sumandos.<br />

12.10. Un texto está formado por líneas <strong>de</strong> longitud<br />

variable. La máxima longitud es <strong>de</strong> 80 caracteres.<br />

Se quiere que todas las líneas t<strong>en</strong>gan la<br />

misma longitud, la <strong>de</strong> la cad<strong>en</strong>a más larga.<br />

Para ello se <strong>de</strong>be <strong>de</strong> cargar con blancos por la<br />

<strong>de</strong>recha las líneas hasta completar la longitud<br />

requerida. Escribir un programa para leer un<br />

texto <strong>de</strong> líneas <strong>de</strong> longitud variable y formatear<br />

el texto para que todas las líneas t<strong>en</strong>gan<br />

la longitud <strong>de</strong> la máxima línea.<br />

12.11. Escribir un programa que <strong>en</strong>cu<strong>en</strong>tre dos cad<strong>en</strong>as<br />

introducidas por teclado que sean anagramas.<br />

Se consi<strong>de</strong>ra que dos cad<strong>en</strong>as son anagramas<br />

si conti<strong>en</strong><strong>en</strong> exactam<strong>en</strong>te los mismos<br />

caracteres <strong>en</strong> el mismo o <strong>en</strong> difer<strong>en</strong>te ord<strong>en</strong>.<br />

Hay que ignorar los blancos y consi<strong>de</strong>rar que<br />

las mayúsculas y las minúsculas son iguales.


PARTE I11<br />

ESTRUCTURA DE DATOS


CAPíTULO 13<br />

ENTRADAS Y SALIDAS<br />

POR ARCHIVOS<br />

CONTENIDO<br />

13.1. Flujos.<br />

13.2. Puntero FILE.<br />

13.3. Apertura <strong>de</strong> un archivo.<br />

13.4. Creación <strong>de</strong> archivo<br />

secu<strong>en</strong>cial.<br />

13.5. Archivos binarios <strong>en</strong> C.<br />

13.6. Funciones para acceso<br />

aleatorio.<br />

13.7. Argum<strong>en</strong>tos <strong>de</strong> main ( 1.<br />

1 3.8. Resum<strong>en</strong>.<br />

13.9. Ejercicios.<br />

13.10. Problemas.<br />

41 O


IMTRODUCCI~N<br />

Hasta este mom<strong>en</strong>to se han realizado las operaciones básicas <strong>de</strong> <strong>en</strong>trada y<br />

salida. La operación <strong>de</strong> introducir (leer) <strong>datos</strong> <strong>en</strong> el sistema se d<strong>en</strong>omina leatura<br />

y la g<strong>en</strong>eración <strong>de</strong> <strong>datos</strong> <strong>de</strong>l sistema se d<strong>en</strong>omina esctritura. La lectura <strong>de</strong> <strong>datos</strong><br />

se realiza <strong>de</strong>s<strong>de</strong> su teclado e incluso <strong>de</strong>s<strong>de</strong> su unidad <strong>de</strong> disco, y la escritura <strong>de</strong><br />

<strong>datos</strong> se realiza <strong>en</strong> el monitor y <strong>en</strong> la impresora <strong>de</strong> su sistema.<br />

Las funciones <strong>de</strong> <strong>en</strong>tradaísalida no están <strong>de</strong>finidas <strong>en</strong> el propio l<strong>en</strong>guaje C,<br />

sino que están incorporadas <strong>en</strong> cada compilador <strong>de</strong> C bajo la forma <strong>de</strong> biblioteca<br />

<strong>de</strong> ejecución. En C existe la biblioteca stdi0.h estandarizada por ANSI; esta<br />

biblioteca proporciona tipos <strong>de</strong> <strong>datos</strong>, macros y funciones para acce<strong>de</strong>r a los<br />

archivos. El manejo <strong>de</strong> archivos <strong>en</strong> C se hace mediante el concepto <strong>de</strong> flujo<br />

(streams) o canal, o también d<strong>en</strong>ominado secu<strong>en</strong>cia. Los flujos pued<strong>en</strong> estar<br />

abiertos o cerrados, conduc<strong>en</strong> los <strong>datos</strong> <strong>en</strong>tre el programa y los dispositivos<br />

externos. Con las funciones proporcionadas por la biblioteca se pued<strong>en</strong> tratar<br />

archivos secu<strong>en</strong>ciales, <strong>de</strong> acceso directo, archivos in<strong>de</strong>xados, etc.<br />

En este capítulo apr<strong>en</strong><strong>de</strong>rá a utilizar las características típicas <strong>de</strong> E/S para<br />

archivos <strong>en</strong> C, así como las funciones <strong>de</strong> acceso más utilizadas.<br />

CONCEPTOS CLAVE<br />

Acceso aleatorio.<br />

Acceso secu<strong>en</strong>cial.<br />

Apertura y cierre <strong>de</strong> un archivo.<br />

Archivos binarios .<br />

Archivos <strong>de</strong> caracteres.<br />

Archivos in<strong>de</strong>xados.<br />

Colisiones <strong>de</strong> claves.<br />

Flujos.<br />

Registro lógico.<br />

Transformación <strong>de</strong> claves.<br />

41 1<br />

1


412 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

13.1. FLUJOS<br />

Un flujo (stream) es una abstracción que se refiere a unflujo o corri<strong>en</strong>te <strong>de</strong> <strong>datos</strong> que fluy<strong>en</strong> <strong>en</strong>tre un<br />

orig<strong>en</strong> o fu<strong>en</strong>te (productor) y un <strong>de</strong>stino o sumi<strong>de</strong>ro (consumidor). Entre el orig<strong>en</strong> y el <strong>de</strong>stino <strong>de</strong>be<br />

existir una conexión o canal (


Entradas y salidas por archivos 413<br />

procesar. Muchas <strong>de</strong> las funciones para procesar archivos son <strong>de</strong>l tipo FILE *, y ti<strong>en</strong><strong>en</strong> argum<strong>en</strong>to(s)<br />

<strong>de</strong> ese tipo.<br />

I<br />

,<br />

Ejemplo 13.1<br />

Se <strong>de</strong>clara un puntero a FILE; se escribe el prototipo <strong>de</strong> una funcicjn <strong>de</strong> tipo puntero a FTI,E y con un<br />

argum<strong>en</strong>to <strong>de</strong>l mismo tipo.<br />

FILE* pf;<br />

FILE* mostrar(FILE*); /* Prototipo <strong>de</strong> una función <strong>de</strong>finida por el<br />

proqramador" /<br />

Cabe recordar que la <strong>en</strong>trada estándar al igual que la salida están asociadas a variables puntero a<br />

FILE:<br />

FILE *stdin, *stdout;<br />

13.3. Apertura <strong>de</strong> un archivo<br />

Para procesar un archivo <strong>en</strong> C (y <strong>en</strong> todos los l<strong>en</strong>guajes <strong>de</strong> programación) la primera operación que hay<br />

que realizar es abrir el archivo. La apertura <strong>de</strong>l archivo supone conectar el archivo externo con el<br />

programa, e indicar cómo va a ser tratado el archivo: binario, <strong>de</strong> caracteres, etc. El programa acce<strong>de</strong> a<br />

los archivos a través <strong>de</strong> un puntero a la <strong>estructura</strong> FILF, la función <strong>de</strong> apertura <strong>de</strong>vuelve dicho puntero.<br />

La función para abrir un archivo es f op<strong>en</strong> ( ) y el formato <strong>de</strong> llamada es:<br />

fop<strong>en</strong>(nombre-archivo, modo);<br />

nombre = cad<strong>en</strong>a<br />

modo = cad<strong>en</strong>a<br />

Conti<strong>en</strong>e el id<strong>en</strong>tljicador externo <strong>de</strong>l archivo.<br />

Conti<strong>en</strong>e el modo <strong>en</strong> que se vu u tratar el archivo.<br />

La función <strong>de</strong>vuelve un puntero a FILE, a través <strong>de</strong> dicho puntero el programa hace refer<strong>en</strong>cia al<br />

archivo. La llamada a f op<strong>en</strong> ( ) se <strong>de</strong>be <strong>de</strong> hacer <strong>de</strong> tal forma que el valor que <strong>de</strong>vuelve se asigne a una<br />

variable puntero a FILE, para así <strong>de</strong>spués referirse a dicha variable.<br />

Ejemplo 13.2<br />

Declara una variable <strong>de</strong> tipo puntero a FILE. A continuarión escribir una s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> apertura <strong>de</strong> un<br />

archivo.<br />

FILE* pf;<br />

pf = fop<strong>en</strong>(nombre-archivo, modo);<br />

La función pue<strong>de</strong> <strong>de</strong>tectar un error al abrir el archivo, por ejemplo que el archivo no exista y se quiera<br />

leer, <strong>en</strong>tonces <strong>de</strong>vuelve NULL.<br />

Ejemplo 13.3<br />

Se <strong>de</strong>sea abrir un archivo <strong>de</strong> nombre LICENCTA . ES?' para obt<strong>en</strong>er ciertos <strong>datos</strong>.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> istdlib.h><br />

FILE *pf;<br />

char nm[l = 'C:\LICENCIA.EST";


~~<br />

41 4 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

pf = fop<strong>en</strong>(nm, "r");<br />

~f (pf == NULL)<br />

i<br />

puts("Error al abrir el archivo.");<br />

exit (1);<br />

i<br />

Ejemplo 13.4<br />

En este ejemplo se ubre el archivo <strong>de</strong> texto JAriUlNES . UAT pura escribir <strong>en</strong> él los dutos <strong>de</strong> un progrumu.<br />

En la misma línea <strong>en</strong> que se ejecuta f op<strong>en</strong> ( )<br />

contrario termina la ejecución.<br />

se comprueba que la operación ha sido correcta, <strong>en</strong> caso<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

,,<br />

I<br />

FILE *ff;<br />

char* arch = "C: \AMUIENTE\JnKolNES.unT";<br />

if ((ff = fop<strong>en</strong>(nm, "w" )==NULL)<br />

i<br />

puts ("Error al abrir el archivo pdrd escribir .") ;<br />

exit (-1);<br />

1<br />

El prototipo <strong>de</strong> f op<strong>en</strong> ( ) se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el archivo stdi o. h, es el sigui<strong>en</strong>te:<br />

FILE* fop<strong>en</strong> (const char* nombre-archivo, const char* modo) ;<br />

13.3.1. Modos <strong>de</strong> apertura <strong>de</strong> un archivo<br />

Al abrir el archivo f op<strong>en</strong> ( ) se espera como segundo argum<strong>en</strong>to el modo <strong>de</strong> tratar el archivo. Fundam<strong>en</strong>talm<strong>en</strong>te<br />

se establece si el archivo es <strong>de</strong> lectura, escritura o añadido; y si es <strong>de</strong> texto o binario. Los<br />

modos básicos se expresan <strong>en</strong> esta tabla:<br />

Modo<br />

"r"<br />

WII<br />

I' a I'<br />

r + I'<br />

"w+"<br />

"a+"<br />

Significado<br />

Abre para lectura.<br />

Abre para crear nuevo archivo (si ya existe se pierd<strong>en</strong> sus <strong>datos</strong>).<br />

Abre para añadir al final.<br />

Abre archivo ya exist<strong>en</strong>te para modificar (leer/escribir).<br />

Crea un archivo para escribidleer (si ya existe se pierd<strong>en</strong> los <strong>datos</strong>).<br />

Abre el archivo para modificar (exribidleer) al final. Si no existe es como w+.<br />

En estos modos no se ha establecido el tipo <strong>de</strong>l archivo, <strong>de</strong> texto o binario. Siempre hay una opción<br />

por <strong>de</strong>fecto y aunque <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong>l coinpilador utilizado, suele ser modo texto. Para no <strong>de</strong>p<strong>en</strong><strong>de</strong>r <strong>de</strong>l<br />

<strong>en</strong>torno es mejor indicar si es <strong>de</strong> texto o binario. Se utiliza la letra t para modo texto, la b para<br />

modo binario como Último carácter <strong>de</strong> la cad<strong>en</strong>a modo (también se pue<strong>de</strong> escribir como carácter<br />

intermedio). Por consigui<strong>en</strong>te, los modos <strong>de</strong> abrir un archivo <strong>de</strong> texto:<br />

II rt II , llwt II , " at 'I , I' r + t I' , IIw t t I' , " a t t " .<br />

Y los modos <strong>de</strong> abrir un archivo binario:


-7<br />

Entradas y salidas por archivos 41 5<br />

HrblI, llwbl1 , Ilab" ,<br />

"r+b", "w+b", "d+b".<br />

Ejemplo 13.5<br />

Se dispone archivo <strong>de</strong> texto LlCENCln. EST, se quiere leerlo para realizar un cierto proceso y escribir<br />

<strong>datos</strong> resultantes <strong>en</strong> al archivo hinario KESLJMEN . REC. Lris operaciones <strong>de</strong> apertura son:<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

FILE "pfl, *pf2;<br />

char erg[] = "C:\LlCENCIA.EC'I'';<br />

chdr dst 1 = "C : \KESUML,N. REC'" ;<br />

pfl = fopcn(orq, "rt") ;<br />

pf2 = fop<strong>en</strong>(dst,"wb");<br />

if (pfl == NULL I 1 pf2 == NULL)<br />

1<br />

puts ("Error al abrir los archivos. ) ;<br />

exit (1) ;<br />

13.3.2. NULL y EOF<br />

Las funciones <strong>de</strong> biblioteca que <strong>de</strong>vuelv<strong>en</strong> un puntero ( strcpy ( ) , f op<strong>en</strong> ( ) . . . ) especifican que si<br />

no pued<strong>en</strong> realizar la operación (g<strong>en</strong>eralm<strong>en</strong>te si hay un error) <strong>de</strong>vuelv<strong>en</strong> NULL. Esta es una macro<br />

<strong>de</strong>finida <strong>en</strong> varios archivos <strong>de</strong> cabecera, <strong>en</strong>tre los que se <strong>en</strong>cu<strong>en</strong>tran stdio . h y stdlib. h .<br />

Las funciones <strong>de</strong> librería <strong>de</strong> E/S <strong>de</strong> archivos, g<strong>en</strong>eralm<strong>en</strong>te empiezan por f <strong>de</strong> file, ti<strong>en</strong><strong>en</strong><br />

especificado que son <strong>de</strong> tipo <strong>en</strong>tero <strong>de</strong> tal forma que si la operación falla <strong>de</strong>vuelv<strong>en</strong> EOF, también<br />

<strong>de</strong>vuelv<strong>en</strong> EOF para indicar que se ha leído el fin <strong>de</strong> archivo. Esta macro está <strong>de</strong>finida <strong>en</strong> stdio . h .<br />

Ejemplo 13.6<br />

El sigui<strong>en</strong>te segm<strong>en</strong>to <strong>de</strong> ccídigo lee <strong>de</strong>1,flujo estándur <strong>de</strong> <strong>en</strong>trada hasta fin <strong>de</strong> archivo:<br />

i<br />

13.3.3. Cierre <strong>de</strong> archivos<br />

Los archivos <strong>en</strong> C trabajan con una memoria intermedia, son con hufer. La <strong>en</strong>trada y salida <strong>de</strong> <strong>datos</strong> se<br />

almac<strong>en</strong>a <strong>en</strong> ese buffer, volcándose cuando está ll<strong>en</strong>o. Al terminar la ejecución <strong>de</strong>l programa podrá<br />

ocurrir que haya <strong>datos</strong> <strong>en</strong> el hgfer. si no se volcas<strong>en</strong> <strong>en</strong> el archivo quedaría este sin las últimas<br />

actualizaciones. Siempre que se termina <strong>de</strong> procesar un archivo y siempre que se termine la ejecución<br />

<strong>de</strong>l programa los archivos abiertos hay que cerrarlos para que <strong>en</strong>tre otras acciones se vuelque el bz&fer.


41 6 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

La función €close (puntero-f ile) cierra el archivo asociado al puntero-f ile, <strong>de</strong>vuelve EOF si ha<br />

habido un error al cerrar. El prototipo <strong>de</strong> la función se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> stdio . h y es:<br />

int fclose(FILE* pf);<br />

Ejemplo 13.7<br />

Abrir dos archivos <strong>de</strong> texto, <strong>de</strong>spués se cierra cada uno <strong>de</strong> ellos.<br />

#inclu<strong>de</strong> <br />

FILE *pfl, *pf2;<br />

pfl = ~~~~~('C:\DATOS.DAT","~+") ;<br />

pf2 = fop<strong>en</strong>("C:\TEMPS.RET","b+") ;<br />

fclose(pf1);<br />

fclose(pf2);<br />

13.4. CREACIÓN DE UN ARCHIVO SECUENCIAL<br />

Una vez abierto un archivo para escribir <strong>datos</strong> hay que grabar los <strong>datos</strong> <strong>en</strong> el archivo. La biblioteca C<br />

proporciona diversas funciones para escribir <strong>datos</strong> <strong>en</strong> el archivo a través <strong>de</strong>l puntero a FILE asociado.<br />

Las funciones <strong>de</strong> <strong>en</strong>trada y <strong>de</strong> salida <strong>de</strong> archivos ti<strong>en</strong><strong>en</strong> mucho parecido con las funciones utilizadas<br />

para <strong>en</strong>trada y salida para los flujos stdin (teclado) y stdout (pantalla): printf í), scanf í),<br />

getchar ( ) , putchar ( ) , gets ( ) y puts ( ) . Todas ti<strong>en</strong><strong>en</strong> una versión para archivos que empieza por<br />

la letra f, así se ti<strong>en</strong>e fprintf ( ), fscanf ( ), fputs ( ), fgets ( ) ; la mayoría <strong>de</strong> las funciones<br />

específicas <strong>de</strong> archivos empiezan porf.<br />

13.4.1. Funciones putc ( ) y fputc ( 1<br />

Ambas funciones son idénticas, putc ( ) está <strong>de</strong>finida como macro. Escrib<strong>en</strong> un carácter <strong>en</strong> el archivo<br />

asociado con el puntero a FILE. Devuelv<strong>en</strong> el carácter escrito, o bi<strong>en</strong> EOF si no pue<strong>de</strong> ser escrito. El<br />

formato <strong>de</strong> llamada:<br />

putc(c, puntero-archivo);<br />

fputc(c, puntero-archivo);<br />

si<strong>en</strong>do c el carácter a escribir.<br />

Ejercicio 13.1<br />

Se <strong>de</strong>sea crear un archivo SALIDA. PTA con los caracteres introúucidos por teclado.<br />

Análisis<br />

Una vez abierto el archivo, un bucle mi<strong>en</strong>tras (while) no sea fin <strong>de</strong> archivo (macro EOF) lee carácter a<br />

carácter y se escribe <strong>en</strong> el archivo asociado al puntero FILE.<br />

#inclu<strong>de</strong> <br />

int main()<br />

{<br />

int c;<br />

FILE* pf;<br />

char *salida = "#SALIDA.&%@";


if ((pf = fop<strong>en</strong>(salida,"wt"))== NULL)<br />

i<br />

puts ("ERROR EN LA OPERACLON DE APERTURA') ;<br />

return 1;<br />

}<br />

Entradas y salidas por archivos 417<br />

}<br />

fclose (pf);<br />

return O;<br />

En el Ejercicio 13. I <strong>en</strong> vez <strong>de</strong> putc (c, pf) se pue<strong>de</strong> utilizar fputc (c, pf) . El prototipo <strong>de</strong> ambas<br />

funciones se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> stdio. h , es el sigui<strong>en</strong>te:<br />

int putc(int c, FLLE* pf);<br />

int fputc( int c, FILE* pf) ;<br />

13.4.2. Funciones getc ( ) y fgetc ( )<br />

Estas dos funciones son iguales, igual formato e igual funcionalidad; pued<strong>en</strong> consi<strong>de</strong>rarse que son<br />

recíprocas <strong>de</strong> putc ( ) y fputc ( ) . Éstas, getc ( ) y fgetc ( ) , le<strong>en</strong> un carácter (el sigui<strong>en</strong>te carácter)<br />

<strong>de</strong>l archivo asociado al puntero a FILE. Devuelv<strong>en</strong> el carácter leído o EOF si es fin <strong>de</strong> archivo (o si ha<br />

habido un error). El formato <strong>de</strong> llamada es:<br />

getc (puntero-archivo);<br />

fgetc(punter0-archivo);<br />

I<br />

Ejercicio 13.2<br />

El archivo SALIDA. PTA, creado <strong>en</strong> el Problema 13.1, se <strong>de</strong>sea leer para mostrarlo por pantalla y<br />

contar las líneas que ti<strong>en</strong>e.<br />

Análisis<br />

Una vez abierto el archivo <strong>de</strong> texto <strong>en</strong> modo lectura, un bucle mi<strong>en</strong>tras no sea fin <strong>de</strong> archivo (macro<br />

EOF) lee carácter a carácter y se escribe <strong>en</strong> pantalla. En el caso <strong>de</strong> leer el carácter <strong>de</strong> fin <strong>de</strong> línea se <strong>de</strong>be<br />

saltar a la línea sigui<strong>en</strong>te y contabilizar una línea más.<br />

#inclu<strong>de</strong> <br />

int main()<br />

i<br />

int c, n=O;<br />

FILE* pf;<br />

char *nombre = "\\\SALIDA.TXT";<br />

if ((pf = fop<strong>en</strong>(nombre,"rt")) == NULL)<br />

{<br />

1<br />

puts ("ERROR EN LA OPERACION DE APERTURA") ;<br />

return 1;


418 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

I<br />

if (c == '\no<br />

{<br />

n++; printf ("\n") ;<br />

1<br />

else<br />

putchar (c);<br />

i<br />

printf ("\nNÚmero <strong>de</strong> líneds <strong>de</strong>l archivo: %d",n);<br />

fclose (pf);<br />

return O;<br />

El prototipo <strong>de</strong> ambas funciones se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> stdio . h y es el sigui<strong>en</strong>te:<br />

int getc(FILE* p f);<br />

int fgetc (FILE* pf) ;<br />

13.4.3. Funciones fputs () y fgets ()<br />

Estas funciones escrib<strong>en</strong>/le<strong>en</strong> una cad<strong>en</strong>a <strong>de</strong> caracteres <strong>en</strong> el archivo asociado. La función f put s í )<br />

escribe una cad<strong>en</strong>a <strong>de</strong> caracteres. La función <strong>de</strong>vuelve I:OF si no ha podido escribir la cad<strong>en</strong>a, un valor<br />

no negativo si la escritura es correcta; el formato <strong>de</strong> llamada es:<br />

fputs(cad<strong>en</strong>a, puntero-archivo) ;<br />

La función fget s ( ) lee una cad<strong>en</strong>a <strong>de</strong> caracteres <strong>de</strong>l archivo. Termina la captación <strong>de</strong> la cad<strong>en</strong>a cuando<br />

lee el carácter <strong>de</strong> tin <strong>de</strong> línea, o bi<strong>en</strong> cuando ha leído n-1 caracteres, si<strong>en</strong>do n un argum<strong>en</strong>to <strong>en</strong>tero <strong>de</strong><br />

la función. La función <strong>de</strong>vuelve un puntero a la cad<strong>en</strong>a <strong>de</strong>vuelta, o NUL,I, si ha habido un error. El<br />

formato <strong>de</strong> llamada es:<br />

fgets(cad<strong>en</strong>a, n, puntero-drchivo);<br />

Ejemplo 13.8<br />

Lectura <strong>de</strong> un múximo <strong>de</strong> 80 caracteres <strong>de</strong> un archivo:<br />

#<strong>de</strong>fine T 81<br />

char cad['rl ;<br />

FILE *f;<br />

fgets(cad, T, f);<br />

Ejercicio 13.3<br />

El archivo CARTAS. DAT conti<strong>en</strong>e un texto al que se le <strong>de</strong>seu aíiudir nuevas líneas, <strong>de</strong> longitud mínima<br />

30 caracteres, <strong>de</strong>súe el archivo PRIMERO. DA?:<br />

Análisis<br />

El problema se resuelve abri<strong>en</strong>do el primer archivo <strong>en</strong> modo añadir ('la") , el segundo archivo <strong>en</strong> modo<br />

lectura ('Ir"). Las líneas se le<strong>en</strong> con fgets ( 1, si cumpl<strong>en</strong> la condición <strong>de</strong> longitud se escrib<strong>en</strong> <strong>en</strong> el


Entradas y salidas por archivos 419<br />

archivo CARTAS. Al t<strong>en</strong>er que realizar un proceso completo <strong>de</strong>l archivo, se realizan iteraciones<br />

mi<strong>en</strong>tras no fin <strong>de</strong> archivo.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine MX 121<br />

#<strong>de</strong>fine MN 30<br />

int main0<br />

i<br />

FILE *in, *out;<br />

char nom1 [ ] = I' \ \CARTAS. DAT" ;<br />

char nom2 [ ] = " \ \PRIMERO. DAT" ;<br />

char cad[MXl;<br />

in = f op<strong>en</strong> ( nom2, "rt " ) ;<br />

out= fop<strong>en</strong>(nom1,"at') ;<br />

if (in==NULL I 1 out==NULL)<br />

{<br />

1<br />

puts ("Error al<br />

exit (-1) ;<br />

fclose(in);<br />

fclose (out);<br />

return O;<br />

abrir archivos. " 1 ;<br />

while (fgets(cad<br />

MX,in)) /*itera hasta que <strong>de</strong>vuelve puntero NULL*/<br />

1<br />

if (strl<strong>en</strong>(cad >= MN)<br />

fputs (cad, out<br />

else<br />

puts (cad);<br />

I<br />

El prototipo <strong>de</strong> ambas funciones está <strong>en</strong> stdio . h, es el sigui<strong>en</strong>te:<br />

int fputs(char* cad, FILE* p t);<br />

char* fgets(char* cad, int n, FILE* p f);<br />

13.4.4. Funciones fprintf ( ) y f scanf ( )<br />

Las funciones printf ( ) y scant ( ) permit<strong>en</strong> escribir o leer variables cualquier tipo <strong>de</strong> dato estándar,<br />

los códigos <strong>de</strong> formato (%d, % f ...) indican a C la transformación que <strong>de</strong>be <strong>de</strong> realizar con la secu<strong>en</strong>cia<br />

<strong>de</strong> caracteres (conversión a <strong>en</strong>tero.. .). La misma funcionalidad ti<strong>en</strong>e fprint f ( ) y f scanf ( ) con los<br />

flujos (archivos asociados) a que se aplican. Estas dos funciones ti<strong>en</strong><strong>en</strong> como primer argum<strong>en</strong>to el<br />

puntero a file asociado al archivo <strong>de</strong> texto.<br />

Ejercicio 13.4<br />

Se <strong>de</strong>sea crear el archivo <strong>de</strong> texto PERSONAS. DAT <strong>de</strong> tal forma que cada línea cont<strong>en</strong>ga un registro con<br />

los <strong>datos</strong> <strong>de</strong> una persona que cont<strong>en</strong>ga los campos nombre, fecha <strong>de</strong> nacimi<strong>en</strong>to (dia(nn), mes(nn),<br />

uño(nnnn) y mes <strong>en</strong> ASCII).


420 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Análisis<br />

En la <strong>estructura</strong> persona se <strong>de</strong>claran los campos correspondi<strong>en</strong>tes. Se <strong>de</strong>fine una función que <strong>de</strong>vuelve<br />

una <strong>estructura</strong> persona leída <strong>de</strong>l teclado. El mes <strong>en</strong> ASCII se obti<strong>en</strong>e <strong>de</strong> una función que ti<strong>en</strong>e como<br />

<strong>en</strong>trada el número <strong>de</strong> mes y <strong>de</strong>vuelve una cad<strong>en</strong>a con el mes <strong>en</strong> ASCII. Los campos <strong>de</strong> la <strong>estructura</strong> son<br />

escritos <strong>en</strong> el archivo con fprint f í ) .<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

/* <strong>de</strong>claración <strong>de</strong> tipo global <strong>estructura</strong> */<br />

type<strong>de</strong>f struct {<br />

char* nm;<br />

int dia;<br />

int ms;<br />

int aa;<br />

char mes [ll] ;<br />

} PERSONA;<br />

void <strong>en</strong>trada(PERSONA* p<br />

char* mes-asci(short n)<br />

int main0<br />

FILE *pff;<br />

char nf [ ] = "\ \PERSONS<br />

char r = 'SI;<br />

DAT " :<br />

if ((pff = fop<strong>en</strong>(nf,"wt")==NULL)<br />

i<br />

puts ("Error al abrir archivos. ") ;<br />

exit (-1);<br />

1<br />

while (toupper(r) == 'S')<br />

I<br />

PERSONA pt;<br />

<strong>en</strong>trada(&pt) ;<br />

printf("%s %d-%d-%d %s\n",pt.nm,pt.dia,pt.ms,pt.aa,pt.mes);<br />

fprintf(pff,"%s %d-%d-%d %s\n",pt.nm,pt.dia,pt.ms,pt.aa,pt.mes);<br />

printf ("otro registro?: ") ; scanf ("%c%*cII,&r);<br />

1<br />

fclose (pf f) ;<br />

return O;<br />

void <strong>en</strong>trada(PERS0NA" p)<br />

{ char bf[81];<br />

printf ("Nombre: "); gets(bf) ;<br />

p->nm =(char*)malloc((strl<strong>en</strong>(bf)+l)*sizeof(char));<br />

strcpy (p->nm, bf) ;<br />

printf("Fecha <strong>de</strong> nacimi<strong>en</strong>to(dd mm aaaa): ");<br />

scanf ("%d%d %d%*c",&p->dia,&p->ms,&p->aa);<br />

printf ("\n%s\n",mes-asci(p->ms ) ;<br />

strcpy(p->mes,mes-asci(p->ms)) ;<br />

1<br />

char* mes-asci(short n)


1<br />

Entradas y salidas por archivos 421<br />

{<br />

1<br />

static char *mes[l2]= {<br />

"Enero", "Febrero", "Marzo", "Abril",<br />

"Mayo", "Junio", " Jul i 0'' , "Agosto",<br />

'I S ep t i embr e " , " O c t ubre I' , I' Nov i embr e 'I , I' Di c i embr e I' } ;<br />

if (n r=l && n


I- -<br />

422 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El prototipo <strong>de</strong> la función se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> stdio . h:<br />

void rewind(FILE*pf);<br />

c<br />

Ejemplo 13.10<br />

Este ejemplo lee un archivo <strong>de</strong> texto, cu<strong>en</strong>ta el número <strong>de</strong> líneas que conti<strong>en</strong>e y a continuación sitúa el<br />

puntero <strong>de</strong>l archivo al inicio para una lectura posterior.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

FILE* pg;<br />

char nom [ ] ="PLWIO. DAT" ;<br />

char buf [1211 ;<br />

int nl = O;<br />

if ((pg = fop<strong>en</strong>(nom,"rt") )==NULL)<br />

t<br />

puts ( "Error al abrir el archivo. I' ) ;<br />

exit (-1);<br />

i<br />

while (!feof (pg )<br />

{<br />

fgets (buf, 121,pg) ; nl++;<br />

1<br />

rewind(pg);<br />

/* De nuevo pue<strong>de</strong> procesarse el archivo */<br />

while (!feof(pg )<br />

I<br />

13.5. Archivos binarios <strong>en</strong> C<br />

Para abrir un archivo <strong>en</strong> modo binario hay que especificar la opción b <strong>en</strong> el modo. Los archivos binarios<br />

son secu<strong>en</strong>cias <strong>de</strong> 0,s y 1,s. Una <strong>de</strong> las características <strong>de</strong> los archivos binarios es que optimizan la<br />

memoria ocupada por un archivo, sobre todo con campos numéricos. Así, almac<strong>en</strong>ar <strong>en</strong> modo binario<br />

un <strong>en</strong>tero supone una ocupación <strong>de</strong> 2 bytes o 4 bytes (<strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong>l sistema), y un número real 4 bytes o<br />

8 bytes; <strong>en</strong> modo texto primero se convierte el valor numérico <strong>en</strong> una cad<strong>en</strong>a <strong>de</strong> dígitos (%6d,<br />

% 8 .2 f . . . ) y <strong>de</strong>spués se escribe <strong>en</strong> el archivo. La mayor efici<strong>en</strong>cia <strong>de</strong> los archivos binarios se<br />

contrapone con el hecho <strong>de</strong> que su lectura se ti<strong>en</strong>e que hacer <strong>en</strong> modo binario y que sólo se pued<strong>en</strong><br />

visualizar <strong>de</strong>s<strong>de</strong> el <strong>en</strong>torno <strong>de</strong> un programa C. Los modos para abrir un archivo binario son los mismos<br />

que para abrir un archivo <strong>de</strong> texto, sustituy<strong>en</strong>do la t por b:<br />

llrb", Ilwb", "ab",<br />

"r+b", "w+b", Ild+b"<br />

Ejemplo 13.11<br />

En este ejemplo se <strong>de</strong>claran 3 punteros a FILE. A continuación se abr<strong>en</strong> tres archivos <strong>en</strong> modo binario.<br />

FILE *pfl, *pf2, "pf3;<br />

pfl = fop<strong>en</strong>("gorjal.arr", "rb"); /*Lectura <strong>de</strong> archivo binario */<br />

pf2 = fop<strong>en</strong> ("tempes. feb", "w+b"); /*lccr/escribir archivo binarlo*/


Entradas y salidas por archivos 423<br />

pf3 = fop<strong>en</strong>("te1con. tff","ab") ; /*dñadir d archivo binario*/<br />

La biblioteca <strong>de</strong> C proporciona dos funciones especialm<strong>en</strong>te dirigidas al proceso <strong>de</strong> <strong>en</strong>trada y salida<br />

<strong>de</strong> archivos binarios con buffer, son f read ( ) y fwr i te ( ) .<br />

13.5.1. Función <strong>de</strong> salida fwrite ( )<br />

La función fwri te ( ) escribe un bgffer <strong>de</strong> cualquier tipo <strong>de</strong> dato <strong>en</strong> un archivo binario. El formato <strong>de</strong><br />

llamada es:<br />

fwrite ( direction-bu f fer, tdniafio, nuni-e? em<strong>en</strong> t os, purl t ero-drchi vo) ;<br />

Ejemplo 13.12<br />

En el ejemplo se abre un archivo <strong>en</strong> modo binario para escritura. Se escrib<strong>en</strong> números reales <strong>en</strong> doble<br />

precisión <strong>en</strong> el bucle for. El buffer es la variable x, el tamaño lo <strong>de</strong>vuelve el operador s i /cof.<br />

FILE *fd;<br />

double x;<br />

fd = fop<strong>en</strong>("reales.num', "wb");<br />

for (x=O. 5; x>O. 01; )<br />

fwrite(hx, sizeof (double), 1, fd) ;<br />

x = pow(x,2.);<br />

El prototipo <strong>de</strong> la función está <strong>en</strong> stdlo. h:<br />

size-t fwrite(const void *ptr,size-t t~rn,size-t n,FILE *pi);<br />

Eltipo size-t está<strong>de</strong>finido<strong>en</strong> stdio.hyesuntipo int.<br />


424 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

FILE "pp;<br />

if ((pp = fop<strong>en</strong>(nom,"wb') )==NULL)<br />

i<br />

puts("\nError <strong>en</strong> ld operación <strong>de</strong> abrir archivo.") ;<br />

return -1;<br />

puts("\nIntroduce coord<strong>en</strong>adas <strong>de</strong> puntos, para acabar: (0,O)");<br />

do i<br />

scanf ("%d%d",&p.x,&p.y);<br />

while (p.xO I I p.y>O)<br />

i<br />

fwrite(&p, sizeoE(PUNTO), 1, pp);<br />

1<br />

} while (p.x>O I 1 p.y>O);<br />

1<br />

fclose (pp) ;<br />

return O;<br />

'*<br />

E<br />

Los archivos binarios están indicados especialm<strong>en</strong>te para guardar registros, <strong>estructura</strong>s <strong>en</strong> C. El<br />

método habitual es la escritura sucesiva <strong>de</strong> <strong>estructura</strong>s <strong>en</strong> el archivo asociado al puntero, la lectura <strong>de</strong><br />

estos archivos es similar.<br />

13.5.2. Función <strong>de</strong> lectura f read ( )<br />

Esta función lee <strong>de</strong> un archivo n bloques <strong>de</strong> bytes y los almac<strong>en</strong>a <strong>en</strong> un buffer. El número <strong>de</strong> bytes <strong>de</strong><br />

cada bloque ( tamaño ) se pasa como parámetro, al igual que el número n <strong>de</strong> bloques y la dirección <strong>de</strong>l<br />

buffer (o variable) don<strong>de</strong> se almac<strong>en</strong>a. El formato <strong>de</strong> llamada:<br />

fread(direccion-buffer,tamaño,n,puntero-archivo) ;<br />

La función <strong>de</strong>vuelve el número <strong>de</strong> bloques que lee y <strong>de</strong>be <strong>de</strong> coincidir con n. El prototipo <strong>de</strong> la<br />

función está <strong>en</strong> stdlo. h:<br />

size-t fread(void *ptr,size-t tdm,size-t n,FILE *pf);<br />

Ejemplo 13.13<br />

En el ejemplo se abre un archivo <strong>en</strong> modo binario para lectura. El archivo se lee hasta el final <strong>de</strong>l<br />

archivo; cada lectura <strong>de</strong> un número real se acumula <strong>en</strong> la variable s.<br />

FILE *fd;<br />

double x,s=O.O;<br />

if ((fd = fop<strong>en</strong>("reales.num","rb") )==NULL)<br />

exit (-1);<br />

while (!eof(fd )<br />

i<br />

fread(&x, sizeof(double), 1, fd);<br />

s+= x;<br />

1


Entradas y salidas por archivos 425<br />

Ejercicio 13.6<br />

En el Ejercicio 13.5 se ha creado un archivo hinario <strong>de</strong> puntos <strong>en</strong> el plano. Se <strong>de</strong>sea escribir un<br />

programa para <strong>de</strong>terminar los sigui<strong>en</strong>tes valores:<br />

n,, número <strong>de</strong> veces que aparece un punto dado (ij) <strong>en</strong> el archivo.<br />

Dado un valor <strong>de</strong>j, obt<strong>en</strong>er la media <strong>de</strong> i para los puntos que conti<strong>en</strong><strong>en</strong> a j.<br />

~~<br />

. I=I<br />

1, =<br />

2 n,,<br />

I= I<br />

Análisis<br />

La primera instrucción es abrir el archivo binario para lectura. A continuación se solicita el punto don<strong>de</strong><br />

se cu<strong>en</strong>tan las ocurr<strong>en</strong>cias <strong>en</strong> el archivo. En la función cu<strong>en</strong>ta-pto í ) se <strong>de</strong>termina dicho número;<br />

para lo cual hay que leer todo el archivo. Para ejecutar el segundo apartado, se solicita el valor <strong>de</strong> j. Con<br />

un bucle <strong>de</strong>s<strong>de</strong> i=l hasta 10 O se cu<strong>en</strong>ta las ocurr<strong>en</strong>cias <strong>de</strong> cada punto (ij) llamando a la función<br />

cu<strong>en</strong>tagto ( ) ; antes <strong>de</strong> cada llamada hay que situar el puntero <strong>de</strong>l archivo al inicio, llamando para ello<br />

a la función rewind ( ) .<br />

#inclu<strong>de</strong> <br />

struct punto<br />

i<br />

int 1, j;<br />

1.<br />

I,<br />

type<strong>de</strong>f struct punto PUNTO;<br />

FILE *pp;<br />

int cu<strong>en</strong>ta-pto(PUNT0 w);<br />

int main()<br />

i<br />

PUNTO p;<br />

char *nom ="C : \PUNTOS. DAT" ;<br />

float media, nmd, dnm;<br />

if ((pp = fop<strong>en</strong>(nom,'rb") )==NULL)<br />

i<br />

puts("\nError al abrir archivo pdra lecturo.");<br />

return -1;<br />

J<br />

printf ("\nIntroduce coord<strong>en</strong>adas <strong>de</strong> punto a buscar: ") ;<br />

scanf ("%d %d",&p. I, &p. J ) ;<br />

printf ("\nKepeticiones <strong>de</strong>l punto (%d,%d): %d\n",<br />

P.I,P.J ,cu<strong>en</strong>ta-pto(p)) ;<br />

/* Cálculo <strong>de</strong> la media i para un valor J */<br />

printf ("Valor <strong>de</strong> J : ") ; scanf ("%d",&p. J) ;<br />

media=nmd=dnm= 0.0;


426 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

for (p.1~1; p.iO.O)<br />

medi a = rimd/diim;<br />

printf ("\nMediCi <strong>de</strong> los valores <strong>de</strong> i pdr-a %d<br />

return O;<br />

int cu<strong>en</strong>ta-pto (PUNTO w)<br />

i<br />

PUNTO p;<br />

irit c;<br />

}<br />

r = O;<br />

while<br />

{<br />

( ! feof (pp))<br />

fread(&p,sizeof (PUN'I'O),l,pp);<br />

if (p.i==w.i&& p. j==w. j) r++;<br />

i<br />

return r;<br />

&.%f",p.j,media);<br />

fl I*<br />

13.6. Funciones para acceso aleatorio<br />

El acceso directo -aleatorio- a los <strong>datos</strong> <strong>de</strong> un archivo se hace mediante su posición, es <strong>de</strong>cir, el lugar<br />

relativo que ocupan. Ti<strong>en</strong>e la v<strong>en</strong>taja <strong>de</strong> que se pued<strong>en</strong> leer y escribir registros <strong>en</strong> cualquier ord<strong>en</strong> y<br />

posición. Son muy rápidos <strong>de</strong> acceso a la información que conti<strong>en</strong><strong>en</strong>. El principal inconv<strong>en</strong>i<strong>en</strong>te que<br />

ti<strong>en</strong>e la organización directa es que necesita programar la relación exist<strong>en</strong>te <strong>en</strong>tre el cont<strong>en</strong>ido <strong>de</strong> un<br />

registro y la posición que ocupan.<br />

Las funciones f seek ( ) y f te1 i ( ) se usan principalm<strong>en</strong>te para el acceso directo a archivos <strong>en</strong> C.<br />

Éstas consi<strong>de</strong>ran el archivo como una secu<strong>en</strong>cia <strong>de</strong> bytes; el número <strong>de</strong> byte es el índice <strong>de</strong>l archivo.<br />

Según se va ley<strong>en</strong>do o escribi<strong>en</strong>do registros o <strong>datos</strong> <strong>en</strong> el archivo, el programa manti<strong>en</strong>e a través <strong>de</strong> un<br />

puntero la posición actual. Con la llamada a la función f te 1 i ( ) se obti<strong>en</strong>e el valor <strong>de</strong> dicha posición.<br />

La llamada a f seek ( ) permite cambiar la posición <strong>de</strong>l puntero al archivo a una dirección <strong>de</strong>terminada.<br />

13.6.1. Función f seek ( )<br />

Con la función fseek ( ) se pue<strong>de</strong> tratar un archivo <strong>en</strong> C como un array que es una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

<strong>de</strong> acceso aleatorio. f seek ( ) sitúa el puntero <strong>de</strong>l archivo <strong>en</strong> una posición aleatoria, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong>l<br />

<strong>de</strong>splazami<strong>en</strong>to y el orig<strong>en</strong> relativo que se pasan como argum<strong>en</strong>tos. En el Ejemplo 13.14 se supone que<br />

existe un archivo <strong>de</strong> productos, se pi<strong>de</strong> el número <strong>de</strong> producto y se sitúa el puntero <strong>de</strong>l archivo para leer<br />

el registro <strong>en</strong> una operación <strong>de</strong> lectura posterior.<br />

Ejemplo 13.14<br />

Declarar una <strong>estructura</strong> (registro) PRODUCTO, y abrir un arcliivo para lectura. Se <strong>de</strong>sea leer un registro<br />

cuyo número (posición) se pi<strong>de</strong> por teclado.


Entradas y salidas por archivos 427<br />

type<strong>de</strong>f struct<br />

i<br />

char nombre 1411 ;<br />

int unida<strong>de</strong>s;<br />

float precio;<br />

int pedidos;<br />

} PRODUCTO;<br />

PRODUCTO uno;<br />

int n, stat;<br />

FILE* pfp;<br />

if ( (pfp = fop<strong>en</strong>("conservas.dat","r")) ==NULL)<br />

i<br />

puts ("No se pue<strong>de</strong> abrir el archivo.") ;<br />

exit (-1);<br />

1<br />

/* Se pi<strong>de</strong> el número <strong>de</strong> registro */<br />

printf ("Número <strong>de</strong> registro: ") ; scanf ("%d",&n) ;<br />

/* Sitúa el puntero <strong>de</strong>l archivo */<br />

stat = fseek(pfp, n*sizeoE(PRODUCTO),O);<br />

/* Comprueba que no ha habido error */<br />

if (stat != O)<br />

i<br />

puts("Error, puntero <strong>de</strong>l archivo movido fuera <strong>de</strong> este");<br />

exit (-1):<br />

i<br />

/* Lee el registro */<br />

fread(&uno, sizeof(PRODUCTO), 1, pfp);<br />

. . .<br />

El segundo argum<strong>en</strong>to <strong>de</strong> f seek ( ) es el <strong>de</strong>splazami<strong>en</strong>to, el tercero es el orig<strong>en</strong> <strong>de</strong>l <strong>de</strong>splazami<strong>en</strong>to,<br />

el O indica que empiece a contar <strong>de</strong>s<strong>de</strong> el principio <strong>de</strong>l archivo.<br />

El formato para llamar a f seek ( ) :<br />

fseek(punter0-archivo, <strong>de</strong>spldzdmicnto, orig<strong>en</strong>);<br />

<strong>de</strong>splazami<strong>en</strong>to : es el número <strong>de</strong> bytes a mover; ti<strong>en</strong><strong>en</strong> que ser <strong>de</strong> tipo long.<br />

orig<strong>en</strong><br />

: es la posición <strong>de</strong>s<strong>de</strong> la que se cu<strong>en</strong>ta el número <strong>de</strong> bytes a mover. Pue<strong>de</strong> t<strong>en</strong>er<br />

tres valores, que son:<br />

O : Cu<strong>en</strong>ta <strong>de</strong>s<strong>de</strong> el inicio <strong>de</strong>l archivo.<br />

1 : Cu<strong>en</strong>ta <strong>de</strong>s<strong>de</strong> la posición actual <strong>de</strong>l puntero al archivo.<br />

2 : Cu<strong>en</strong>ta <strong>de</strong>s<strong>de</strong> el final <strong>de</strong>l archivo.<br />

Estos tres valores están repres<strong>en</strong>tados por tres id<strong>en</strong>tificadores (macros):<br />

0 : SEEK-SET<br />

1 : SEEK-CUR<br />

2 : SEEK-END<br />

La función f seek ( ) <strong>de</strong>vuelve un valor <strong>en</strong>tero, distinto <strong>de</strong> cero si se comete un error <strong>en</strong> su ejecución;<br />

cero si no hay error. El prototipo se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> st dlo . h :<br />

int fseek(F1LE *pf,long dsplz,int orig<strong>en</strong>);<br />

Ejercicio 13.7<br />

Para celebrar las fiestas patronales <strong>de</strong> un pueblo se celebra una carrera popular <strong>de</strong> 9 Km. Se establec<strong>en</strong><br />

las categorías masculina (M) y fem<strong>en</strong>ina (F), y por cada una <strong>de</strong> ellas, s<strong>en</strong>ior y veterano. Los nacidos


L- A<br />

428 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

antes <strong>de</strong> 1954 son veteranos (tanto para hombres como para mujeres) y el resto s<strong>en</strong>iors. Según se<br />

realizan inscripciones se crea el archivo binario CARRERA. POP, <strong>de</strong> tal forma que el número <strong>de</strong> dorsal es<br />

la posición que ocupa el registro <strong>en</strong> el archivo. La carrera se celebra; según llegan los corredores se<br />

toman los tiempos realizados y los números <strong>de</strong> dorsales.<br />

Se <strong>de</strong>sea escribir un programa para crear el archivo CARRERA. POP y un segundo programa que<br />

actualice cada registro, según el número <strong>de</strong> dorsal, con el tiempo realizado <strong>en</strong> la carrera.<br />

Análisis<br />

En una <strong>estructura</strong> se agrupan los campos necesarios para cada participante: nombre, año <strong>de</strong> nacimi<strong>en</strong>to,<br />

sexo, categoría, tiempo empleado (minutos, segundos), número <strong>de</strong> dorsal y puesto ocupado. El primer<br />

programa abre el archivo <strong>en</strong> modo binario para escribir los registros correspondi<strong>en</strong>tes a los participantes<br />

<strong>en</strong> la posición <strong>de</strong>l número <strong>de</strong> dorsal. Los números <strong>de</strong> dorsal se asignan según la categoría, para las<br />

mujeres veteranas <strong>de</strong>l 51 al 100; para mujeres s<strong>en</strong>ior <strong>de</strong> 101 al 200. Para hombres veteranos <strong>de</strong> 251 al<br />

500, y para s<strong>en</strong>ior <strong>de</strong>l 501 al 1000.<br />

El programa, <strong>en</strong> primer lugar inicializa los nombres <strong>de</strong> los registros <strong>de</strong>l archivo a blancos. Los<br />

dorsales se asignan aleatoriam<strong>en</strong>te, comprobando que no estén previam<strong>en</strong>te asignados. El segundo<br />

programa abre el archivo <strong>en</strong> modo modificación, acce<strong>de</strong> a un registro, según dorsal y escribe el tiempo<br />

y puesto. Los tipos <strong>de</strong> <strong>datos</strong> que se crean para la aplicación, <strong>estructura</strong> fecha, <strong>estructura</strong> tiempo,<br />

<strong>estructura</strong> atleta, se incluy<strong>en</strong> <strong>en</strong> el archivo atleta . h.<br />

/* Archivo at1eta.h */<br />

type<strong>de</strong>f struct fecha<br />

1<br />

int d, in, a;<br />

}FECHA;<br />

type<strong>de</strong>f struct tiempo<br />

{<br />

int h, m, s;<br />

}TIEMPO;<br />

struct atleta<br />

i<br />

char nombre [ 28 I ;<br />

FECHA f;<br />

char sx; /* Sexo */<br />

char cat; /* Categoria */<br />

TIEMPO t ;<br />

unsigned int dorsal;<br />

unsigned short puesto;<br />

1;<br />

type<strong>de</strong>f struct atleta ADTA;<br />

#<strong>de</strong>fine <strong>de</strong>splz (n) (n-1)*sizeor(ADTA)<br />

/* Programa para dar <strong>en</strong>trada <strong>en</strong> el archivo <strong>de</strong> atletas. */<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> "atleta. h"<br />

void inicializar(FILE*);<br />

void unatleta(ADTA* at,FILE*);<br />

unsigned numdorsal(char s, chdr cat, FILE* p f);<br />

int main()


Entradas y salidas por archivos 429<br />

1<br />

FILE *pf;<br />

ADTA a;<br />

char *archivo= "C: \CARRERA. POL";<br />

randomize ( ) ;<br />

if ( (pf=fop<strong>en</strong>(archivo, "wb+")==NULL)<br />

{<br />

printf("\nError al abrir el drchivo as, fin <strong>de</strong>l proceso.\n'j;<br />

return -1;<br />

1<br />

inicializar (pf j ;<br />

/* Se introduc<strong>en</strong> registros hdstü teclear como nombre: FIN */<br />

unatleta(&a,pfj;<br />

do {<br />

fseek(pf,<strong>de</strong>splz(a.dorsal) ,SEEK-SET);<br />

fwrite(&a,sizeof(ADTAj,l,pf);<br />

unatleta(&a,pf);<br />

}while (strcmpi(a.nombre,"FIN")) ;<br />

fclose (pf) ;<br />

return 0;<br />

void unatleta(ADTA* at, FILE*pf)<br />

i<br />

print f ( "Nombre:<br />

");gets(dt->nombre);<br />

i f ( s t r cmp i ( at - >nombre, 'I f in I' j )<br />

i<br />

printf ("Fecha <strong>de</strong> nacimi<strong>en</strong>to: ") ;<br />

scanf ("%d%d %d%*c",&at->f.d,&at->f.m,&at->f.a);<br />

if (at->f.acat = 'VI;<br />

else<br />

at->cat = IS';<br />

1<br />

1<br />

print f ( "Sexo:<br />

");scanf("%c%*c",&at->sx)<br />

;<br />

at->sx=(char)toupper(at->sx);<br />

at->t.h = 0; at->t.m = 0; at->t.s = O;<br />

at->dorsal = numdorsal(at->sx,at->cat,pf);<br />

printf("Dorsa1 asignado: %u\n",at->dorsal);<br />

unsigned numdorsal(char s, char cat, FILE* pfj<br />

i<br />

unsigned base, tope, d;<br />

ADTA a;<br />

if (s=='M' && cat=='V')<br />

i<br />

base = 251; tope = 500;<br />

I<br />

base = 501; tope = 1000;


430 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

1<br />

d = (unsigned) random(tope+l-base)+base;<br />

fseek(pf,<strong>de</strong>spla(d),SEEK_SET);<br />

fread(&a,sizeof(ADTA),l,p~);<br />

if (!(*a.nombre)) /* Cad<strong>en</strong>d nula: está vacío */<br />

return d;<br />

else<br />

return numdorsal(s,cat,Pf);<br />

void inicializar(FILE*pf)<br />

{<br />

int k;<br />

ADTA a:<br />

a.nombre[Ol = '\O';<br />

for (k=l; k


Entradas y salidas por archivos 431<br />

/* SP situd el puntero <strong>en</strong> el reyistro */<br />

fceek (pf, <strong>de</strong>splz (dorsdl), SEEK-CFT) ;<br />

fredd(ha,sizeof(ADTA),l,pf) ;<br />

if (*d.nombre)<br />

I<br />

<strong>datos</strong>dtleta(a);<br />

printf ("\n Tiempo realizado <strong>en</strong> miniit.os y seqiindos: ") ;<br />

scant ("%a%d",&h.m,&h.s)<br />

;<br />

ii.t = h;<br />

tseck (pf, <strong>de</strong>spl z (dorsal), Sb:i-:ti-SKT) ;<br />

fwrite(&a,sizeof(AD'TA),l,pf);<br />

i<br />

else<br />

printf ("Este dorsal no est& rey i ~Lrddo. \n");<br />

printf ("\n Dorsal <strong>de</strong>l dt.let


432 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

printf('Posición actual: &ld\n",ftell(pf));/*muestra 24*/<br />

fclose(pf);<br />

return O;<br />

Para llamar a la función se pasa como argum<strong>en</strong>to el puntero a FIL,E. El prototipo se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong><br />

stdi0.h:<br />

long int ftell(F1LE *pf);<br />

13.7. DATOS EXTERNOS AL PROGRAMA CON ARGUMENTOS DE main ( )<br />

La línea <strong>de</strong> comandos o <strong>de</strong> órd<strong>en</strong>es es una línea <strong>de</strong> texto <strong>de</strong>s<strong>de</strong> la que se pue<strong>de</strong> ejecutar un programa. Por<br />

ejemplo, si se ha escrito el programa matrices. c , una vez compilado da lugar a matrices. exe . Su<br />

ejecución <strong>de</strong>s<strong>de</strong> la línea <strong>de</strong> órd<strong>en</strong>es:<br />

C:>matrices<br />

La línea <strong>de</strong> órd<strong>en</strong>es pue<strong>de</strong> ser una fu<strong>en</strong>te <strong>de</strong> <strong>datos</strong> al programa, así se podría pasar las dim<strong>en</strong>siones <strong>de</strong><br />

la matriz:<br />

C:>matrices 4 5<br />

Para que un programa C pueda captar <strong>datos</strong>, información <strong>en</strong> la línea <strong>de</strong> órd<strong>en</strong>es, la función main (<br />

ti<strong>en</strong>e dos argum<strong>en</strong>tos opcionales: el primero es un argum<strong>en</strong>to <strong>en</strong>tero que conti<strong>en</strong>e el número <strong>de</strong><br />

parámetros transmitidos al programa (incluy<strong>en</strong>do el mismo número <strong>de</strong> programa). El segundo<br />

argum<strong>en</strong>to conti<strong>en</strong>e los parámetros transmitidos, <strong>en</strong> forma <strong>de</strong> cad<strong>en</strong>as <strong>de</strong> caracteres; por lo que el tipo<br />

<strong>de</strong> este argum<strong>en</strong>to es un array <strong>de</strong> punteros a char. Pue<strong>de</strong> haber un tercer argum<strong>en</strong>to que conti<strong>en</strong>e las<br />

variables <strong>de</strong> <strong>en</strong>torno, <strong>de</strong>finido también como array <strong>de</strong> punteros a carácter que no se va a utilizar. Un<br />

prototipo válido <strong>de</strong> la función main ( ) :<br />

int main (int argc, char*argv [ I ) ;<br />

También pue<strong>de</strong> ser<br />

int main(int argc, char**arqv);<br />

Los nombres <strong>de</strong> los argum<strong>en</strong>tos pued<strong>en</strong> cambiarse, tradicionalm<strong>en</strong>te siempre se pone argc , argv.<br />

Ejemplo 13.16<br />

En este ejemplo se escribe un programa que muestra <strong>en</strong> pantalla los argum<strong>en</strong>tos escritos <strong>en</strong> la línea <strong>de</strong><br />

círd<strong>en</strong>es.<br />

#inclu<strong>de</strong> <br />

int main(int argc, char *arqv[l)<br />

i<br />

int i;<br />

printf ("Número <strong>de</strong> argum<strong>en</strong>tos 8d \n\n", arqc) ;<br />

printf('Argum<strong>en</strong>tos <strong>de</strong> la línea <strong>de</strong> ord<strong>en</strong>es pasados ci main:\n\n");<br />

for (i = O; i i argc; i++)<br />

printf ('I argv[%d] : %s\n\n", i, drgvlil);<br />

return O;


Entradas y salidas por archivos 433<br />

En el supuesto que el nombre <strong>de</strong>l programa ejecutable sea ARGMTOS . EXE, y que esté <strong>en</strong> la unidad<br />

<strong>de</strong> disco C:, la ejecución se realiza con esta instrucción:<br />

C:\ARGMTOS Bu<strong>en</strong>as palabras "el amigo agra<strong>de</strong>ce" 6 7 Adios.<br />

Los argum<strong>en</strong>tos se separan por un blanco. Para que el blanco forme parte <strong>de</strong>l argum<strong>en</strong>to se <strong>de</strong>be <strong>de</strong><br />

<strong>en</strong>cerrar <strong>en</strong>tre dobles comillas. La salida <strong>de</strong> la ejecución <strong>de</strong> ARGMTOS (ARGMTOS. EXE) :<br />

Numero <strong>de</strong> argum<strong>en</strong>tos 7<br />

Argum<strong>en</strong>tos <strong>de</strong> la linea <strong>de</strong> ord<strong>en</strong>es pasados a main:<br />

argv[O]: C:\ARGMTOS.EXE<br />

argv [ 11 : Bu<strong>en</strong>as<br />

argv [ 2 ] : palabras<br />

argv[3] : el amigo agra<strong>de</strong>ce<br />

argv[41: 6<br />

argv[i]: 7<br />

argv [ 61 : Adios.<br />

Ejercicio 13.8<br />

Se <strong>de</strong>sea escribir un programa para concat<strong>en</strong>ar archivos. Los nombres <strong>de</strong> los archivos han <strong>de</strong> estur <strong>en</strong><br />

la línea <strong>de</strong> órd<strong>en</strong>es, el nuevo archivo resultante <strong>de</strong> la concat<strong>en</strong>ación ha <strong>de</strong> ser el último argum<strong>en</strong>to <strong>de</strong><br />

la línea <strong>de</strong> órd<strong>en</strong>es.<br />

Análisis<br />

El número mínimo <strong>de</strong> argum<strong>en</strong>tos <strong>de</strong> la línea <strong>de</strong> Órd<strong>en</strong>es ha <strong>de</strong> ser 3, nombre <strong>de</strong>l programa ejecutable,<br />

primer archivo, segundo archivo, etc. y el archivo nuevo. El programa <strong>de</strong>be <strong>de</strong> comprobar este hecho.<br />

Para copiar un archivo se utiliza la función f get s ( ) que lee una línea <strong>de</strong>l archivo <strong>de</strong> <strong>en</strong>trada, y la<br />

función fputs ( ) que escribe la línea <strong>en</strong> el archivo <strong>de</strong> salida. En una función, copia-archivo ( ) , se<br />

realiza la operación <strong>de</strong> copia, que se llamará tantas veces como archivos <strong>de</strong> <strong>en</strong>trada se introduzcan <strong>de</strong>s<strong>de</strong><br />

la línea <strong>de</strong> órd<strong>en</strong>es.<br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine MAX-LIN 120<br />

void copia-archivo(FILE*, FILE*);<br />

int main (int argc, char *argv[l)<br />

{<br />

FILE *pfe, *pfw;<br />

int i;<br />

if (argc < 3)<br />

i<br />

puts("Error <strong>en</strong> la línea <strong>de</strong> ord<strong>en</strong>es, archivos insufici<strong>en</strong>tes.");<br />

I<br />

I<br />

return -2;<br />

/* El Último archivo es don<strong>de</strong> se realiza la concat<strong>en</strong>ación */<br />

if ((pfw = fop<strong>en</strong>(argv[argc-11 ,"w") )== NULL )<br />

{<br />

I<br />

printf ("Error al abrir el archivo %s ",argv[argc-11) ;<br />

return -3;<br />

for (i=l; i


434 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

i<br />

i<br />

printf ("Error dl abrir el ax-chivo %s ",ar.gv[i])<br />

;<br />

return -1;<br />

1<br />

copia-archivo (pfe, pfw) ;<br />

fclose (pfe);<br />

fclose (pfw) ;<br />

return O;<br />

/* E'unción copiu un richer-o <strong>en</strong> otro fichcro */<br />

/* utiliza fputs0 y fgetso */<br />

void copia_drchivo(~ILE*fl,E'ILE* f2)<br />

I<br />

char cad[MAX-LINI;<br />

}<br />

while (! feof (f1 )<br />

i<br />

fgets(cad, MAX-LIN, f1) ;<br />

if (!feof(fl)) fputs(cad, f ? ) ;<br />

1<br />

13.8. RESUMEN<br />

Este capítulo explora las operaciones fundam<strong>en</strong>tales<br />

<strong>de</strong> la biblioteca estándar <strong>de</strong> <strong>en</strong>trada y salida para el<br />

tratami<strong>en</strong>to y manejo <strong>de</strong> archivos externos.<br />

El l<strong>en</strong>guaje C, a<strong>de</strong>más <strong>de</strong> las funciones básicas <strong>de</strong><br />

US, conti<strong>en</strong>e un conjunto completo <strong>de</strong> funciones <strong>de</strong><br />

manipulación <strong>de</strong> archivos, <strong>de</strong> tipos y macros, que se<br />

<strong>en</strong>cu<strong>en</strong>tran <strong>en</strong> el archivo s tdio . h . Estas funciones<br />

se id<strong>en</strong>tifican porque empiezan todas por f <strong>de</strong> file,<br />

excepto aquellas que proced<strong>en</strong> <strong>de</strong> versiones anteriores<br />

<strong>de</strong> C. Las funciones más utilizadas:<br />

fop<strong>en</strong> () y fclose () abr<strong>en</strong> o cierran el<br />

archivo.<br />

fputc () , fgetc ( ) para acce<strong>de</strong>r ai archivo<br />

carácter a carácter(byte a byte).<br />

f put s ( ) , f ge t s ( ) para acce<strong>de</strong>r al archivo<br />

<strong>de</strong> caracteres línea a línea.<br />

fread() yfwriteo paraleeryescribirpor<br />

bloques, g<strong>en</strong>eralm<strong>en</strong>te por registros.<br />

f tell ( ) y f seek ( ) para <strong>de</strong>splazar el puntero<br />

a una posición dada (<strong>en</strong> bytes).<br />

Con estas funciones y otras que están disponibles<br />

se pue<strong>de</strong> hacer cualquier tratami<strong>en</strong>to <strong>de</strong> un archivo,<br />

modo texto o modo binario; modo secu<strong>en</strong>cia1 o modo<br />

directo.<br />

Para asociar un archivo externo con un flujo, o<br />

también podríamos <strong>de</strong>cir con el nombre interno <strong>en</strong> el<br />

programa (puntero a FILE) se utiliza f op<strong>en</strong> ( ) . La<br />

función f close ( ) termina la asociación y vuelca el<br />

buffer <strong>de</strong>l archivo; los archivos que se han manejado<br />

son todos con buffer intermedio para aum<strong>en</strong>tar la<br />

efectividad.<br />

Un archivo <strong>de</strong> texto almac<strong>en</strong>a toda la información<br />

<strong>en</strong> formato carácter. Por ejemplo, los valores numéricos<br />

se conviert<strong>en</strong> <strong>en</strong> caracteres (dígitos) que son la<br />

repres<strong>en</strong>tación numérica. Se indica el modo texto <strong>en</strong><br />

el segundo argum<strong>en</strong>to <strong>de</strong> f op<strong>en</strong> ( ) , con una t. Las<br />

funciones más usuales con los archivos <strong>de</strong> texto son<br />

fputc (), fgetc (), fputs (), fgets (),<br />

fscanf () y fprintf (1.<br />

Un archivo binario almac<strong>en</strong>a toda la información<br />

utilizando la misma repres<strong>en</strong>tación binaria que la<br />

computadora utiliza internam<strong>en</strong>te. Los archivos binarios<br />

son más efici<strong>en</strong>tes, no hay conversión <strong>en</strong>tre la<br />

repres<strong>en</strong>tación <strong>en</strong> la computadora y la repres<strong>en</strong>tación<br />

<strong>en</strong> el archivo; también ocupan m<strong>en</strong>os espacio. Sin


Entradas y salidas por archivos 435<br />

embargo son m<strong>en</strong>os transportables que los archivos<br />

<strong>de</strong> texto. Se indica el modo binario <strong>en</strong> el segundo<br />

argum<strong>en</strong>to <strong>de</strong> f op<strong>en</strong> ( ), con una b. Las funciones<br />

fputc ( ) , fgetc ( ) y sobre todo f read ( y<br />

fwrite ( ) son las que soportan <strong>en</strong>trada y salida<br />

binaria.<br />

Para proporcionar un acceso aleatorio se dispone<br />

<strong>de</strong> las funciones f tell ( ) y f seek ( ) . También<br />

hay otras funciones como f setpos ( ) y f get -<br />

pos ( 1.<br />

Con las funciones expuestas se pue<strong>de</strong> hacer todo<br />

tipo <strong>de</strong> tratami<strong>en</strong>to <strong>de</strong> archivos, sobre todo archivos<br />

con direccionami<strong>en</strong>to hash, archivos secu<strong>en</strong>ciales<br />

in<strong>de</strong>xados.<br />

13.9. EJERCICIOS<br />

13.1. Escribir las s<strong>en</strong>t<strong>en</strong>cias necesarias para abrir un<br />

archivo <strong>de</strong> caracteres cuyo nombre y acceso se<br />

introduce por teclado <strong>en</strong> modo lectura; <strong>en</strong> el<br />

caso <strong>de</strong> que el resultado <strong>de</strong> la operación sea<br />

erróneo, abrir el archivo <strong>en</strong> modo escritura.<br />

13.2. Señal<strong>en</strong> los errores <strong>de</strong>l sigui<strong>en</strong>te programa:<br />

#inclu<strong>de</strong> <br />

int main ( )<br />

t<br />

FILE* pf;<br />

pf = fop<strong>en</strong>('almac<strong>en</strong>.dat") ;<br />

1<br />

fputs ("Datos <strong>de</strong> los almac<strong>en</strong>es<br />

TIESO", pf) ;<br />

fclose (pf) ;<br />

return O;<br />

13.3. Se ti<strong>en</strong>e un archivo <strong>de</strong> caracteres <strong>de</strong> nombre<br />

"SALAS. DATO'. Escribir un programa para<br />

crear el archivo "SALAS. BIN" con el cont<strong>en</strong>ido<br />

<strong>de</strong>l primer archivo pero <strong>en</strong> modo binario.<br />

13.4. La función rewind ( ) sitúa el puntero <strong>de</strong>l<br />

archivo <strong>en</strong> el inicio <strong>de</strong>l archivo. Escribir una<br />

s<strong>en</strong>t<strong>en</strong>cia, con la función f seek ( ) que realice<br />

el mismo cometido.<br />

13.5. Utiliza los argum<strong>en</strong>tos <strong>de</strong> la función main (<br />

para dar <strong>en</strong>trada a dos cad<strong>en</strong>as; la primera<br />

repres<strong>en</strong>ta una máscara, la segunda el nombre<br />

<strong>de</strong> un archivo <strong>de</strong> caracteres. El programa ti<strong>en</strong>e<br />

que localizar las veces que ocurre la máscara<br />

<strong>en</strong> el archivo.<br />

13.6. Las funciones fgetpos ( ) y f setpos ( )<br />

<strong>de</strong>vuelv<strong>en</strong> la posición actual <strong>de</strong>l puntero<br />

<strong>de</strong>l archivo, y establec<strong>en</strong> el puntero <strong>en</strong><br />

una posición dada. Escribir las funciones<br />

pos-actual ( ) y moverjos ( ), con los<br />

prototipos:<br />

int pos-actual (FILE* pf, long* p) ;<br />

int movergos (FILE* pf, const long" p) ;<br />

La primera función <strong>de</strong>vuelve <strong>en</strong> p la posición<br />

actual <strong>de</strong>l archivo. La segunda función<br />

establece el puntero <strong>de</strong>l archivo <strong>en</strong> la posición<br />

13.7. Un archivo conti<strong>en</strong>e <strong>en</strong>teros positivos y nega-<br />

, tivos. Utiliza la función f scanf ( ) para leer<br />

el archivo y <strong>de</strong>terminar el número <strong>de</strong> <strong>en</strong>teros<br />

negativos.<br />

13.8. Un archivo <strong>de</strong> caracteres quiere escribirse <strong>en</strong> la<br />

pantalla. Escribir un programa para escribir el<br />

archivo, cuyo nombre vi<strong>en</strong>e dado <strong>en</strong> la línea <strong>de</strong><br />

órd<strong>en</strong>es, <strong>en</strong> pantalla.<br />

13.9. Escribir una función que <strong>de</strong>vuelva una cad<strong>en</strong>a<br />

<strong>de</strong> caracteres <strong>de</strong> longitud n, <strong>de</strong>l archivo cuyo<br />

puntero se pasa como argum<strong>en</strong>to. La función<br />

termina cuando se han leído los n caracteres o<br />

es fin <strong>de</strong> archivo. Utilizar la función f getc ( ) .<br />

El prototipo <strong>de</strong> la función solicitada:<br />

char* leer-cad<strong>en</strong>a (FILE* pf, int n) ;<br />

13.10. Se quiere concat<strong>en</strong>ar archivos <strong>de</strong> texto <strong>en</strong> un<br />

nuevo archivo. La separación <strong>en</strong>tre archivo y


436 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

archivo ha <strong>de</strong> ser una línea con el nombre <strong>de</strong>l<br />

archivo que se acaba <strong>de</strong> procesar. Escribir el<br />

programa correspondi<strong>en</strong>te <strong>de</strong> tal forma que<br />

los nombres <strong>de</strong> los archivos se <strong>en</strong>cu<strong>en</strong>tr<strong>en</strong> <strong>en</strong><br />

la línea <strong>de</strong> órd<strong>en</strong>es.<br />

13.11. Escribir una función que t<strong>en</strong>ga como argum<strong>en</strong>tos:<br />

un puntero <strong>de</strong> un archivo <strong>de</strong> texto, un<br />

número <strong>de</strong> línea inicial y otro número <strong>de</strong> línea<br />

final. La función <strong>de</strong>be <strong>de</strong> mostrar las líneas<br />

<strong>de</strong>l archivo compr<strong>en</strong>didas <strong>en</strong>tre los límites<br />

indicados.<br />

13.12. Escribir un programa que escriba por pantalla<br />

las líneas <strong>de</strong> texto <strong>de</strong> un archivo, numerando<br />

cada línea <strong>de</strong>l mismo.<br />

13.10. PROBLEMAS<br />

13.1. Escribir un programa que compare dos archivos<br />

<strong>de</strong> texto. El programa ha <strong>de</strong> mostrar las<br />

difer<strong>en</strong>cias <strong>en</strong>tre el primer archivo y el segundo,<br />

precedidas <strong>de</strong>l número <strong>de</strong> línea y <strong>de</strong> columna.<br />

13.2. Un atleta utiliza un pulsómetro para sus <strong>en</strong>tr<strong>en</strong>ami<strong>en</strong>tos.<br />

El pulsómetro almac<strong>en</strong>a las pulsaciones<br />

cada 15 segundos, durante un tiempo<br />

máximo <strong>de</strong> 2 horas. Escribir un programa para<br />

almac<strong>en</strong>ar <strong>en</strong> un archivo los <strong>datos</strong> <strong>de</strong>l pulsómetro<br />

<strong>de</strong>l atleta, <strong>de</strong> tal forma que el primer<br />

registro cont<strong>en</strong>ga la fecha, hora y tiempo <strong>en</strong><br />

minutos <strong>de</strong> <strong>en</strong>tr<strong>en</strong>ami<strong>en</strong>to, a continuación los<br />

<strong>datos</strong> <strong>de</strong>l pulsómetro por parejas: tiempo, pulsaciones.<br />

133. Se <strong>de</strong>sea obt<strong>en</strong>er una estadística <strong>de</strong> un archivo<br />

<strong>de</strong> caracteres. Escribir un programa para contar<br />

el número <strong>de</strong> palabras <strong>de</strong> que consta un<br />

archivo, así como una estadística <strong>de</strong> cada longitud<br />

<strong>de</strong> palabra.<br />

13.4. En un archivo binario se <strong>en</strong>cu<strong>en</strong>tran pares <strong>de</strong><br />

valores que repres<strong>en</strong>tan la int<strong>en</strong>sidad <strong>en</strong><br />

miliamperios y el correspondi<strong>en</strong>te voltaje <strong>en</strong><br />

voltios para un diodo. Por ejemplo:<br />

0.5 0.35<br />

1.0 0.45<br />

2.0 0.55<br />

2.5 0.58<br />

. . .<br />

Nuestro problema es que dado un valor <strong>de</strong>l<br />

voltaje v, compr<strong>en</strong>dido <strong>en</strong>tre el mínimo valor<br />

y el máximo <strong>en</strong>contrar el correspondi<strong>en</strong>te valor<br />

<strong>de</strong> la int<strong>en</strong>sidad. Para ello el programa <strong>de</strong>be<br />

leer el archivo, formar una tabla y aplicar un<br />

mdtodo <strong>de</strong> interpolación, por ejemplo el método<br />

<strong>de</strong> polinomios <strong>de</strong> Lagrange. Una vez calculada<br />

la int<strong>en</strong>sidad, el programa <strong>de</strong>be <strong>de</strong> escribir<br />

e1 par <strong>de</strong> valores <strong>en</strong> el archivo.<br />

13.5. Un profesor ti<strong>en</strong>e 30 estudiantes y cada estudiante<br />

ti<strong>en</strong>e tres calificaciones <strong>en</strong> el primer<br />

parcial. Almac<strong>en</strong>ar los <strong>datos</strong> <strong>en</strong> un archivo,<br />

<strong>de</strong>jando espacio para dos notas más y la nota<br />

final. Incluir un m<strong>en</strong>lí <strong>de</strong> opciones, para añadir<br />

más estudiantes, visualizar <strong>datos</strong> <strong>de</strong> un estudiante,<br />

introducir nuevas notas y calcular nota<br />

final.<br />

13.6. Se <strong>de</strong>sea escribir una carta <strong>de</strong> felicitación navi<strong>de</strong>ña<br />

a los empleados <strong>de</strong> un c<strong>en</strong>tro sanitario. El<br />

texto <strong>de</strong> la carta se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el archivo<br />

CARTA. TXT . El nombre y dirección <strong>de</strong> los<br />

empleados se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el archivo binario<br />

EMPLA . DAT, como una secu<strong>en</strong>cia <strong>de</strong> registros<br />

con los campos nombre, dirección, etc. Escribir<br />

un programa que g<strong>en</strong>ere un archivo <strong>de</strong> texto<br />

por cada empleado, la primera línea conti<strong>en</strong><strong>en</strong><br />

el nombre, la segunda está <strong>en</strong> blanco, la<br />

tercera la dirección y <strong>en</strong> la quinta empieza el<br />

texto CARTA. TXT.


Entradas y salidas por archivos 437<br />

13.7. Se <strong>de</strong>sea crear un archivo binario formado por<br />

registros que repres<strong>en</strong>tan productos <strong>de</strong> perfumería.<br />

Los campos <strong>de</strong> cada registro son código<br />

<strong>de</strong> producto, <strong>de</strong>scripción, precio y número <strong>de</strong><br />

unida<strong>de</strong>s. La dirección <strong>de</strong> cada registro vi<strong>en</strong>e<br />

dada por una función hash que toma como<br />

campo clave el código <strong>de</strong>l producto(tres dígitos):<br />

hash(c1ave) = (clave modulo97) +1<br />

El número máximo <strong>de</strong> productos distintos es<br />

100. Las colisiones, <strong>de</strong> producirse, se situarán<br />

secu<strong>en</strong>cialm<strong>en</strong>te a partir <strong>de</strong>l registro número<br />

120.<br />

13.8. Escribir un programa para listar el cont<strong>en</strong>ido<br />

<strong>de</strong> un <strong>de</strong>terminado subdirectorio, pasado<br />

como parámetro a la función main ( ) .<br />

13.9. Modificar el Problema 13.2 para añadir un<br />

m<strong>en</strong>ú con opciones <strong>de</strong> añadir al archivo nuevos<br />

<strong>en</strong>tr<strong>en</strong>ami<strong>en</strong>tos, obt<strong>en</strong>er el tiempo que se<br />

está por <strong>en</strong>cima <strong>de</strong>l umbral aeróbico (data<br />

pedido por teclado) para un día <strong>de</strong>terminado<br />

y media <strong>de</strong> las pulsaciones.<br />

13.10. Un archivo <strong>de</strong> texto consta <strong>en</strong> cada línea <strong>de</strong><br />

dos cad<strong>en</strong>as <strong>de</strong> <strong>en</strong>teros separadas por el operador<br />

+, o -, . Se <strong>de</strong>sea formar un archivo<br />

binario con los resultados <strong>de</strong> la operación que<br />

se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el archivo <strong>de</strong> texto.


CAPíTULO 14<br />

LISTAS ENLAZADAS<br />

CONTENIDO<br />

14.1. Fundam<strong>en</strong>tos teóricos.<br />

14.2. Clasificación <strong>de</strong> las listas<br />

<strong>en</strong>lazadas.<br />

14.3. Operaciones <strong>en</strong> listas <strong>en</strong>lazadas.<br />

14.4. Listas doblem<strong>en</strong>te <strong>en</strong>lazadas.<br />

14.5. Ljstaa circulares.<br />

14.6. Resum<strong>en</strong>.<br />

14.7. Ejercicios.<br />

14.8. Problemas.<br />

438


INTRODUCCI~N<br />

En este capítulo se comi<strong>en</strong>za el estudio <strong>de</strong> las <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> dinámicas.<br />

Al contrario que las <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> estáticas (arrays-listas, vectores y<br />

tablas- y <strong>estructura</strong>s) <strong>en</strong> las que su tamaño <strong>en</strong> memoria se establece durante<br />

la compilación y permanece inalterable durante la ejecución <strong>de</strong>l programa, las<br />

<strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> dinámicas crec<strong>en</strong> y se contra<strong>en</strong> a medida que se ejecuta el<br />

programa.<br />

La <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> que se estudiará <strong>en</strong> este capítulo es la lista <strong>en</strong>lazada<br />

(ligada o <strong>en</strong>cad<strong>en</strong>ada, ((linked List))) que es una colección <strong>de</strong> elem<strong>en</strong>tos (d<strong>en</strong>ominados<br />

nodos) dispuestos uno a continuación <strong>de</strong> otro, cada uno <strong>de</strong> ellos conectado<br />

al sigui<strong>en</strong>te elem<strong>en</strong>to por un ((<strong>en</strong>lace» o ((puntero)). Las listas <strong>en</strong>lazadas<br />

son <strong>estructura</strong>s muy flexibles y con numerosas aplicaciones <strong>en</strong> el mundo <strong>de</strong> la<br />

programación.<br />

CONCEPTOS CLAVE<br />

Búsqueda <strong>de</strong> un nodo <strong>en</strong> una<br />

lista <strong>en</strong>lazada.<br />

Lista doblem<strong>en</strong>te <strong>en</strong>lazada.<br />

Estructura <strong>de</strong> una lista <strong>en</strong>lazada.<br />

Operaciones <strong>en</strong> listas <strong>en</strong>lazadas.<br />

Eliminación <strong>de</strong> nodos <strong>en</strong> una<br />

lista <strong>en</strong>lazada.<br />

Recorrido <strong>de</strong> una lista.<br />

Fundam<strong>en</strong>tos teóricos <strong>de</strong> listas<br />

<strong>en</strong>lazadas.<br />

Variables puntero y variables<br />

apuntadas.<br />

Lista circular.<br />

439


440 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

14.1. FUNDAMENTOS TEÓRICOS<br />

En capítulos anteriores se han estudiado <strong>estructura</strong>s lineales <strong>de</strong> elem<strong>en</strong>tos homogéneos (listas, tablas,<br />

vectores) y se utilizaban arrays para implem<strong>en</strong>tar tales <strong>estructura</strong>s. Esta técnica obliga a fijar por<br />

a<strong>de</strong>lantado el espacio a ocupar <strong>en</strong> memoria, <strong>de</strong> modo que cuando se <strong>de</strong>sea añadir un nuevo elem<strong>en</strong>to que<br />

rebase el tamaño prefijado <strong>de</strong>l array, no es posible realizar la operación sin que se produzca un error <strong>en</strong><br />

tiempo <strong>de</strong> ejecución. Ello se <strong>de</strong>be a que los arrays hac<strong>en</strong> un uso inefici<strong>en</strong>te <strong>de</strong> la memoria. Gracias a la<br />

asignación dinámica <strong>de</strong> variables, se pued<strong>en</strong> implem<strong>en</strong>tar listas <strong>de</strong> modo que la memoria física utilizada<br />

se corresponda con el número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong> la tabla. Para ello se recurre a los punteros (apuntadores)<br />

que hac<strong>en</strong> un uso más efici<strong>en</strong>te <strong>de</strong> la memoria como ya se ha visto con anterioridad.<br />

Una lista <strong>en</strong>lazada es una colección o secu<strong>en</strong>cia <strong>de</strong> elem<strong>en</strong>tos dispuestos uno <strong>de</strong>trás <strong>de</strong> otro, <strong>en</strong> la<br />

que cada elem<strong>en</strong>to se conecta al sigui<strong>en</strong>te elem<strong>en</strong>to por un «<strong>en</strong>lace» o «puntero». La i<strong>de</strong>a básica consiste<br />

<strong>en</strong> construir una lista cuyos elem<strong>en</strong>tos llamados nodos se compon<strong>en</strong> <strong>de</strong> dos partes o campos: la primera<br />

parte o campo conti<strong>en</strong>e la información y es, por consigui<strong>en</strong>te, un valor <strong>de</strong> un tipo g<strong>en</strong>érico (d<strong>en</strong>ominado<br />

Dato, TipoElem<strong>en</strong>to, Znfo, etc.) y la segunda parte o campo es un puntero (d<strong>en</strong>ominado <strong>en</strong>lace o sgte)<br />

que apunta al sigui<strong>en</strong>te elem<strong>en</strong>to <strong>de</strong> la lista.<br />

pz-pwrir<br />

puntero<br />

puntero<br />

Figura 14.1. Lista <strong>en</strong>lazada (repres<strong>en</strong>tación simple).<br />

,<br />

I<br />

La repres<strong>en</strong>tación gráfica más ext<strong>en</strong>dida es aquella que utiliza una caja (un rectángulo) con dos<br />

secciones <strong>en</strong> su interior. En la primera sección se escribe el elem<strong>en</strong>to o valor <strong>de</strong>l dato y <strong>en</strong> la segunda<br />

sección, el <strong>en</strong>lace o puntero mediante una flecha que sale <strong>de</strong> la caja y apunta al nodo sigui<strong>en</strong>te.<br />

e,<br />

e2<br />

Una lista <strong>en</strong>lazada consta <strong>de</strong> un número <strong>de</strong> elem<strong>en</strong>tos y cada elem<strong>en</strong>to ti<strong>en</strong>e dos compon<strong>en</strong>tes<br />

(campos), un puntero ai sigui<strong>en</strong>te elem<strong>en</strong>to <strong>de</strong> la lista y un valor, que pue<strong>de</strong> ser <strong>de</strong> cualquier<br />

tipo.<br />

Los <strong>en</strong>laces se repres<strong>en</strong>tan por flechas para facilitar la compr<strong>en</strong>sión <strong>de</strong> la conexión <strong>en</strong>tre dos nodos;<br />

ello indica que el <strong>en</strong>lace ti<strong>en</strong>e la dirección <strong>en</strong> memoria <strong>de</strong>l sigui<strong>en</strong>te nodo. Los <strong>en</strong>laces también sitúan<br />

los nodos <strong>en</strong> una secu<strong>en</strong>cia. En la Figura 14.2 los nodos forman una secu<strong>en</strong>cia <strong>de</strong>s<strong>de</strong> el primer elem<strong>en</strong>to<br />

(e,) al último elem<strong>en</strong>to (erz). El primer nodo se <strong>en</strong>laza al segundo nodo, el segundo nodo se <strong>en</strong>laza al<br />

tercero y así sucesivam<strong>en</strong>te hasta llegar al último nodo. El nodo último ha <strong>de</strong> ser repres<strong>en</strong>tado <strong>de</strong> forma<br />

difer<strong>en</strong>te para significar que este nodo no se <strong>en</strong>laza a ningún otro. La Figura 14.3 muestra difer<strong>en</strong>tes<br />

repres<strong>en</strong>taciones gráficas que se utilizan para dibujar el campo <strong>en</strong>lace <strong>de</strong>l último nodo.


Listas <strong>en</strong>lazadas 441<br />

Figura 14.3. Difer<strong>en</strong>tes repres<strong>en</strong>taciones gráficas <strong>de</strong>l nodo Último<br />

14.2. CLASIFICACIÓN DE LAS LISTAS ENLAZADAS<br />

Las listas se pued<strong>en</strong> dividir <strong>en</strong> cuatro categorías :<br />

Listas simplem<strong>en</strong>te <strong>en</strong>lazadas. Cada nodo (elem<strong>en</strong>to) conti<strong>en</strong>e un único <strong>en</strong>lace que conecta ese<br />

nodo al nodo sigui<strong>en</strong>te o nodo sucesor. La lista es efici<strong>en</strong>te <strong>en</strong> recorridos directos ((


442 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

El primer nodo, fr<strong>en</strong>te, es el nodo apuntado por cabeza. La lista <strong>en</strong>cad<strong>en</strong>a nodos juntos <strong>de</strong>s<strong>de</strong> el<br />

fr<strong>en</strong>te al final (cola) <strong>de</strong> la lista. El final se id<strong>en</strong>tifica como el nodo cuyo campo puntero ti<strong>en</strong>e el valor<br />

NULL = O. La lista se recorre <strong>de</strong>s<strong>de</strong> el primero al Último nodo; <strong>en</strong> cualquier punto <strong>de</strong>l recorrido la<br />

posición actual se refer<strong>en</strong>cia por el puntero Ptr-actual. En el caso <strong>en</strong> que la lista no conti<strong>en</strong>e ningún<br />

nodo (está vacía), el puntero cabeza es nulo.<br />

cabeza -,<br />

14.3. OPERACIONES EN LISTAS ENLAZADAS<br />

Una lista <strong>en</strong>lazada requiere unos controles para la gestión <strong>de</strong> los elem<strong>en</strong>tos cont<strong>en</strong>idos <strong>en</strong> ellas. Estos<br />

controles se manifiestan <strong>en</strong> forma <strong>de</strong> operaciones que t<strong>en</strong>drán las sigui<strong>en</strong>tes funciones:<br />

Declaración <strong>de</strong> los tipos nodo y puntero a nodo.<br />

Iniciulización o creación.<br />

Insertar elem<strong>en</strong>tos <strong>en</strong> una lista.<br />

Eliminar elem<strong>en</strong>tos <strong>de</strong> una lista.<br />

Buscar elem<strong>en</strong>tos <strong>de</strong> una lisfa (comprobar la exist<strong>en</strong>cia <strong>de</strong> elem<strong>en</strong>tos <strong>en</strong> una lista).<br />

Recorrer una lista <strong>en</strong>lazada (visitar cada nodo <strong>de</strong> la lista).<br />

Comprobar si la lista está vacía.<br />

14.3.1. Declaración <strong>de</strong> un nodo<br />

Una lista <strong>en</strong>lazada se compone <strong>de</strong> una serie <strong>de</strong> nodos <strong>en</strong>lazados mediante punteros. Cada nodo es una<br />

combinación <strong>de</strong> dos partes: un tipo <strong>de</strong> dato (<strong>en</strong>tero, real, doble, carácter o tipo pre<strong>de</strong>finido) y un <strong>en</strong>lace<br />

(puntero) al sigui<strong>en</strong>te nodo. En C, se pue<strong>de</strong> <strong>de</strong>clarar un nuevo tipo <strong>de</strong> dato por un nodo mediante las<br />

palabras reservadas s t ruc t que conti<strong>en</strong>e las dos partes citadas.<br />

struct Nodo<br />

1<br />

int dato;<br />

struct Nodo* <strong>en</strong>lace;<br />

I;<br />

type<strong>de</strong>f struct Nodo<br />

i<br />

int dato;<br />

struct Nodo "<strong>en</strong>lace;<br />

1 NODO ;<br />

La <strong>de</strong>claración utiliza el tipo struct que permite agrupar campos <strong>de</strong> difer<strong>en</strong>tes tipos, el campo<br />

dato y el campo <strong>en</strong>lace. Con type<strong>de</strong>f se pue<strong>de</strong> <strong>de</strong>clarar a la vez un nuevo id<strong>en</strong>tificador <strong>de</strong> struct<br />

Nodo, <strong>en</strong> el caso anterior se ha elegido NODO.<br />

Dado que los tipos <strong>de</strong> <strong>datos</strong> que se pue<strong>de</strong> incluir <strong>en</strong> una lista pued<strong>en</strong> ser <strong>de</strong> cualquier tipo (<strong>en</strong>teros,<br />

dobles, caracteres o incluso cad<strong>en</strong>as), con el objeto <strong>de</strong> que el tipo <strong>de</strong> dato <strong>de</strong> cada nodo se pueda cambiar<br />

con facilidad, se suele utilizar una s<strong>en</strong>t<strong>en</strong>cia Lypedcf para <strong>de</strong>clarar el nombre <strong>de</strong> Elem<strong>en</strong>to como un<br />

sinónimo <strong>de</strong>l tipo <strong>de</strong> dato <strong>de</strong> cada campo. El tipo Elern<strong>en</strong>to se utiliza <strong>en</strong>tonces d<strong>en</strong>tro <strong>de</strong> la <strong>estructura</strong><br />

nodo, como se muestra a continuación:<br />

type<strong>de</strong>f double Elem<strong>en</strong>to;<br />

sLruct nodo<br />

i<br />

Elem<strong>en</strong>to dato;


Listas <strong>en</strong>lazadas 443<br />

ctruct nodo *<strong>en</strong>lace;<br />

1;<br />

Entonces, si se necesita cambiar el tipo <strong>de</strong> elem<strong>en</strong>to <strong>en</strong> los nodos, sólo t<strong>en</strong>drá que cambiar la<br />

s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> <strong>de</strong>claración <strong>de</strong> tipos que afecta a ~1 em<strong>en</strong>to. Siempre que una función necesite referirse al<br />

tipo <strong>de</strong>l dato <strong>de</strong>l nodo, pue<strong>de</strong> utilizar el nombre t.,l ern<strong>en</strong>to.<br />

Ejemplo 14.1<br />

En este ejemplo se <strong>de</strong>clara un tipo d<strong>en</strong>ominado P~JNTO que repres<strong>en</strong>ta un punto <strong>en</strong> el plano con su<br />

coord<strong>en</strong>ada x e y. También se <strong>de</strong>clara el tipo NODO con el campo dato <strong>de</strong>l tipo PUNTO. Por Último, se<br />

<strong>de</strong>fine un puntero a NODO.<br />

#inclu<strong>de</strong> <br />

type<strong>de</strong>f struct punto<br />

float x, y;<br />

} PUNTO;<br />

type<strong>de</strong>f struct Nodo<br />

i<br />

PUNTO dato;<br />

struct Nodo* <strong>en</strong>lace;<br />

1 NODO;<br />

NODO* cabecera;<br />

cabecera = NULL;<br />

14.3.2. Puntero <strong>de</strong> cabecera y cola<br />

Normalm<strong>en</strong>te, los programas no <strong>de</strong>claran realm<strong>en</strong>te variables <strong>de</strong> nodos. En su lugar, cuando se construye<br />

y manipula una lista <strong>en</strong>lazada, a la lista se acce<strong>de</strong> a través <strong>de</strong> uno o más punfevos a los nodos. El acceso<br />

más frecu<strong>en</strong>te a una lista <strong>en</strong>lazada es a través <strong>de</strong>l primer nodo <strong>de</strong> la lista que se llama cabeza o cabecera<br />

<strong>de</strong> la lista. Un puntero al primer nodo se llama puntero cabeza. En ocasiones, se manti<strong>en</strong>e también un<br />

puntero al último nodo <strong>de</strong> una lista <strong>en</strong>lazada. El último nodo es la cola <strong>de</strong> la lista, y un puntero al último<br />

nodo es el puntero cola. También se pued<strong>en</strong> mant<strong>en</strong>er punteros otros nodos <strong>de</strong> una lista <strong>en</strong>lazada.<br />

0 23.5 40.7<br />

Declaración <strong>de</strong>l nodo<br />

Definición <strong>de</strong> punteros<br />

1 ;<br />

( e'rrii rit o tLi t o ; t 1 1 1 t ~ r oric *pi I -C (11 < I ;<br />

i t I ic t :iodo *i ri I ;<br />

Figura 14.4. Declaraciones <strong>de</strong> tipo <strong>en</strong> lista <strong>en</strong>lazada


444 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Cada puntero a un nodo <strong>de</strong>be ser <strong>de</strong>clarado como una variable puntero. Por ejemplo, si se manti<strong>en</strong>e<br />

una lista <strong>en</strong>lazada con un puntero <strong>de</strong> cabecera y otro <strong>de</strong> cola, se <strong>de</strong>b<strong>en</strong> <strong>de</strong>clarar dos variables puntero:<br />

struct nodo *ptr -cabeza;<br />

struct nodo *ptr-cola;<br />

El tipo struct nodo a veces se simplifica utilizando la <strong>de</strong>claración type<strong>de</strong>f . Así po<strong>de</strong>mos escribir:<br />

type<strong>de</strong>f struct nodo NODO;<br />

type<strong>de</strong>f struct nodo* ptrnodo;<br />

ptrnodo ptr-cabeya;<br />

ptrnodo ptr-cola;<br />

La construcción y manipulación <strong>de</strong> una lista <strong>en</strong>lazada requiere el acceso a los nodos <strong>de</strong> la<br />

lista a través <strong>de</strong> uno o más punteros a nodos. Normalm<strong>en</strong>te, un programa incluye un puntero<br />

ai primer nodo (cabeza) y un puntero al Último nodo (cola).<br />

En cualquier forma, el último elem<strong>en</strong>to <strong>de</strong> la lista conti<strong>en</strong>e un valor <strong>de</strong> O, esto es, un puntero<br />

nulo (NULL) que señala el final <strong>de</strong> la lista.<br />

14.3.3. El puntero nulo<br />

La Figura 14.4 muestra una lista con un puntero cabeza y un puntero nulo al final <strong>de</strong> la lista sobre el que<br />

se ha escrito la palabra NULL. La palabra NULI, repres<strong>en</strong>ta el puntero nulo, que es una constante<br />

especial <strong>de</strong> C. Se pue<strong>de</strong> utilizar el puntero nulo para cualquier valor <strong>de</strong> puntero que no apunte a ningún<br />

sitio. El puntero nulo se utiliza, normalm<strong>en</strong>te, <strong>en</strong> dos situaciones:<br />

Usar el puntero nulo <strong>en</strong> el campo <strong>en</strong>lace o sigui<strong>en</strong>te <strong>de</strong>l nodo final <strong>de</strong> una lista <strong>en</strong>lazada.<br />

O Cuando una lista <strong>en</strong>lazada no ti<strong>en</strong>e ningún nodo, se utiliza el puntero NULL como puntero <strong>de</strong><br />

cabeza y <strong>de</strong> cola. Tal lista se d<strong>en</strong>omina lista vacía.<br />

En un programa, el puntero nulo se pue<strong>de</strong> escribir coino NULL, que es una constante <strong>de</strong> la biblioteca<br />

estándar stdlib. h'. El puntero nulo se pue<strong>de</strong> asignar a una variable puntero con una s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong><br />

asignación ordinaria. Por ejemplo:<br />

p? r -c dk>c,/a<br />

Figura 14.5. Puntero NIJLL<br />

El puntero <strong>de</strong> cabeza y <strong>de</strong> cola <strong>en</strong> una lista <strong>en</strong>lazada pue<strong>de</strong> ser NULL, lo que indicará que la lista<br />

es vacía (no ti<strong>en</strong>e nodos). Éste suele ser un método usual para construir una lista. Cualquier<br />

función que se escribe para manipular listas <strong>en</strong>lazadas <strong>de</strong>be po<strong>de</strong>r manejar un puntero <strong>de</strong><br />

cabeza y un puntero <strong>de</strong> cola nulos.<br />

' A ~eces algunos programadores escrib<strong>en</strong> el puntero nulo coiiio O, pero p<strong>en</strong>sarnos es un estilo i d s claro escribirlo como birri,L


Listas <strong>en</strong>lazadas 445<br />

14.3.4. El operador -> <strong>de</strong> selección <strong>de</strong> un miembro<br />

Si p es un puntero a una <strong>estructura</strong> y m es un miembro <strong>de</strong> la <strong>estructura</strong>, <strong>en</strong>tonces p -> m acce<strong>de</strong> al<br />

miembro m <strong>de</strong> la <strong>estructura</strong> apuntada por P.<br />

El símbolo "->" se consi<strong>de</strong>ra como un operador simple (<strong>en</strong> vez <strong>de</strong> compuesto, al constar <strong>de</strong> dos<br />

símbolos in<strong>de</strong>p<strong>en</strong>di<strong>en</strong>tes "-" y ">". Se d<strong>en</strong>omina operudor <strong>de</strong> seleccicín <strong>de</strong> miembro o también operador<br />

<strong>de</strong> selección <strong>de</strong> compon<strong>en</strong>te. De modo visual el operador P -> m recuerda a una flecha que apunta <strong>de</strong>l<br />

puntero p al objeto que conti<strong>en</strong>e al miembro m.<br />

Suponi<strong>en</strong>do que un programa ha <strong>de</strong> construir una lista <strong>en</strong>lazada y crear un puntero <strong>de</strong> cabecera<br />

ptr-cabeza a un nodo Nodo, el operador * <strong>de</strong> indirección aplicado a una variable puntero repres<strong>en</strong>ta<br />

el cont<strong>en</strong>ido <strong>de</strong>l nodo apuntado por ptr-cabeza. Es <strong>de</strong>cir, *pt r-cabeza es un tipo <strong>de</strong> dato ~odo.<br />

Al igual que con cualquier objeto, se pue<strong>de</strong> acce<strong>de</strong>r a los dos miembros <strong>de</strong> *ptr-cabeza <strong>en</strong> la<br />

Figura 14.5. Por ejemplo, la s<strong>en</strong>t<strong>en</strong>cia sigui<strong>en</strong>te escribe los <strong>datos</strong> <strong>de</strong>l nodo cabecera.<br />

pr intf ("%lf",(*ptr-cdbezd).dato);<br />

(*ptr-cabeza) miembro dato <strong>de</strong>l nodo dpuntddo por ptr-cabeza<br />

Precaución<br />

Los paréntesis son necesarios alre<strong>de</strong>dor <strong>de</strong> la primera parte <strong>de</strong> la expresión ( "ptr-cabeza) ya<br />

que los operadores unitarios que aparec<strong>en</strong> a la <strong>de</strong>recha ti<strong>en</strong><strong>en</strong> prioridad más alta que los operadores<br />

unitarios que aparec<strong>en</strong> <strong>en</strong> el lado izquierdo (el asterisco <strong>de</strong> indirección).<br />

Sin los paréntesis, el significado <strong>de</strong> pt r-cabe La producirá un error <strong>de</strong> sintaxis, al int<strong>en</strong>tar evaluar<br />

ptr-cabeza. dato antes <strong>de</strong> la indirección o <strong>de</strong>srefer<strong>en</strong>cia.<br />

I P -> m significa lo mismo que ( *p). m I<br />

Utilizando el operador <strong>de</strong> selección -> se pued<strong>en</strong> imprimir los <strong>datos</strong> <strong>de</strong>l primer nodo <strong>de</strong> la lista<br />

printf ("%lf", ptr-cabeza->dato) ;<br />

Error<br />

Uno <strong>de</strong> los errores típicos <strong>en</strong> el tratami<strong>en</strong>to <strong>de</strong> punteros es escribir la expresión *p o bi<strong>en</strong> p-><br />

cuando el valor <strong>de</strong>l puntero p es el puntero nulo, ya que como se sabe el puntero nulo no apunta<br />

a nada.<br />

14.3.5. Construcción <strong>de</strong> una lista<br />

Un algoritmo para la creación <strong>de</strong> una lista <strong>en</strong>lazada <strong>en</strong>traña los sigui<strong>en</strong>tes pasos:<br />

Paso I. Declarar el tipo <strong>de</strong> dato y el puntero <strong>de</strong> cabeLa o primero.<br />

Puso 2. Asignar memoria para un elem<strong>en</strong>to <strong>de</strong>l tipo <strong>de</strong>finido anteriorm<strong>en</strong>te utilizando alguna <strong>de</strong><br />

las funciones <strong>de</strong> asignación <strong>de</strong> memoria (mdiloc (), calioc (), realioc ()) y un cast<br />

para la conversión <strong>de</strong> void* al tipo puntero a nodo; la dirección <strong>de</strong>l nuevo elem<strong>en</strong>to es<br />

pt r-nuevo.


446 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Paso 3.<br />

Puso 4.<br />

Crear iterativam<strong>en</strong>te el primer elem<strong>en</strong>to (cabeza) y los elem<strong>en</strong>tos sucesivos <strong>de</strong> una lista<br />

<strong>en</strong>lazada simplem<strong>en</strong>te.<br />

Repetir hasta que no haya más <strong>en</strong>trada para el elem<strong>en</strong>to.<br />

Ejemplo 14.2<br />

Crear una listu <strong>en</strong>lazada <strong>de</strong> elem<strong>en</strong>tos que almuccw<strong>en</strong> <strong>datos</strong> <strong>de</strong> tipo <strong>en</strong>tero.<br />

Un elem<strong>en</strong>to <strong>de</strong> la lista se pue<strong>de</strong> <strong>de</strong>finir con la ayuda <strong>de</strong> la <strong>estructura</strong> sigui<strong>en</strong>te:<br />

struct Elem<strong>en</strong>to<br />

i<br />

int dato;<br />

struct Elem<strong>en</strong>to * sigui<strong>en</strong>te;<br />

I;<br />

type<strong>de</strong>f struct Elem<strong>en</strong>to Nodo;<br />

En la <strong>estructura</strong> Elem<strong>en</strong>to hay dos miembros, dato y si.gui<strong>en</strong>te que es un puntero al sigui<strong>en</strong>te<br />

nodo y dato que conti<strong>en</strong>e el valor <strong>de</strong>l elem<strong>en</strong>to <strong>de</strong> la lista. También se <strong>de</strong>clara un nuevo tipo: Nodo<br />

que es sinónimo <strong>de</strong> struct Elem<strong>en</strong>to. El sigui<strong>en</strong>te paso para construir la lista es <strong>de</strong>clarar la variable<br />

Primero que apuntará al primer elem<strong>en</strong>to <strong>de</strong> la lista:<br />

Nodo "Primero = NULL /* o bi<strong>en</strong> = O */<br />

1<br />

Sib,'<br />

El puntero Primero (también se pue<strong>de</strong> llamar Cabeza) se ha inicializado a un valor nulo, lo que<br />

implica que la lista está vacía (no ti<strong>en</strong>e elem<strong>en</strong>tos). Ahora se crea un elem<strong>en</strong>to <strong>de</strong> la lista, para ello hay<br />

que reservar memoria, tanta como tamaño ti<strong>en</strong>e cada nodo, y asignar la dirección <strong>de</strong> la memoria<br />

reservada al puntero Primero:<br />

Primero = (Nodo*)malloc(sizeof(Nodo));<br />

Con el operador sizeoí se obti<strong>en</strong>e el tamaño <strong>de</strong> cada nodo <strong>de</strong> la lista, la función maiioc ( )<br />

<strong>de</strong>vuelve un puntero g<strong>en</strong>érico (voi a*), por lo que se convierte a Nodo*. Ahora se pue<strong>de</strong> asignar un<br />

valor al campo dato:<br />

Primero -> dato = 11;<br />

Primero -> sigui<strong>en</strong>t.e = NLJT,L,;<br />

Primero<br />

El puntero Primero apunta al nuevo elem<strong>en</strong>to, que se inicializa a I I. El campo sigui<strong>en</strong>te <strong>de</strong>l<br />

nuevo elem<strong>en</strong>to toma el valor nulo, por no haber un nodo sigui<strong>en</strong>te. La operación <strong>de</strong> crear un nodo se<br />

pue<strong>de</strong> hacer <strong>en</strong> una función a la que se pasa el valor <strong>de</strong>l campo dato y <strong>de</strong>l campo sigui<strong>en</strong>te. La<br />

función <strong>de</strong>vuelve un puntero al nodo creado:<br />

Nodo* Crearnodo(1nt x, Nodo* <strong>en</strong>lace)<br />

{<br />

Nodo *p;<br />

p = (Nodo*)malloc(sizeof(Nodo));<br />

p->dato = x;<br />

p->siqui<strong>en</strong>te = <strong>en</strong>lace;<br />

return p;


-<br />

Listas <strong>en</strong>lazadas 447<br />

La lainada a la función Crearnodo ( ) para crear el primer nodo <strong>de</strong> la lista:<br />

Primero = Crearnodo(l1, NULL);<br />

Si ahora se <strong>de</strong>sea añadir un nuevo elem<strong>en</strong>to con un valor 6, y situarlo <strong>en</strong> el primer lugar <strong>de</strong> la lista se<br />

escribe simplem<strong>en</strong>te:<br />

Primero = Crearnodo(6,Primero);<br />

e 6<br />

e 11 NI J L 1 I ,<br />

Por Último para obt<strong>en</strong>er una lista compuesta <strong>de</strong> 4 , 6, 11 se habría <strong>de</strong> ejecutar:<br />

Primero = Crearnodo(4,Primero);<br />

14.3.6. Insertar un elem<strong>en</strong>to <strong>en</strong> una lista<br />

El algoritmo empleado para añadir o insertar un elern<strong>en</strong>to <strong>en</strong> una lista <strong>en</strong>lazada varía <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> la<br />

posición <strong>en</strong> que se <strong>de</strong>sea insertar el elem<strong>en</strong>to. La posición <strong>de</strong> inserción pue<strong>de</strong> ser:<br />

O En la cabeza (elem<strong>en</strong>to primero) <strong>de</strong> la lista.<br />

O En el final <strong>de</strong> la lista (elem<strong>en</strong>to último).<br />

O Antes <strong>de</strong> un elem<strong>en</strong>to especificado.<br />

O Después <strong>de</strong> un elem<strong>en</strong>to especificado.<br />

Insertar un nuevo elem<strong>en</strong>to <strong>en</strong> la cabeza <strong>de</strong> una lista<br />

Aunque normalm<strong>en</strong>te se insertan nuevos <strong>datos</strong> al final <strong>de</strong> una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong>, es más fácil y más<br />

efici<strong>en</strong>te insertar un elem<strong>en</strong>to nuevo <strong>en</strong> la cabeza <strong>de</strong> una lista. El proceso <strong>de</strong> inserción se pue<strong>de</strong> resumir<br />

<strong>en</strong> este algoritmo:<br />

1. Asignar un nuevo nodo apuntado por nuevo que es una variable puntero local que apunta al<br />

nuevo nodo que se va a insertar <strong>en</strong> la lista.<br />

2. Situar el nuevo elem<strong>en</strong>to <strong>en</strong> el campo dato (Info) <strong>de</strong>l nuevo nodo.<br />

3. Hacer que el campo <strong>en</strong>lace sigui<strong>en</strong>te <strong>de</strong>l nuevo nodo apunte a la cabeza (primer nodo) <strong>de</strong> la<br />

lista original.<br />

4. Hacer que cabeza (puntero cabeza) apunte al nuevo nodo que se ha creado.<br />

Ejemplo 14.3<br />

Uiiu lista <strong>en</strong>lazada conti<strong>en</strong>e tres elem<strong>en</strong>tos, 10, 25 y 40. Imertar un iiuevo elem<strong>en</strong>to, 4, <strong>en</strong> cabeza dr la


448 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Código C<br />

type<strong>de</strong>f int Item;<br />

typedf struct tipo-nodo<br />

i<br />

Item dato;<br />

struct tipo-nodo* sigui<strong>en</strong>te;<br />

} Nodo; /* <strong>de</strong>claración <strong>de</strong>l tipo Nodo */<br />

Nodo* nuevo;<br />

nuevo = (Nodo*)malloc(sizeof(Nodo));/* se asigna un nuevo nodo */<br />

nuevo -> dato = <strong>en</strong>trada;<br />

Paso 3<br />

El campo <strong>en</strong>lace (sigui<strong>en</strong>te) <strong>de</strong>l nuevo nodo apunta a la cabeza actual <strong>de</strong> la lista<br />

Código C<br />

nuevo -> sigui<strong>en</strong>te = cabeza;<br />

cabeza<br />

25 NTJLL<br />

Paso 4<br />

Se cambia el puntero <strong>de</strong> cabeza para apuntar al nuevo nodo creado: es <strong>de</strong>cir, el puntero <strong>de</strong> cabeza apunta<br />

al mismo sitio que apunte nuevo<br />

Código C<br />

cabeza = nuevo;<br />

rabei:i< = nuevo;<br />

EE-+--7<br />

nuevo<br />

4<br />

10 25 NULL


Listas <strong>en</strong>lazadas 449<br />

En este mom<strong>en</strong>to, la función <strong>de</strong> insertar un elem<strong>en</strong>to <strong>en</strong> la lista termina su ejecución y la variable<br />

local nuevo <strong>de</strong>saparece y sólo permanece el puntero <strong>de</strong> cabeza cabeza que apunta a la nueva lista<br />

<strong>en</strong>lazada<br />

0 4<br />

10 25 NUI,I,<br />

El código fu<strong>en</strong>te <strong>de</strong> la función InsertarCabezaLista:<br />

void InsertarCabezaLista(Nodo** cabeza, Item <strong>en</strong>trada)<br />

Nodo *nuevo ;<br />

nuevo = (Nodo*)malloc(sizeof(Nodo));/* asigna nuevo nodo */<br />

nuevo -> dato = <strong>en</strong>trada; /* pone elem<strong>en</strong>to <strong>en</strong> nuevo */<br />

nuevo -> sigui<strong>en</strong>te = *cabeza; /* <strong>en</strong>laza nuevo nodo al fr<strong>en</strong>te <strong>de</strong><br />

la lista */<br />

*cabeza = nuevo;<br />

/* mueve puntero cabeza y apuntd<br />

al nuevo nodo */<br />

Caso particular<br />

La función InsertarCabezaLista actúa también correctam<strong>en</strong>te si se trata el caso <strong>de</strong> añadir un primer<br />

nodo o elem<strong>en</strong>to a una lista vacía. En este caso, y como ya se ha com<strong>en</strong>tado cabeza apunta a NULL y<br />

termina apuntando al nuevo nodo <strong>de</strong> la lista <strong>en</strong>lazada.<br />

Ejercicio 14.1<br />

Crear una lista <strong>de</strong> números aleatorios e insertar los nuevos nodos por la cabeza <strong>de</strong> la lista. Un vez<br />

creada la lista, .se ha <strong>de</strong> recorrer los nodos pura mostrar los números pares.<br />

Análisis<br />

La función InsertarCabezaLista ( ) aña<strong>de</strong> un nodo a la lista, siempre como nodo cabeza. El priiner<br />

argum<strong>en</strong>to es un puntero a puntero porque ti<strong>en</strong>e que modificar la variable cabeza, que es a su vez un<br />

puntero a Nodo. La función NuevoNodo ( ) reserva memoria para un nodo, asigna el campo dato y<br />

<strong>de</strong>vuelve la dirección <strong>de</strong>l nodo creado.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine MX 99<br />

type<strong>de</strong>f int item;<br />

type<strong>de</strong>f struct Elem<strong>en</strong>to<br />

{<br />

Item dato;<br />

struct Elem<strong>en</strong>to* sigui<strong>en</strong>te;<br />

}Nodo ;<br />

void InsertarCabezaLista(Nodo** cabeza, Item <strong>en</strong>trada);<br />

Nodo* NuevoNodo(1tem x);<br />

void main ( )


450 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

{<br />

Item d;<br />

Nodo *cabeza,*Ptr;<br />

int k;<br />

cabeza = NULI,; /* Inicidl lid c-dbezd lisid vacía */<br />

randomize ( ) ;<br />

/* El bucle termind cudndo se y<strong>en</strong>erd el número aleatorLo O */<br />

for (u=random(MX); d; )<br />

i<br />

ZnsertarCabezaListd (&cabeza, d) ;<br />

d = random(MX) ;<br />

1<br />

/* Ahora se recorre la lisid para escribir los pares */<br />

for (k=O,ptr=cabeza; pt.r;<br />

i<br />

if (ptr->dato%2 == O)<br />

i<br />

i<br />

printf ("ad ",ptr-idato);<br />

k++;<br />

printf("%c", (k%12?' ':'\n')); /*ccrdd. 12 <strong>datos</strong> sa1t.a <strong>de</strong> línea */<br />

i<br />

ptr = ptr->si~giii <strong>en</strong>te;<br />

printf ("\n\n") ;<br />

void lnsertarCabezaL sigui<strong>en</strong>te = *cabeza; /* <strong>en</strong>laza nuevo nodo al<br />

fr<strong>en</strong>te <strong>de</strong> Ic1 lista */<br />

*cabeza = nuevo;<br />

/* mueve puntero cabeza y dpilntd al nuev<br />

nodo */<br />

Nodo* NuevoNodo (Item x)<br />

i<br />

Nodo *a ;<br />

a = (Nodo*)malloc(sizeot(Nodo)); /* asiqna nuevo nodo */<br />

a -> dato = x;<br />

a -> sigui<strong>en</strong>te = TKJIJ,;<br />

return a;<br />

i<br />

Inserción <strong>de</strong> un nuevo nodo que no esta <strong>en</strong> la cabeza <strong>de</strong> lista<br />

La inserción <strong>de</strong> un nuevo nodo no siempre se realiza al principio (<strong>en</strong> cabeza) <strong>de</strong> la lista. Se pue<strong>de</strong> inserta<br />

<strong>en</strong> el c<strong>en</strong>tro o al final <strong>de</strong> la lista.<br />

Ejemplo 14.4<br />

4<br />

Se <strong>de</strong>sea insertar un nuevo elem<strong>en</strong>to 75 <strong>en</strong>tre cl elem<strong>en</strong>to 25 y el elern<strong>en</strong>to 40 <strong>en</strong> la lista <strong>en</strong>lu~ada I<br />

25, 40.


Listas <strong>en</strong>lazadas 451<br />

El algoritmo <strong>de</strong> la nueva operación insertar requiere las sigui<strong>en</strong>tes etapas:<br />

1. Asignar el nuevo nodo apuntado por el puntero nuevo.<br />

2. Situar el nuevo elem<strong>en</strong>to <strong>en</strong> el campo dutn (Info) <strong>de</strong>l nuevo nodo.<br />

3. Hacer que el campo <strong>en</strong>lace sigui<strong>en</strong>te <strong>de</strong>l nuevo nodo apunte al nodo que va <strong>de</strong>spués <strong>de</strong> la<br />

posición <strong>de</strong>l nuevo nodo (o bi<strong>en</strong> a NULL si no hay ningún nodo <strong>de</strong>spués <strong>de</strong> la nueva posición).<br />

4. En la variable puntero anterior t<strong>en</strong>er la dirección <strong>de</strong>l nodo que está antes <strong>de</strong> la posición<br />

<strong>de</strong>seada'para el nuevo nodo. Hacer que anterior -> sigui<strong>en</strong>te apunte al nuevo nodo que se<br />

acaba <strong>de</strong> crear.<br />

Etapas I y 2<br />

Se crea un nuevo nodo que conti<strong>en</strong>e a 75<br />

Código C<br />

nuevo = (Nodo*)malloc (sizeof (Nodo ) ;<br />

nuevo -> dato = <strong>en</strong>trada ;<br />

Etapa 3


F<br />

452 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Código C<br />

nuevo -> sigui<strong>en</strong>te = anterior -> sigui<strong>en</strong>te<br />

Etapa 4<br />

antrr lor<br />

NU I.> L<br />

J<br />

nuevo<br />

nuevo<br />

Después <strong>de</strong> ejecutar todas las s<strong>en</strong>t<strong>en</strong>cias <strong>de</strong> las sucesivas etapas, la nueva lista com<strong>en</strong>zaría <strong>en</strong> el nodo 10,<br />

seguiría 25, 75 y, por Último, 40.<br />

Código C<br />

void InsertarLista(Nodo* anterior,Item <strong>en</strong>trada)<br />

i<br />

Nodo *nuevo;<br />

i<br />

nuevo = (Nodo*)malloc(sizeof(Nodo));<br />

nuevo -> dato = <strong>en</strong>trada;<br />

nuevo -> sigui<strong>en</strong>te = anterior -> sigui<strong>en</strong>te;<br />

anterior -> sigui<strong>en</strong>te = nuevo;<br />

Inserción al final <strong>de</strong> la lista<br />

La inserción al final <strong>de</strong> la lista es m<strong>en</strong>os efici<strong>en</strong>te <strong>de</strong>bido a que, normalm<strong>en</strong>te, no se ti<strong>en</strong>e un puntero al<br />

Último elem<strong>en</strong>to <strong>de</strong> la lista y <strong>en</strong>tonces se ha <strong>de</strong> seguir la traza <strong>de</strong>s<strong>de</strong> la cabeza <strong>de</strong> la lista hasta el último<br />

nodo <strong>de</strong> la lista y a continuación realizar la inserción. Cuando ultimo es una variable puntero que<br />

apunta al Último nodo <strong>de</strong> la lista, las s<strong>en</strong>t<strong>en</strong>cias sigui<strong>en</strong>tes insertan un nodo al final <strong>de</strong> la lista.<br />

ultimo -> sigui<strong>en</strong>te = (Nodo*)malloc(sizeof(Nodo));<br />

ultimo -> sigui<strong>en</strong>te -> dato = <strong>en</strong>trada;<br />

ultimo -> sigui<strong>en</strong>te -> sigui<strong>en</strong>te = NULL;<br />

ultimo = ultimo -> sigui<strong>en</strong>te;<br />

La primera s<strong>en</strong>t<strong>en</strong>cia asigna un nuevo nodo que está apuntado por el campo sigui<strong>en</strong>te al último<br />

nodo <strong>de</strong> la lista (antes <strong>de</strong> la inserción) <strong>de</strong> modo que el nuevo nodo ahora es el último nodo <strong>de</strong> la lista.<br />

La segunda s<strong>en</strong>t<strong>en</strong>cia establece el campo dato <strong>de</strong>l nuevo Último nodo al valor <strong>de</strong> <strong>en</strong>trada. La tercera<br />

s<strong>en</strong>t<strong>en</strong>cia establece el campo sigui<strong>en</strong>te <strong>de</strong>l nuevo Último nodo a NULL. La última s<strong>en</strong>t<strong>en</strong>cia pone la<br />

variable ui t imo al nuevo último nodo <strong>de</strong> la lista.


T<br />

Listas <strong>en</strong>lazadas 453<br />

I<br />

I 14.3.7. Búsqueda <strong>de</strong> un elem<strong>en</strong>to Dado que una función <strong>en</strong> C pue<strong>de</strong> <strong>de</strong>volver un puntero, el algoritmo que sirva para localizar un elem<strong>en</strong>to<br />

<strong>en</strong> una lista <strong>en</strong>lazada pue<strong>de</strong> <strong>de</strong>volver un puntero a ese elem<strong>en</strong>to.<br />

41 .7'> 101.43<br />

5.75<br />

La función BuscarLista utiliza una variable puntero d<strong>en</strong>ominada indice que va recorri<strong>en</strong>do la<br />

lista nodo a nodo. Mediante un bucle, Indice apunta a los nodos <strong>de</strong> la lista <strong>de</strong> modo que si se <strong>en</strong>cu<strong>en</strong>tra<br />

el nodo buscado, se <strong>de</strong>vuelve un puntero al nodo buscado con la s<strong>en</strong>t<strong>en</strong>cia <strong>de</strong> retorno (return); <strong>en</strong> el<br />

caso <strong>de</strong> no <strong>en</strong>contrarse el nodo buscado la función <strong>de</strong>be <strong>de</strong>volver NULL (return NULL)<br />

Código C<br />

Nodo* BuscarLista (Nodo" cabeza, item <strong>de</strong>stino)<br />

/* cabeza: puntero <strong>de</strong> cabeza <strong>de</strong> una lista <strong>en</strong>lazada.<br />

<strong>de</strong>stino: dato que se busca <strong>en</strong> la lista.<br />

indice: valor <strong>de</strong> retorno, puntero que apunta al primer<br />

nodo que conti<strong>en</strong>e el <strong>de</strong>stino (elem<strong>en</strong>to buscado);<br />

si no existe el nodo, se <strong>de</strong>vuelve puntero nulo.<br />

*/<br />

i<br />

Nodo "indice;<br />

for (indice = cabeza; indice != NULL; indice = indice -><br />

sigui<strong>en</strong>te)<br />

if (<strong>de</strong>stino == índice -> dato)<br />

return indice;<br />

I<br />

Ejemplo 14.5<br />

En este ejemplo se escribe unaJcuncicín para <strong>en</strong>contrar la dirección <strong>de</strong> un nodo dada su posición <strong>en</strong><br />

una lista <strong>en</strong>lazada.<br />

Análisis<br />

El nodo o elem<strong>en</strong>to se especifica por su posición <strong>en</strong> la lista; para ello se consi<strong>de</strong>ra posición 1, la<br />

correspondi<strong>en</strong>te al nodo <strong>de</strong> cabeza, posición 2, la correspondi<strong>en</strong>te al sigui<strong>en</strong>te nodo, y así sucesivam<strong>en</strong>te.<br />

El algoritmo <strong>de</strong> búsqueda <strong>de</strong>l elem<strong>en</strong>to comi<strong>en</strong>za con el recorrido <strong>de</strong> la lista mediante un puntero<br />

indice que comi<strong>en</strong>za apuntando al nodo cabeza <strong>de</strong> la lista. Un bucle mueve el indice hacia a<strong>de</strong>lante<br />

el número correcto <strong>de</strong> sitios (lugares). A cada iteración <strong>de</strong>l bucle se mueve el puntero indice un nodo<br />

hacia a<strong>de</strong>lante. El bucle termina cuando se alcanza la posición <strong>de</strong>seada e indice apunta al nodo<br />

correcto. El bucle también se pue<strong>de</strong> terminar si indice apunta a NULL, lo que indicará que la posición<br />

solicitada era más gran<strong>de</strong> que el número <strong>de</strong> nodos <strong>de</strong> la lista.<br />

Código C<br />

Nodo* BuscarPosicion(Nodo "cabeza, size-t posicion)<br />

/* El programa que llame a esta función ha <strong>de</strong> incluir<br />

biblioteca std1ib.h (para implem<strong>en</strong>tar tipo size-t)<br />

*/<br />

{<br />

Nodo "indice;


454 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

I<br />

size-t i;<br />

if (O < posicion) /* posición ha <strong>de</strong> ser mayor que O */<br />

return NULL;<br />

indice = cabeza;<br />

for (i = 1 ;(i < posición) && (indice != NULL) ; i++)<br />

indice = indice -> sigui<strong>en</strong>te;<br />

return indice;<br />

14.3.8. Supresión <strong>de</strong> un nodo <strong>en</strong> una lista<br />

La operación <strong>de</strong> eliminar un nodo <strong>de</strong> una lista <strong>en</strong>lazada supone <strong>en</strong>lazar el nodo anterior con el nod<br />

sigui<strong>en</strong>te al que se <strong>de</strong>sea eliminar y liberar la memoria que ocupa. El algoritmo para eliminar un nod<br />

que conti<strong>en</strong>e un dato se pue<strong>de</strong> expresar <strong>en</strong> estos pasos:<br />

1. Búsqueda <strong>de</strong>l nodo que conti<strong>en</strong>e el dato. Se ha <strong>de</strong> t<strong>en</strong>er la dirección <strong>de</strong>l nodo a eliminar y l<br />

dirección <strong>de</strong>l anterior.<br />

2. El puntero sigui<strong>en</strong>te <strong>de</strong>l nodo anterior ha <strong>de</strong> apuntar al sigui<strong>en</strong>te <strong>de</strong>l nodo a eliminar.<br />

3. En caso <strong>de</strong> que el nodo a eliminar sea el primero, cabeza, se modifica cabeza para que t<strong>en</strong>g<br />

la dirección <strong>de</strong>l nodo sigui<strong>en</strong>te.<br />

4. Por último, se libera la memoria ocupada por el nodo.<br />

A continuación se escribe una función que recibe la cabeza <strong>de</strong> la lista y el dato <strong>de</strong>l nodo que s<br />

quiere borrar.<br />

void eliminar (Nodo** cabeza, item <strong>en</strong>trada)<br />

i<br />

Nodo* actual, "anterior;<br />

int <strong>en</strong>contrado = O;<br />

actual = *cabeza; anterior = NULL;<br />

/* Bucle <strong>de</strong> búsqueda */<br />

while ((actual!=NULL) && (!<strong>en</strong>contrado))<br />

i<br />

<strong>en</strong>contrado = (actual->dato = = <strong>en</strong>trada);<br />

if (!<strong>en</strong>contrado)<br />

I<br />

{<br />

1<br />

anterior = actual;<br />

actual = actual -> sigui<strong>en</strong>te;<br />

/* Enlace <strong>de</strong> nodo anterior con sigui<strong>en</strong>te */<br />

if (actual != NULL)<br />

i ,<br />

/* Se distingue <strong>en</strong>tre que el nodo sea el cabecera o <strong>de</strong>l<br />

resto <strong>de</strong> la lista */<br />

if (actual == *cabeza)<br />

"cabeza = actual->sigui<strong>en</strong>te;<br />

I<br />

else {<br />

anterior -> sigui<strong>en</strong>te = actual ->sigui<strong>en</strong>te<br />

1<br />

free(actua1);


Listas <strong>en</strong>lazadas 455<br />

Ejercicio 14.2<br />

Se <strong>de</strong>sea crear una lista <strong>en</strong>lazada <strong>de</strong> números <strong>en</strong>teros ord<strong>en</strong>ada. La lista va estar organizada <strong>de</strong> tal<br />

forma que el nodo cabecera t<strong>en</strong>ga el melior elem<strong>en</strong>to, y así <strong>en</strong> ord<strong>en</strong> creci<strong>en</strong>te los <strong>de</strong>más nodos. Una ve:<br />

creada la lista, se recorre pam escribir los <strong>datos</strong> por pantalla.<br />

Análisis<br />

La función Insertaord<strong>en</strong> ( ) aña<strong>de</strong> los nuevos elem<strong>en</strong>tos. Inicialm<strong>en</strong>te la lista se crea con el primer<br />

valor. El segundo elem<strong>en</strong>to se ha <strong>de</strong> insertar antes <strong>de</strong>l primero o <strong>de</strong>spués, <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> que sea m<strong>en</strong>or<br />

o mayor. Así, <strong>en</strong> g<strong>en</strong>eral, para insertar un nuevo elem<strong>en</strong>to, primero se busca la posición <strong>de</strong> inserción <strong>en</strong><br />

la lista actual, que <strong>en</strong> todo mom<strong>en</strong>to está ord<strong>en</strong>ada, <strong>de</strong>l nodo a partir <strong>de</strong>l cual se ha <strong>de</strong> <strong>en</strong>lazar el nuevo<br />

nodo para que la lista siga ord<strong>en</strong>ada. La función I ecorrer ( ) avanza por cada uno <strong>de</strong> los nodos <strong>de</strong> la<br />

lista con la finalidad <strong>de</strong> escribir el campo dato.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine MX 101<br />

type<strong>de</strong>f int Item;<br />

type<strong>de</strong>f struct Elem<strong>en</strong>to<br />

I<br />

Item dato;<br />

struct Elern<strong>en</strong>to* sigui<strong>en</strong>te;<br />

i Nodo ;<br />

void InsertaOrd<strong>en</strong>(Nodo** cabeza, ltcm <strong>en</strong>tradü);<br />

Nodo* NuevoNodo(1tem x);<br />

void recorrer(Nodo* cabeza)<br />

void müin()<br />

{<br />

Item d;<br />

Nodo* cabeza;<br />

I<br />

cabetd = NULL; /* Inicid ida cabeza a lista vacía */<br />

r andomi Le ( ) ;<br />

/* El bucle termind cuando se qeriera el número aleatorio O */<br />

for (d=random(MX); d; )<br />

i<br />

InsertaOrd<strong>en</strong>(&cabe/d,d);<br />

d = random(MX) ;<br />

1<br />

recorrer(ciibeza);<br />

void InsertaOrd<strong>en</strong>(Nodo** cdbezd, Item eritrddd)<br />

t<br />

Nodo *nuevo;<br />

nuevo = NuevoNodo(<strong>en</strong>trada);<br />

if ("cabeza == NULI,)<br />

"cabeza = nuevo;<br />

else if (<strong>en</strong>trada < (*cübeza)->ddto)<br />

i<br />

nuevo -> sigui<strong>en</strong>te = *cabeza;<br />

*cabeza = nuevo;


456 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

i<br />

else /* búsqueda <strong>de</strong>l nodo anterior a partir <strong>de</strong>l que<br />

se <strong>de</strong>be insertar */<br />

I<br />

Nodo* anter-ior, *p;<br />

anterior = p = "cabeza;<br />

while ((p->sigui<strong>en</strong>te != NULL,) && (<strong>en</strong>trada > p->dato))<br />

{<br />

1<br />

anterior = p;<br />

p = p->sigui<strong>en</strong>te;<br />

if (<strong>en</strong>trada > p->dato) /* se inserta <strong>de</strong>spués <strong>de</strong>l Último nodo */<br />

anterior = p;<br />

/* Se proce<strong>de</strong> al <strong>en</strong>lace <strong>de</strong>l nuevo nodo */<br />

nuevo -> sigui<strong>en</strong>te = anterior -> sigui<strong>en</strong>te;<br />

anterior -> sigui<strong>en</strong>te = nuevo;<br />

Nodo* NuevoNodo(item x)<br />

t<br />

Nodo *a ;<br />

a = (Nodo*)malloc(sizeof(Nodo));/* asigna nuevo nodo */<br />

a -> dato = x; /* pone elem<strong>en</strong>to <strong>en</strong> nuevo */<br />

a -> sigui<strong>en</strong>te = NULL;<br />

return a;<br />

1<br />

void recorrer(Nodo* cabeza)<br />

i<br />

int k;<br />

printf ("\n\t\t Lista Ord<strong>en</strong>dda \n")<br />

for (k=O; cabeza; cabezazcabeza->sigui<strong>en</strong>te)<br />

i<br />

printf ("%d ",cabeza->dato) ;<br />

;<br />

i<br />

k++;<br />

printf("%c", (k%15 ?'<br />

1<br />

printf ("\n\n") ;<br />

.:'\n'));<br />

!<br />

14.4. LISTA DOBLEMENTE ENLAZADA<br />

Hasta ahora el recorrido <strong>de</strong> una lista se realizaba <strong>en</strong> s<strong>en</strong>tido directo (a<strong>de</strong>lante) o, <strong>en</strong> algunos casos, <strong>en</strong><br />

s<strong>en</strong>tido inverso (hacia atrás). Sin embargo, exist<strong>en</strong> numerosas aplicaciones <strong>en</strong> las que es conv<strong>en</strong>i<strong>en</strong>te<br />

po<strong>de</strong>r acce<strong>de</strong>r a los elem<strong>en</strong>tos o nodos <strong>de</strong> una lista <strong>en</strong> cualquier ord<strong>en</strong>. En este caso se recomi<strong>en</strong>da el<br />

uso <strong>de</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada. En tal lista, cada elem<strong>en</strong>to conti<strong>en</strong>e dos punteros, aparte <strong>de</strong>l<br />

valor almac<strong>en</strong>ado <strong>en</strong> el elem<strong>en</strong>to. Un puntero apunta al sigui<strong>en</strong>te elem<strong>en</strong>to <strong>de</strong> la lista y el otro puntero<br />

apunta al elem<strong>en</strong>to anterior. La Figura 14.6 muestra una lista doblem<strong>en</strong>te <strong>en</strong>lazada y un nodo <strong>de</strong> dicha<br />

lista.


Listas <strong>en</strong>lazadas<br />

457<br />

< I<br />

cabeza<br />

i a)<br />

í b)<br />

Figura 14.6. Lista doblem<strong>en</strong>te <strong>en</strong>lazada. (a) Lista con tres nodos; (b) nodo.<br />

Existe una operación <strong>de</strong> insertur y eliminar (borrar) <strong>en</strong> cada dirección. La Figura 14.7 muestra el<br />

problema <strong>de</strong> insertar un nodo p a la <strong>de</strong>recha <strong>de</strong>l nodo actual. Deb<strong>en</strong> asignarse cuatro nuevos <strong>en</strong>laces<br />

Nodo actual<br />

iiiii;<br />

Figura 14.7. Inserción <strong>de</strong> un nodo <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada<br />

En el caso <strong>de</strong> eliminar (borrar) un nodo <strong>de</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada es preciso cambiar dos<br />

punteros.<br />

D<br />

I<br />

Figura 14.8. Eliminación <strong>de</strong> un nodo <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada.<br />

14.4.1. Declaración <strong>de</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada<br />

Una lista doblem<strong>en</strong>te <strong>en</strong>lazada con valores <strong>de</strong> tipo int necesita dos punteros y el valor <strong>de</strong>l campo <strong>datos</strong>.<br />

En una <strong>estructura</strong> se agrupan estos <strong>datos</strong> <strong>de</strong>l modo sigui<strong>en</strong>te:<br />

type<strong>de</strong>f i nt Item;<br />

struct unnodo<br />

I<br />

Item dato;<br />

struct unnodo *a<strong>de</strong>lante;


458 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

struct unnodo *atras;<br />

I;<br />

t-ype<strong>de</strong>f struct unnodo Nodo;<br />

14.4.2. Insertar un elem<strong>en</strong>to <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada<br />

El algoritmo empleado para añadir o insertar un elem<strong>en</strong>to <strong>en</strong> una lista doble varía <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> la<br />

posición <strong>en</strong> que se <strong>de</strong>sea insertar el elem<strong>en</strong>to. La posición <strong>de</strong> inserción pue<strong>de</strong> ser:<br />

O<br />

O<br />

O<br />

O<br />

En la cabeza (elem<strong>en</strong>to primero) <strong>de</strong> la lista.<br />

En el final <strong>de</strong> la lista (elem<strong>en</strong>to Último).<br />

Antes <strong>de</strong> un elem<strong>en</strong>to especificado.<br />

Después <strong>de</strong> un elem<strong>en</strong>to especificado.<br />

Insertar un nuevo elem<strong>en</strong>to <strong>en</strong> la cabeza <strong>de</strong> una lista doble<br />

El proceso <strong>de</strong> inserción se pue<strong>de</strong> resumir <strong>en</strong> este algoritmo:<br />

1. Asignar un nuevo nodo apuntado por nuevo que es una variable puntero local que apunta al<br />

nuevo nodo que se va a insertar <strong>en</strong> la lista doble.<br />

2. Situar el nuevo elem<strong>en</strong>to <strong>en</strong> el campo dci to ( ~nfo) <strong>de</strong>l nuevo nodo.<br />

3. Hacer que el campo <strong>en</strong>lace a<strong>de</strong>lante <strong>de</strong>l nuevo nodo apunte a la cabeza (primer nodo) <strong>de</strong> la lista<br />

original, y que el campo <strong>en</strong>lace at-ras <strong>de</strong>l nodo cabeza apunte al nuevo nodo.<br />

4. Hacer que cabeza (puntero cabeza) apunte al nuevo nodo que se ha creado.<br />

Chdigo C<br />

type<strong>de</strong>t int Item;<br />

typedf struct tipo-nodo<br />

i<br />

Item dato;<br />

struct tipo-nodo* a<strong>de</strong>lante;<br />

struct tipopnodo* atras;<br />

}Nodo ;<br />

Nodo* nuevo;<br />

nuevo = (Nodo*)rnalloc(sizeof(Nodo));<br />

nuevo -> dato = <strong>en</strong>trada<br />

nuevo -> a<strong>de</strong>lante = cabezd;<br />

nuevo -> atras = NUT,T,;<br />

cabeza -> atras = nuevo;<br />

cabeza = nuevo;<br />

En este mom<strong>en</strong>to, la función <strong>de</strong> insertar un elem<strong>en</strong>to <strong>en</strong> la lista termina su ejecución y la variable<br />

local nuevo <strong>de</strong>saparece y sólo permanece el puntero <strong>de</strong> cabeza cabeza que apunta a la nueva lista<br />

doblem<strong>en</strong>te <strong>en</strong>lazada.<br />

Inserción <strong>de</strong> un nuevo nodo que no esta <strong>en</strong> la cabeza <strong>de</strong> lista<br />

La inserción <strong>de</strong> un nuevo nodo <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada se pue<strong>de</strong> realizar <strong>en</strong> un nodo intermedio<br />

<strong>de</strong> ella. El algoritmo <strong>de</strong> la nueva operación insertar requiere las sigui<strong>en</strong>tes etapas:<br />

1. Asignar el nuevo nodo apuntado por el puntero nuevo.<br />

2. Situar el nuevo elem<strong>en</strong>to <strong>en</strong> el campo dato (lnfo) <strong>de</strong>l nuevo nodo.<br />

3. Hacer que el campo <strong>en</strong>lace a<strong>de</strong>lante <strong>de</strong>l nuevo nodo apunte al nodo que va <strong>de</strong>spués <strong>de</strong> la<br />

posición <strong>de</strong>l nuevo nodo (o bi<strong>en</strong> a NU1,L si no hay ningún nodo <strong>de</strong>spués <strong>de</strong> la nueva posición). El<br />

campo atras <strong>de</strong>l nodo sigui<strong>en</strong>te al nuevo ti<strong>en</strong>e que apuntar a nuevo.


Listas <strong>en</strong>lazadas 459<br />

4. La dirección <strong>de</strong>l nodo que está antes <strong>de</strong> la posición <strong>de</strong>seada para el nuevo nodo está <strong>en</strong> la variable<br />

puntero anterior. Hacer que anterior -> a<strong>de</strong>lante apunte al nuevo nodo. El <strong>en</strong>lace atrds<br />

<strong>de</strong>l nuevo nodo <strong>de</strong>be <strong>de</strong> apuntar a ant er lor .<br />

Chdigo C<br />

nuevo = (Nodo*)malloc(sizeof(Nodo)) ;<br />

nuevo -> dato = <strong>en</strong>trada ;<br />

nuevo -> a<strong>de</strong>lante = anterior -> a<strong>de</strong>lante;<br />

anterior -> a<strong>de</strong>lante -> atras = nuevo; /* campo atras <strong>de</strong>l sigui<strong>en</strong>te<br />

apunta al nodo nuevo creado */<br />

anterior -> a<strong>de</strong>lante = nuevo;<br />

nuevo -> atras = anterior;<br />

14.4.3. Supresión <strong>de</strong> un elem<strong>en</strong>to <strong>en</strong> una lista doblem<strong>en</strong>te <strong>en</strong>lazada<br />

La operación <strong>de</strong> eliminar un nodo <strong>de</strong> una lista doble supone realizar el <strong>en</strong>lace <strong>de</strong> dos punteros, el nodo<br />

anterior con el nodo sigui<strong>en</strong>te al que se <strong>de</strong>sea eliminar con el puntero a<strong>de</strong>lante y el nodo sigui<strong>en</strong>te con<br />

el anterior con el puntero atras y liberar la memoria que ocupa.<br />

El algoritmo para eliminar un nodo que conti<strong>en</strong>e un dato es similar al algoritmo <strong>de</strong> borrado para<br />

una lista simple. Ahora la dirección <strong>de</strong>l nodo anterior se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> el puntero atras <strong>de</strong>l nodo a<br />

borrar. Los pasos a seguir:<br />

1. Búsqueda <strong>de</strong>l nodo que conti<strong>en</strong>e el dato. Se ha <strong>de</strong> t<strong>en</strong>er la dirección <strong>de</strong>l nodo a eliminar y la<br />

dirección <strong>de</strong>l anterior.<br />

2. El puntero a<strong>de</strong>lante <strong>de</strong>l nodo anterior ti<strong>en</strong>e que apuntar al puntero a<strong>de</strong>lante <strong>de</strong>l nodo a<br />

eliminar, esto <strong>en</strong> el caso <strong>de</strong> no ser el nodo cabecera.<br />

3. El puntero atras <strong>de</strong>l nodo sigui<strong>en</strong>te a borrar ti<strong>en</strong>e que apuntar al puntero atras <strong>de</strong>l nodo a<br />

eliminar, esto <strong>en</strong> el caso <strong>de</strong> no ser el nodo último.<br />

4. En caso <strong>de</strong> que el nodo a eliminar sea el primero, cabeza, se modifica cabeza para que t<strong>en</strong>ga<br />

la dirección <strong>de</strong>l nodo sigui<strong>en</strong>te.<br />

5. Por último, se libera la memoria ocupada por el nodo.<br />

La codificación se pres<strong>en</strong>ta <strong>en</strong> la sigui<strong>en</strong>te función:<br />

void eliminar (Nodo** cabeza, item <strong>en</strong>trada)<br />

Nodo* actual;<br />

int <strong>en</strong>contrado = O;<br />

ictual - *cabeza;<br />

/* ~ucie <strong>de</strong> búsqueda */<br />

while ((actual!=NULL) && (!<strong>en</strong>contrado))<br />

I<br />

<strong>en</strong>contrado = (actual->dato =- <strong>en</strong>trada);<br />

if (!<strong>en</strong>contrado)<br />

actual = actual -> a<strong>de</strong>ldritc;<br />

/* Enlace <strong>de</strong> nodo anterior con siqiii~nte */<br />

if (actual != NULL)<br />

i<br />

/* Se distinque <strong>en</strong>tre que el nodo sed el cabecera o <strong>de</strong>l<br />

resto <strong>de</strong> la lista */<br />

if (actual == "cabezd)<br />

i<br />

"cabeza = actual->a<strong>de</strong>lante;


460 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

if (actual->a<strong>de</strong>lante != NULL)<br />

actual->a<strong>de</strong>lante->atras = NULL;<br />

1<br />

else if (actual->a<strong>de</strong>lante != NULL) /* No es el iiltimo nodo */<br />

{<br />

actual -> atras ->a<strong>de</strong>lante = actual -> a<strong>de</strong>lante;<br />

actual -> a<strong>de</strong>lante -> atrds = actual -> atras;<br />

i<br />

else { /* último nodo */<br />

actual -> atras -> a<strong>de</strong>lante = NULL;<br />

free(actua1);<br />

i<br />

Ejercicio 14.3<br />

Se va a crear una lista doblem<strong>en</strong>te <strong>en</strong>lazada con números <strong>en</strong>teros obt<strong>en</strong>idos aleatoriam<strong>en</strong>te. Una vez<br />

creada la lista se <strong>de</strong>sea eliminarse los nodos que estén fuera <strong>de</strong> un rango <strong>de</strong>terminado.<br />

Análisis<br />

La inserción <strong>de</strong> elem<strong>en</strong>tos <strong>en</strong> la lista se hace por el nodo cabecera. El número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong> la lista<br />

se pi<strong>de</strong> para ser introducido por teclado. También se pi<strong>de</strong> por teclado el rango <strong>de</strong> valores que <strong>de</strong>b<strong>en</strong> <strong>de</strong><br />

estar <strong>en</strong> la lista. Para eliminar los elem<strong>en</strong>tos se recorre la lista, los nodos que no están d<strong>en</strong>tro <strong>de</strong>l rango<br />

se borran <strong>de</strong> la lista. Para borrar los nodos se utiliza la función el iminar ( ) , t<strong>en</strong>i<strong>en</strong>do <strong>en</strong> cu<strong>en</strong>ta que la<br />

dirección <strong>de</strong>l nodo a suprimir ya se ti<strong>en</strong>e.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

type<strong>de</strong>f int Item;<br />

type<strong>de</strong>f struct Elem<strong>en</strong>to<br />

i<br />

Item dato;<br />

struct Elem<strong>en</strong>to" a<strong>de</strong>lante;<br />

struct Elem<strong>en</strong>to* atras;<br />

1 Nodo ;<br />

void InsertarCabezaLista(Nodo** cabeza, Item <strong>en</strong>trada);<br />

Nodo* NuevoNodo(1tem x);<br />

void eliminar(Nodo** cabeza, Nodo* actual);<br />

void recorrer(Nodo* ptr);<br />

void main( )<br />

{<br />

Nodo* cabeza,*ptr;<br />

int x,y;<br />

cabeza = NULL; /* Inicializa cabeza d lista vacía */<br />

randomize ( ) ;<br />

printf("\n Número <strong>de</strong> elem<strong>en</strong>tos a g<strong>en</strong>erar: ");<br />

scanf ("%d",&x);<br />

/* Se g<strong>en</strong>era la lista doble */<br />

for ( ; x--; )<br />

i<br />

InsertarCabezaLista(&cabeza,rand( );<br />

I


Listas <strong>en</strong>lazadas 461<br />

recorrer (cabeza) ;<br />

printf("\nRango <strong>de</strong> los valores que va a t<strong>en</strong>er la lista: " );<br />

scanf ("%d %d", &x, &y) ;<br />

/* Recorre la lista para el irnincir nodos que no están <strong>en</strong><br />

el rango <strong>de</strong> valores */<br />

printf ("\n\tNodos climinados\n") ;<br />

for (ptr=cabeza; ptr; )<br />

I<br />

if ((ptr->datoy) )<br />

i<br />

Nodo* t ;<br />

t = ptr->a<strong>de</strong>lante; /* Guarda el nodo por el que seguir */<br />

printf ("%-d",ptr->dato);<br />

eliminar(&cabeza,ptr);<br />

ptr = t;<br />

I<br />

else<br />

pkr = ptr->a<strong>de</strong>lante;<br />

/* Recorre la lista para mostrar sus clem<strong>en</strong>tos */<br />

recorrer(cabeza);<br />

void eliminar (Nodo** cabeza, Nodo* actual)<br />

1<br />

/* Elimina el nodo <strong>de</strong> dir-eccion actual.<br />

Se distingue <strong>en</strong>tre que el nodo sea el cabecera o <strong>de</strong>l<br />

resto <strong>de</strong> la lista.<br />

*/<br />

if (actual == *cabeza)<br />

i<br />

*cabeza = actual->a<strong>de</strong>lante;<br />

if (actual->a<strong>de</strong>lante != NULL)<br />

actual->a<strong>de</strong>lante->atras = NULL;<br />

1<br />

else if (actual->a<strong>de</strong>lante != NULL) /* No es el Último nodo */<br />

{<br />

actual -> atras ->a<strong>de</strong>lante = actual -> a<strong>de</strong>lante;<br />

actual -> a<strong>de</strong>lante -> atras = actual -> atras;<br />

else { /* Último nodo */<br />

actual -> atras -> a<strong>de</strong>lante = NULL;<br />

I<br />

free(actua1) ;<br />

i<br />

void recorrer(Nodo* ptr)<br />

t<br />

int k = O;<br />

printf ("\n\n\t Elem<strong>en</strong>tos <strong>de</strong> la lista\n") ;<br />

for ( ; ptr ; )<br />

i<br />

k++ ;<br />

printf ('I%-5d",ptr-> dato) ;<br />

printf ("%c",(k%12==O?'\n'<br />

: ' ') ) ;


462 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

1<br />

i<br />

ptr = ptr -> adcldnte;<br />

vclld insertarCabezaLi sta (Nodo** idbezcr, Item <strong>en</strong>trdda)<br />

i<br />

Nodo* nuevo;<br />

i<br />

nuevo = NuevoNodo(<strong>en</strong>trdda1;<br />

nuevo -> a<strong>de</strong>lante = *cCibead;<br />

nuevo -> atrds = NULL;<br />

if (*cabeza != NULL)<br />

("cabeza) -> atras = nucvo;<br />

*cabeza = nuevo;<br />

Nodo* NuevoNodo(1tern x)<br />

i<br />

Nodo *a ;<br />

a = (Nodo*)malloc<br />

a -> dato = x;<br />

a -> a<strong>de</strong>ldnte = d<br />

return a;<br />

i<br />

14.5. LISTAS CIRCULARES<br />

En las listas lineales simples o <strong>en</strong> las dobles siempre hay un primer nodo y un último nodo que ti<strong>en</strong>e el<br />

campo <strong>de</strong> <strong>en</strong>lace a nulo. Una iistu circular, por propia nuturule~u no ti<strong>en</strong>e ni principio ni,fin. Sin<br />

embargo, resulta Útil establecer un nodo a partir <strong>de</strong>l cual se acceda a la lista y así po<strong>de</strong>r acce<strong>de</strong>r a sus<br />

nodos. La Figura 14.9 muestra una lista circular con <strong>en</strong>lace simple; podría consi<strong>de</strong>rarse como una lista<br />

lineal, <strong>de</strong> tal manera que el último nodo apunta al primero.<br />

I<br />

Figura 14.9. Lista circular.<br />

Las operaciones que se realizan sobre una lista circular son similares a las operaciones sobre listas<br />

lineales, t<strong>en</strong>i<strong>en</strong>do <strong>en</strong> cu<strong>en</strong>ta que el 61 t.irrio nodo no apunta a nulo sino al primero. La creación <strong>de</strong> una<br />

lista circular se pue<strong>de</strong> hacer con un <strong>en</strong>lace simple o un <strong>en</strong>lace doble. Consi<strong>de</strong>ramos que la lista circular<br />

se <strong>en</strong>laza con un solo <strong>en</strong>lace, la realización con <strong>en</strong>lace a<strong>de</strong>luntc y utrús es similar (se pue<strong>de</strong> consultar<br />

el Apartado 14.4).<br />

14.5.1. Insertar un elem<strong>en</strong>to <strong>en</strong> una lista circular<br />

El algoritmo empleado para añadir o insertar un elem<strong>en</strong>to <strong>en</strong> una lista circular varía <strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> la<br />

posición <strong>en</strong> que se <strong>de</strong>sea insertar el elem<strong>en</strong>to. La posición <strong>de</strong> inserción pue<strong>de</strong> variar, consi<strong>de</strong>ramos que


Listas <strong>en</strong>lazadas 463<br />

se hace coino nodo anterior al <strong>de</strong>l nodo <strong>de</strong> acceso a la lista LL., y que I,(-. ti<strong>en</strong>e la dirección <strong>de</strong>l último<br />

nodo insertado. A continuación se escribe la <strong>de</strong>claración <strong>de</strong> un nodo, una función que crea un nodo y la<br />

función que inserta el nodo <strong>en</strong> la lista circular.<br />

I.ype<strong>de</strong>f char* Item;<br />

typedcf s truct E:] em<strong>en</strong>t.o<br />

i<br />

Item dato;<br />

s t ruc t Elem<strong>en</strong>to" s i gu i erit.e ;<br />

1 Nodo ;<br />

Nodo* NuevoNodo (Tt.em x<br />

i<br />

Nodo *a ;<br />

a (Nodo*)malloc(s<br />

-> dato = X;<br />

d -> siyui<strong>en</strong>te : u;<br />

ret-urn a;<br />

}<br />

vo i d T n s e r -t aC i r-c 11 1 a r ( Nodo * *<br />

i<br />

Nodo* nuevo ;<br />

nuevo = NuevoNodo (<strong>en</strong>tr ddii<br />

if (*Le != NIJLI,) /* I<br />

nuevo > sigui<strong>en</strong>te = (*<br />

I<br />

*Lc = nuevo;<br />

14.5.2. Supresión <strong>de</strong> un elem<strong>en</strong>to <strong>en</strong> una lista circular<br />

La operación <strong>de</strong> eliminar un nodo <strong>de</strong> una lista circular sigue los mismos pasos que los dados para<br />

eliminar un nodo <strong>en</strong> una lista lineal. Hay que <strong>en</strong>lazar el nodo anterior con el nodo sigui<strong>en</strong>te al que se<br />

<strong>de</strong>sea eliminar y liberar la memoria que ocupa. El algoritmo para eliminar un nodo <strong>de</strong> una lista circular:<br />

1. Búsqueda <strong>de</strong>l nodo que conti<strong>en</strong>e el dato.<br />

2. Se <strong>en</strong>laza el nodo anterior con el sigui<strong>en</strong>te.<br />

3. En caso <strong>de</strong> que el nodo a eliminar sea el refer<strong>en</strong>ciado por el puntero <strong>de</strong> acceso a la lista, I,(:, se<br />

modifica LC para que t<strong>en</strong>ga la dirección <strong>de</strong>l nodo anterior.<br />

4. Por último, se libera la mernoria ocupada por el nodo.<br />

En la función <strong>de</strong> eliminar hay que t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta la característica <strong>de</strong> lista circular, así para <strong>de</strong>tectar<br />

si la lista es <strong>de</strong> un solo nodo ocurre que se apunta a él mismo.<br />

[,e == L,c->sigui <strong>en</strong>te si esta expresión es cierta la lista consta <strong>de</strong> un solo nodo.<br />

A continuación se escribe el código <strong>de</strong> la función eliminar para una lista circular. Para ello recorre<br />

la lista con un puntero al nodo anterior, por esa razón se acce<strong>de</strong> al dato con la s<strong>en</strong>t<strong>en</strong>cia<br />

a c tu c~ 1 - > s i yu i <strong>en</strong>te - >dato.<br />

Esto permite, <strong>en</strong> el caso <strong>de</strong> <strong>en</strong>contrarse el nodo, t<strong>en</strong>er <strong>en</strong> drtud 1 el nodo anterior. Después <strong>de</strong>l bucle<br />

es necesario volver a preguntar por el campo dato, ya que no se comparó el nodo LC y el bucle pue<strong>de</strong><br />

haber terminado sin <strong>en</strong>contrar el nodo:


464 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Código C<br />

void eliminar (Nodo** Lc, Item <strong>en</strong>trada)<br />

i<br />

Nodo* actual ;<br />

int <strong>en</strong>contrado = O;<br />

actual = *Lc;<br />

/* Bucle <strong>de</strong> búsqueda */<br />

while ((actual->sigui<strong>en</strong>te != *Lc) && (!<strong>en</strong>contrado))<br />

i<br />

<strong>en</strong>contrado = (actudl->ciguicntc-;düto == <strong>en</strong>trada);<br />

if (!<strong>en</strong>contrado)<br />

{<br />

actual = actual -> sigui<strong>en</strong>te;<br />

1<br />

1<br />

<strong>en</strong>contrado = (actual->siyui~<strong>en</strong>te->dato == <strong>en</strong>trada);<br />

/* Enlace <strong>de</strong> nodo anterior con sigiii<strong>en</strong>te */<br />

if (<strong>en</strong>contrado)<br />

{<br />

i<br />

Nodo* p;<br />

p = actual->sigui<strong>en</strong>te; /* Nodo il eliminar */<br />

if (*Lc == (*Lc)->sigui<strong>en</strong>te) /* Lista con un solo nodo */<br />

*Lc = NULL;<br />

else {<br />

if (p == *Lc)<br />

i<br />

*Lc = actual; /* Se borra el elem<strong>en</strong>to refer<strong>en</strong>ciado por Lc;<br />

el nuevo acceso a Id lista es el anterior */<br />

}<br />

actual->sigui<strong>en</strong>te = p->siqui<strong>en</strong>te;<br />

i<br />

free (PI ;<br />

~~~<br />

Ejercicio 14.4<br />

Este ejercicio crea una lista circular con paluhras leídas <strong>de</strong>l teclado. El programa <strong>de</strong>be t<strong>en</strong>er u<br />

conjunto <strong>de</strong> opciones para:<br />

a) Mostrar las cad<strong>en</strong>as yue,forman la lista;<br />

h) Borrar una palabra dada;<br />

c) Al terminar la ejecución, recorrer la lista eliminando los nodos.<br />

Análisis<br />

Los nodos <strong>de</strong> la lista ti<strong>en</strong><strong>en</strong> como campo dato un puntero a una cad<strong>en</strong>a que es la palabra. Des<strong>de</strong><br />

teclado se lee la palabra <strong>en</strong> un buffer sufici<strong>en</strong>tem<strong>en</strong>te amplio; se ha <strong>de</strong> reservar memoria para tanto<br />

caracteres como longitud (s t r 1 <strong>en</strong> ( ) ) t<strong>en</strong>ga la cad<strong>en</strong>a leída y asignar su dirección al puntero <strong>de</strong>l nodo<br />

a continuación se copia el buffer a la memoria reservada (campo dato <strong>de</strong>l nodo). El nodo se insert<br />

llamando a la función insertacirculdr ( ). Para borrar una palabra se llama a la funci<br />

eliminar ( ) .<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong>


Listas <strong>en</strong>lazadas 465<br />

type<strong>de</strong>f char* Item;<br />

type<strong>de</strong>f struct Elem<strong>en</strong>to<br />

{<br />

Item dato;<br />

struct Elem<strong>en</strong>to* sigui<strong>en</strong>te;<br />

}Nodo ;<br />

Nodo* NuevoNodo(1tem x);<br />

void InsertaCircular(Nodo** Lc, Item <strong>en</strong>trada);<br />

void eliminar(Nodo** Lc, Item <strong>en</strong>trada);<br />

void recorrer(Nodo* Lc);<br />

void borrarlista(Nodo** Lc);<br />

int main ( )<br />

1<br />

{<br />

char cad<strong>en</strong>a [ 81 I ;<br />

Nodo *Lc; int opc;<br />

LC = NULL;<br />

printf ("\n\n Entrada <strong>de</strong> Nombres. Termina con ^Z.\n");<br />

while (gets(cad<strong>en</strong>a )<br />

{<br />

Insertacircular (&Lc,cad<strong>en</strong>d);<br />

I<br />

recorrer (Lc);<br />

puts("\n\n\t Opciones para manejar la lista");<br />

do {<br />

puts("\n 1. Elimar una palabra <strong>de</strong> la lista circular.\n");<br />

puts("\n 2. Mostrar todos los elem<strong>en</strong>tos <strong>de</strong> la lista.\n");<br />

puts("\n 3. Salir y eliminar los nodos <strong>de</strong> la lista.\n");<br />

do {<br />

scanf ("%d%*c" I LoPC) ;<br />

}while (opc


466 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

}<br />

void InsertaCirculdr ( Nodo** L(., I tem erit rcidd)<br />

Nodo* nuevo;<br />

1<br />

nuevo = NuevoNodo (e2ni.r ada) ;<br />

if (*Lc != NULL) /* iisL,i ~irciil~i~. 110 vacía */<br />

i<br />

nuevc -> sigui<strong>en</strong>te = (*k) -> sigiiieril.~;<br />

(*Lc) -> sigui<strong>en</strong>t-e = nuevo;<br />

i<br />

*Lc = nuevo;<br />

void eliminar (Nodo** Lc, ILim c~nt.rddci)<br />

I<br />

Nodo* actual;<br />

int <strong>en</strong>contrddo = O;<br />

,<br />

actual = *Lc;<br />

/* Bucle <strong>de</strong> búsqueda */<br />

while ( (actudL->sigul<strong>en</strong>te !- *Lc) hh ( !<strong>en</strong>contrado))<br />

i<br />

<strong>en</strong>contrado = strcmp (dctudl- ~siyuicnte->dato, <strong>en</strong>trada) ==O;<br />

if ( ! <strong>en</strong>contr ddo)<br />

i<br />

actual = actual -> siqiii<strong>en</strong>te;<br />

i<br />

i<br />

<strong>en</strong>contrddo = strcmp(~ictud1 ->siguirntc--ddt o, <strong>en</strong>trada) ==O;<br />

/* Enlace <strong>de</strong> nodo dntcrior con siqui<strong>en</strong>te */<br />

if (<strong>en</strong>contrddo 1<br />

t<br />

Nodo* p;<br />

yrintf("\nNodo <strong>de</strong> la pcildbrd \" Bs \" <strong>en</strong>contrado. \n",<strong>en</strong>trada);<br />

p = actual->sigui<strong>en</strong>te; /* Nodo ci eliminar */<br />

if (*Lc == (*Lc)->siyui<strong>en</strong>t.e) /* List-ii con un solo nodo */<br />

*Lc = NULL;<br />

else i<br />

if (p == *Le)<br />

i<br />

*Lc = actual; /* Se borra el elem<strong>en</strong>to refer<strong>en</strong>ciado por Lc:;<br />

el nuevo acceso sigui<strong>en</strong>t e : p-~>siyuicnte;<br />

void recorrer-(Nodo" Lc)<br />

Nodo* p;<br />

if (Lc != NULL)<br />

{


Listas <strong>en</strong>lazadas 467<br />

p = Lc->sigui<strong>en</strong>te; /* Lc ti<strong>en</strong>e el último nodo, el sigui<strong>en</strong>te es<br />

el primero que se insertó */<br />

do {<br />

print f ( I' \ t \ t % s I' , p - >dato ) ;<br />

p = p->sigui<strong>en</strong>te;<br />

}while(p != Lc->sigui<strong>en</strong>te);<br />

else<br />

printf ("\n\t Lista vacía. \n") ;<br />

void borrarlista(Nodo** L,c)<br />

I<br />

Nodo* p;<br />

if (Lc != NULL)<br />

{<br />

p = *Lc;<br />

do I<br />

Nodo* t ;<br />

t = p; p = p->sigui<strong>en</strong>te;<br />

free(t);<br />

}while(p ! = *Lc);<br />

1<br />

else<br />

printf ("\n\t Lista vacía. \n")<br />

*Lc = NULL;<br />

;<br />

14.6. RESUMEN<br />

La <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> lista se pue<strong>de</strong> implem<strong>en</strong>tar,<br />

bi<strong>en</strong> como un array, bi<strong>en</strong> como una lista <strong>en</strong>lazada.<br />

Una lista <strong>en</strong>lazada es una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> dinámica<br />

<strong>en</strong> la que sus compon<strong>en</strong>tes están ord<strong>en</strong>ados lógicam<strong>en</strong>te<br />

por sus campos punteros <strong>en</strong> vez <strong>de</strong> ord<strong>en</strong>ados<br />

físicam<strong>en</strong>te como están <strong>en</strong> un array. El final <strong>de</strong> la<br />

lista se señala mediante una constante o puntero especial<br />

llamado NULL.<br />

La gran v<strong>en</strong>taja <strong>de</strong> una lista <strong>en</strong>lazada sobre un<br />

array es que la lista <strong>en</strong>lazada pue<strong>de</strong> crecer y <strong>de</strong>crecer<br />

<strong>en</strong> tamaño, ajustándose al número <strong>de</strong> elem<strong>en</strong>tos.<br />

Una lista simplem<strong>en</strong>te <strong>en</strong>lazada conti<strong>en</strong>e sólo un<br />

<strong>en</strong>lace a un sucesor único, a m<strong>en</strong>os que sea el último,<br />

<strong>en</strong> cuyo caso no se <strong>en</strong>laza con ningún otro nodo.<br />

Cuando se inserta un elem<strong>en</strong>to <strong>en</strong> una lista <strong>en</strong>lazada,<br />

se <strong>de</strong>b<strong>en</strong> consi<strong>de</strong>rar cuatro casos: añadir a una lista<br />

vacía, añadir al principio <strong>de</strong> la lista, añadir <strong>en</strong> el<br />

interior y añadir al final <strong>de</strong> la lista.<br />

Para borrar un elem<strong>en</strong>to, primero hay que buscar<br />

el nodo que lo conti<strong>en</strong>e y consi<strong>de</strong>rar dos casos: borrar<br />

el primer nodo y borrar cualquier otro <strong>de</strong> la lista.<br />

El recorrido <strong>de</strong> una lista <strong>en</strong>lazada significa pasar<br />

por cada nodo (visitar) y procesarlo. El proceso pue<strong>de</strong><br />

ser escribir su cont<strong>en</strong>ido, modificar el campo <strong>de</strong><br />

<strong>datos</strong>.<br />

Una lista doblem<strong>en</strong>te <strong>en</strong>lazada es aquella <strong>en</strong> la<br />

que cada nodo ti<strong>en</strong>e un puntero a su sucesor y otro a<br />

su pre<strong>de</strong>cesor.<br />

Las listas doblem<strong>en</strong>te <strong>en</strong>lazadas se pued<strong>en</strong> recorrer<br />

<strong>en</strong> ambos s<strong>en</strong>tidos. Las operaciones básicas son<br />

inserción, borrado y recorrer la lista; similares a las<br />

listas simples.<br />

Una lista <strong>en</strong>lazada circulam<strong>en</strong>te por propia<br />

naturaleza no ti<strong>en</strong>e primero ni Último nodo. Las listas<br />

circulares pued<strong>en</strong> ser <strong>de</strong> <strong>en</strong>lace simple o doble.


~<br />

1<br />

468 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

14.7. EJERCICIOS<br />

14.1. Escribir una función que <strong>de</strong>vuelva cierto (# O)<br />

si la lista está vacía.<br />

14.2. Escribir una función <strong>en</strong>tera que <strong>de</strong>vuelva el<br />

número <strong>de</strong> nodos <strong>de</strong> una lista <strong>en</strong>lazada.<br />

14.3. En una lista <strong>en</strong>lazada <strong>de</strong> números <strong>en</strong>teros se<br />

<strong>de</strong>sea añadir un nodo <strong>en</strong>tre dos nodos consecutivos<br />

con campos dato <strong>de</strong> distinto signo; el<br />

valor <strong>de</strong>l campo dato <strong>de</strong>l nuevo nodo que sea la<br />

difer<strong>en</strong>cia <strong>en</strong> valor absoluto.<br />

14.4. Escribir una función que elimine el nodo que<br />

ocupa la posición i, si<strong>en</strong>do el nodo cabecera el<br />

que ocupa la posición O.<br />

14.5. Escribir una función que t<strong>en</strong>ga como argum<strong>en</strong>to<br />

el puntero cabeza ai primer nodo <strong>de</strong> una<br />

lista <strong>en</strong>lazada. La función <strong>de</strong>be <strong>de</strong> <strong>de</strong>volver un<br />

puntero a una lista doble con los mismos campos<br />

dato pero <strong>en</strong> ord<strong>en</strong> inverso.<br />

14.6. Se ti<strong>en</strong>e una lista simplem<strong>en</strong>te <strong>en</strong>lazada <strong>de</strong><br />

números reales. Escribir una función para obt<strong>en</strong>er<br />

una lista doble ord<strong>en</strong>ada respecto al campo<br />

dato, con los valores reales <strong>de</strong> la lista simple.<br />

14.7. Escribir una función para crear una lista doblem<strong>en</strong>te<br />

<strong>en</strong>lazada <strong>de</strong> palabras introducidas por<br />

teclado. La función <strong>de</strong>be t<strong>en</strong>er un argum<strong>en</strong>to<br />

puntero Ld <strong>en</strong> el que se <strong>de</strong>vuelva la dirección<br />

<strong>de</strong>l nodo que está <strong>en</strong> la posición intermedia.<br />

14.8. Se ti<strong>en</strong>e que Lc es una lista circular <strong>de</strong> palabras.<br />

Escribir una función que cu<strong>en</strong>te el<br />

número <strong>de</strong> veces que una palabra dada se<br />

<strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> la lista.<br />

14.9. Escribir una función <strong>en</strong>tera que t<strong>en</strong>ga como<br />

argum<strong>en</strong>to una lista circular <strong>de</strong> números <strong>en</strong>teros.<br />

La función <strong>de</strong>be <strong>de</strong> <strong>de</strong>volver el dato <strong>de</strong>l<br />

nodo con mayor valor.<br />

14.10. Se ti<strong>en</strong>e una lista <strong>de</strong> simple <strong>en</strong>lace, el campo<br />

dato es un registro (<strong>estructura</strong>) con los campos<br />

<strong>de</strong> un alumno: nombre, edad, sexo. Escribir<br />

una función para transformar la lista <strong>de</strong> tal<br />

forma que si el primer nodo es <strong>de</strong> un alumno<br />

<strong>de</strong> sexo masculino el sigui<strong>en</strong>te sea <strong>de</strong> sexo<br />

fem<strong>en</strong>ino.<br />

14.11. Una lista circular <strong>de</strong> cad<strong>en</strong>as está ord<strong>en</strong>ada<br />

alfabéticam<strong>en</strong>te. El puntero Lc ti<strong>en</strong>e la dirección<br />

<strong>de</strong>l nodo alfabéticam<strong>en</strong>te mayor, apunta<br />

al nodo alfabéticam<strong>en</strong>te m<strong>en</strong>or. Escribir una<br />

función para añadir una nueva palabra, <strong>en</strong> el<br />

ord<strong>en</strong> que le corresponda, a la lista.<br />

14.12. Dada la lista <strong>de</strong>l Ejercicio 14.11 escribir una<br />

función que elimine una palabra dada.<br />

14.8. PROBLEMAS<br />

14.1. Escribir un programa o funciones individuales<br />

que realic<strong>en</strong> las sigui<strong>en</strong>tes tareas:<br />

o Crear una lista <strong>en</strong>lazada <strong>de</strong> números <strong>en</strong>teros<br />

positivos al azar, la inserción se realiza por<br />

el último nodo.<br />

O<br />

Recorrer la lista para mostrar los elem<strong>en</strong>tos<br />

por pantalla.<br />

o Eliminar todos los nodos que super<strong>en</strong> un<br />

valor dado.<br />

14.2. Se ti<strong>en</strong>e un archivo <strong>de</strong> texto <strong>de</strong> palabras separadas<br />

por un blanco o el carácter <strong>de</strong> tin <strong>de</strong> línea.<br />

Escribir un programa para formar una lista<br />

<strong>en</strong>lazada con las palabras <strong>de</strong>l archivo. Una vez<br />

formada la lista se pued<strong>en</strong> añadir nuevas palabras<br />

o borrar alguna <strong>de</strong> ellas. AI finalizar el programa<br />

escribir las palabras <strong>de</strong> la lista <strong>en</strong> el<br />

archivo.


Listas <strong>en</strong>lazadas 469<br />

143. Un polinomio se pue<strong>de</strong> repres<strong>en</strong>tar como una<br />

lista <strong>en</strong>lazada. El primer nodo <strong>de</strong> la lista repres<strong>en</strong>ta<br />

el primer término <strong>de</strong>l polinomio, el<br />

segundo nodo al segundo término <strong>de</strong>l polinomio<br />

y así sucesivam<strong>en</strong>te. Cada nodo ti<strong>en</strong>e<br />

como campo dato el coefici<strong>en</strong>te <strong>de</strong>l término y<br />

el expon<strong>en</strong>te.<br />

Por ejemplo, el polinomio 3x4 - 4x2 + 11<br />

se repres<strong>en</strong>ta<br />

11<br />

o<br />

Escribir un programa que permita dar<br />

<strong>en</strong>trada a polinomios <strong>en</strong> x, repres<strong>en</strong>tándolos<br />

con una lista <strong>en</strong>lazada simple. A continuación<br />

obt<strong>en</strong>er una tabla <strong>de</strong> valores <strong>de</strong>l polinomio<br />

para valores <strong>de</strong> x = 0.0,0.5, 1.0, 1.5, ... ,5.0.<br />

14.4. T<strong>en</strong>i<strong>en</strong>do <strong>en</strong> cu<strong>en</strong>ta la repres<strong>en</strong>tación <strong>de</strong> un<br />

polinomio propuesta <strong>en</strong> el Problema 14.3,<br />

hacer los cambios necesarios para que la lista<br />

<strong>en</strong>lazada sea circular. El puntero <strong>de</strong> acceso<br />

<strong>de</strong>be <strong>de</strong> t<strong>en</strong>er la dirección <strong>de</strong>l Último término<br />

<strong>de</strong>l polinomio, el cual apuntará al primer término.<br />

14.5. Según la repres<strong>en</strong>tación <strong>de</strong> un polinomio propuesta<br />

<strong>en</strong> el Problema 14.4, escibir un programa<br />

para realizar las sigui<strong>en</strong>tes operaciones:<br />

o Obt<strong>en</strong>er la lista circular suma <strong>de</strong> dos polinomios.<br />

o Obt<strong>en</strong>er el polinomio <strong>de</strong>rivada.<br />

o Obt<strong>en</strong>er una lista circular que sea el producto<br />

<strong>de</strong> dos polinomios.<br />

14.6. Escribir un programa para obt<strong>en</strong>er una lista<br />

doblem<strong>en</strong>te <strong>en</strong>lazada con los caracteres <strong>de</strong> una<br />

cad<strong>en</strong>a leída <strong>de</strong>s<strong>de</strong> el teclado. Cada nodo <strong>de</strong> la<br />

lista t<strong>en</strong>drá un carácter.<br />

Una vez que se ti<strong>en</strong>e la lista ord<strong>en</strong>arla alfabéticam<strong>en</strong>te<br />

y escribirla por pantalla.<br />

14.7. Un conjunto es una secu<strong>en</strong>cia <strong>de</strong> elem<strong>en</strong>tos<br />

todos <strong>de</strong>l mismo tipo, sin duplicida<strong>de</strong>s. Escribir<br />

un programa para repres<strong>en</strong>tar un conjunto<br />

<strong>de</strong> <strong>en</strong>teros mediante una lista <strong>en</strong>lazada. El programa<br />

<strong>de</strong>be contemplar las operaciones:<br />

o Cardinal <strong>de</strong>l conjunto.<br />

o Pert<strong>en</strong><strong>en</strong>cia <strong>de</strong> un elem<strong>en</strong>to al conjunto.<br />

o Añadir un elem<strong>en</strong>to al conjunto.<br />

o Escribir <strong>en</strong> pantalla los elem<strong>en</strong>tos <strong>de</strong>l conjunto.<br />

14.8. Con la repres<strong>en</strong>tación propuesta <strong>en</strong> el Problema<br />

14.7, añadir las operaciones básicas <strong>de</strong><br />

conjuntos:<br />

o Unión <strong>de</strong> dos conjuntos.<br />

o Intersección <strong>de</strong> dos conjuntos.<br />

o Difer<strong>en</strong>cia <strong>de</strong> dos conjuntos.<br />

o Inclusión <strong>de</strong> un conjunto <strong>en</strong> otro.<br />

14.9. Escribir un programa <strong>en</strong> el que dados dos<br />

archivos F1, F2 formados por palabras separadas<br />

por un blanco o tin <strong>de</strong> línea, se cre<strong>en</strong><br />

dos conjuntos con las palabras <strong>de</strong> F1 y F2,<br />

respectivam<strong>en</strong>te. Posteriorm<strong>en</strong>te <strong>en</strong>contrar las<br />

palabras comunes y mostrarías por pantalla.<br />

14.10. Utilizar una lista doblem<strong>en</strong>te <strong>en</strong>lazada para<br />

controlar una lista <strong>de</strong> pasajeros <strong>de</strong> una línea<br />

aérea . El programa principal <strong>de</strong>be ser controlado<br />

por m<strong>en</strong>ú y permitir al usuario visualizar<br />

íos <strong>datos</strong> <strong>de</strong> un pasajero <strong>de</strong>terminado, insertar<br />

un nodo (siempre por el final), eliminar un<br />

pasajero <strong>de</strong> la lista. A la lista se acce<strong>de</strong> por un<br />

puntero ai primer nodo y otro al último nodo.<br />

14.11. Para repres<strong>en</strong>tar un <strong>en</strong>tero largo, <strong>de</strong> más <strong>de</strong> 30<br />

dígitos, utilizar una lista circular t<strong>en</strong>i<strong>en</strong>do el<br />

campo dato <strong>de</strong> cada nodo un dígito <strong>de</strong>l <strong>en</strong>tero<br />

largo. Escribir un programa <strong>en</strong> el que se introduzcan<br />

dos <strong>en</strong>teros largos y se obt<strong>en</strong>ga su<br />

suma.<br />

14.12. Un vector disperso es aquel que ti<strong>en</strong>e muchos<br />

elem<strong>en</strong>tos que son cero. Escribir un programa<br />

que permita repres<strong>en</strong>tar mediante listas <strong>en</strong>lazadas<br />

un vector disperso. Los nodos <strong>de</strong> la lista<br />

son los elem<strong>en</strong>tos <strong>de</strong> la lista distintos <strong>de</strong><br />

cero; <strong>en</strong> cada nodo se repres<strong>en</strong>ta el valor <strong>de</strong>l<br />

elem<strong>en</strong>to y el índice (posición <strong>de</strong>l vector). El<br />

programa ha <strong>de</strong> realizar las operaciones:<br />

sumar dos vectores <strong>de</strong> igual dim<strong>en</strong>sión y<br />

hallar el producto escalar.


CAPíTULO 15<br />

PILAS Y COLAS<br />

CONTENIDO<br />

15.1. Concepto <strong>de</strong> pila.<br />

15.a. El tipo pila implem<strong>en</strong>tado<br />

con arrays.<br />

15.3. Concepto <strong>de</strong> cola.<br />

15.4. Colas implem<strong>en</strong>tadas con<br />

arrays.<br />

15.5. RealbaciÓn <strong>de</strong> una cola con<br />

una lista <strong>en</strong>lazada.<br />

15.6. Resum<strong>en</strong>.<br />

15.7. Ejercicios.<br />

15.8. Problemas.<br />

3<br />

470


En este capítulo se estudian <strong>en</strong> <strong>de</strong>talle las <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> pilas y colas gue<br />

son probablem<strong>en</strong>te las utilizadas mas frecu<strong>en</strong>tem<strong>en</strong>te <strong>en</strong> los programas más<br />

usuales. Son <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong> que almac<strong>en</strong>an y recuperan sus elem<strong>en</strong>tos<br />

at<strong>en</strong>di<strong>en</strong>do a un estricto ord<strong>en</strong>. Las pilas se conoc<strong>en</strong> también como <strong>estructura</strong>s<br />

LIFO (Last-in, first-out, último <strong>en</strong> <strong>en</strong>trar-primero <strong>en</strong> salir) y las colas como<br />

<strong>estructura</strong>s FIFO (nirSt-in, First-out, primero <strong>en</strong> <strong>en</strong>trm-primero <strong>en</strong> salir). Entre<br />

las numerosas aplicaciones <strong>de</strong> las pilas <strong>de</strong>staca la evaluación <strong>de</strong> expresiones<br />

algebraicas, así como la organización <strong>de</strong> la memoria. Las colas ti<strong>en</strong><strong>en</strong> numerosas<br />

aplicaciones <strong>en</strong> el mundo <strong>de</strong> la computación: colas <strong>de</strong> m<strong>en</strong>sajes, colas <strong>de</strong><br />

tareas a realizar por una impresora, colas <strong>de</strong> priorida<strong>de</strong>s.<br />

CONCEPTOS CLAVE<br />

Concepto <strong>de</strong> tipo abstracto <strong>de</strong><br />

<strong>datos</strong>.<br />

Concepto <strong>de</strong> una cola.<br />

Concepto <strong>de</strong> una pila.<br />

Listas <strong>en</strong>lazadas.<br />

47 1


472 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

15.1. CONCEPTO DE PILA<br />

Una pila (stack) es una colección ord<strong>en</strong>ada <strong>de</strong> elem<strong>en</strong>tos a los que sólo se pue<strong>de</strong> acce<strong>de</strong>r por un único<br />

lugar o extremo <strong>de</strong> la pila. Los elem<strong>en</strong>tos <strong>de</strong> la pila se añad<strong>en</strong> o quitan (borran) <strong>de</strong> la misma sólo por su<br />

parte superior (cima) <strong>de</strong> la pila. Éste es el caso <strong>de</strong> una pila <strong>de</strong> platos, una pila <strong>de</strong> libros, etc.<br />

<strong>de</strong> <strong>en</strong>tradas ord<strong>en</strong>adas tales que<br />

cima.<br />

Cuando se dice que la pila está ord<strong>en</strong>ada, lo que se quiere <strong>de</strong>cir es que hay un elem<strong>en</strong>to al que se<br />

pue<strong>de</strong> acce<strong>de</strong>r primero (el que está <strong>en</strong>cima <strong>de</strong> la pila), otro elem<strong>en</strong>to al que se pue<strong>de</strong> acce<strong>de</strong>r <strong>en</strong> segundo<br />

lugar (justo el elem<strong>en</strong>to que está <strong>de</strong>bajo <strong>de</strong> la cima), un tercero, etc. No se requiere que las <strong>en</strong>tradas se<br />

puedan comparar utilizando el operador «m<strong>en</strong>or que» (


Pilas ycolas 473<br />

La operación Insertar (push) sitúa un elem<strong>en</strong>to dato <strong>en</strong> la cima <strong>de</strong> la pila y Quitar (pop)<br />

elimina o quita el elem<strong>en</strong>to <strong>de</strong> la pila.<br />

Insertar I<br />

1 Quitar<br />

Cima ---Y<br />

Figura 15.3. Operaciones básicas <strong>de</strong> una pila.<br />

+- Fondo<br />

La pila se pue<strong>de</strong> implem<strong>en</strong>tar mediante arrays <strong>en</strong> cuyo caso su dim<strong>en</strong>sión o longitud es fija, y<br />

mediante punteros o listas <strong>en</strong>lazadas <strong>en</strong> cuyo caso se utiliza memoria dinámica y no existe limitación <strong>en</strong><br />

su tamaño.<br />

Una pila pue<strong>de</strong> estar vacía (no ti<strong>en</strong>e elem<strong>en</strong>tos) o ll<strong>en</strong>a (<strong>en</strong> el caso <strong>de</strong> t<strong>en</strong>er tamaño fijo, si no cabe<br />

más elem<strong>en</strong>tos <strong>en</strong> la pila). Si un programa int<strong>en</strong>ta sacar un elem<strong>en</strong>to <strong>de</strong> una pila vacía, se producirá un<br />

error <strong>de</strong>bido a que esa operación es imposible; esta situación se d<strong>en</strong>omina <strong>de</strong>sbordami<strong>en</strong>to negativo<br />

(un<strong>de</strong>rflow). Por el contrario, si un programa int<strong>en</strong>ta poner un elem<strong>en</strong>to <strong>en</strong> una pila se produce un error<br />

llamado <strong>de</strong>sbordami<strong>en</strong>to (overflow) o rehosami<strong>en</strong>to. Pata evitar estas situaciones se diseña funciones,<br />

que comprueban si la pila está ll<strong>en</strong>a o vacía.<br />

15.1.1. Especificaciones <strong>de</strong> una pila<br />

Las operaciones que sirv<strong>en</strong> para <strong>de</strong>finir una pila y po<strong>de</strong>r manipular su cont<strong>en</strong>ido son las sigui<strong>en</strong>tes (no<br />

todas ellas se implem<strong>en</strong>tan al <strong>de</strong>finir una pila).<br />

Tipo <strong>de</strong> dato<br />

Insertar (push)<br />

Quitar (pop)<br />

Pila vacía<br />

Pila ll<strong>en</strong>a<br />

Limpiar pila<br />

Tamaño <strong>de</strong> la pila<br />

Cima<br />

Dato que se almac<strong>en</strong>a <strong>en</strong> la pila.<br />

Insertar un dato <strong>en</strong> la pila.<br />

Sacar (quitar) un dato <strong>de</strong> la pila.<br />

Comprobar si la pila no ti<strong>en</strong>e elem<strong>en</strong>tos.<br />

Comprobar si la pila está ll<strong>en</strong>a <strong>de</strong> elem<strong>en</strong>tos.<br />

Quitar todos sus elem<strong>en</strong>tos y <strong>de</strong>jar la pila vacía.<br />

Número <strong>de</strong> elem<strong>en</strong>tos máximo que pue<strong>de</strong> cont<strong>en</strong>er la pila.<br />

Obti<strong>en</strong>e el elem<strong>en</strong>to cima <strong>de</strong> la pila.<br />

15.2. EL TIPO PILA IMPLEMENTADO CON ARRAYS<br />

Una pila se pue<strong>de</strong> implem<strong>en</strong>tar mediante armys o mediante listas <strong>en</strong>lazadas. Una implem<strong>en</strong>tación<br />

estática se realiza utilizando un array <strong>de</strong> tamaño fijo y una implem<strong>en</strong>tación dinámica mediante una lista<br />

<strong>en</strong>lazada.


474 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

En C para <strong>de</strong>finir una pila con arrays se utiliza una <strong>estructura</strong>. Los miembros <strong>de</strong> la <strong>estructura</strong> pila<br />

incluy<strong>en</strong> una lista (array) y un índice o puntero a la cima <strong>de</strong> la pila; a<strong>de</strong>más una constante con el máximo<br />

número <strong>de</strong> elem<strong>en</strong>tos. El tipo pila junto al conjunto <strong>de</strong> operaciones <strong>de</strong> la pila se pued<strong>en</strong> <strong>en</strong>cerrar <strong>en</strong> un<br />

archivo <strong>de</strong> inclusión (pi la. h) . AI utilizar un array para cont<strong>en</strong>er los elem<strong>en</strong>tos <strong>de</strong> la pila hay que t<strong>en</strong>er<br />

<strong>en</strong> cu<strong>en</strong>ta que el tamaño <strong>de</strong> la pila no pue<strong>de</strong> exce<strong>de</strong>r el número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong>l array y la condición pila<br />

ll<strong>en</strong>a será significativa para el diseño.<br />

El método usual <strong>de</strong> introducir elem<strong>en</strong>tos <strong>en</strong> una pila es <strong>de</strong>finir elfondo <strong>de</strong> la pila <strong>en</strong> la posición O <strong>de</strong>l<br />

array y sin ningún elem<strong>en</strong>to <strong>en</strong> su interior, es <strong>de</strong>cir, <strong>de</strong>finir una pila vacia; a continuación, se van<br />

introduci<strong>en</strong>do elem<strong>en</strong>tos <strong>en</strong> el array (<strong>en</strong> la pila) <strong>de</strong> modo que el primer elem<strong>en</strong>to añadido se introduce<br />

<strong>en</strong> una pila vacía y <strong>en</strong> la posición O, el segundo elem<strong>en</strong>to <strong>en</strong> la posición 1, el sigui<strong>en</strong>te <strong>en</strong> la posición 2<br />

y así sucesivam<strong>en</strong>te. Con estas operaciones el puntero (apuntador) que apunta a la cima <strong>de</strong> la pila se va<br />

increm<strong>en</strong>tando <strong>en</strong> 1 cada vez que se aña<strong>de</strong> un nuevo elem<strong>en</strong>to; es <strong>de</strong>cir, el puntero <strong>de</strong> la pila almac<strong>en</strong>a<br />

el índice <strong>de</strong>l array que se está utilizando como cima <strong>de</strong> la pila. Los <strong>algoritmos</strong> <strong>de</strong> introducir «insertar»<br />

@ush) y quitar «sacar» (pop) <strong>datos</strong> <strong>de</strong> la pila utilizan el índice <strong>de</strong>l array como puntero <strong>de</strong> la pila son:<br />

Insertar (push)<br />

1.verificar si la pila no está ll<strong>en</strong>a.<br />

2.Increm<strong>en</strong>tar <strong>en</strong> 1 el puntero <strong>de</strong> la pila.<br />

?.Almac<strong>en</strong>ar elem<strong>en</strong>to <strong>en</strong> la posición <strong>de</strong>l puntero <strong>de</strong> la pila.<br />

Quitar (pop)<br />

1.si la pila no está vacía.<br />

2.Leer el elem<strong>en</strong>to <strong>de</strong> la posición <strong>de</strong>l puntero <strong>de</strong> la pila.<br />

3.Decrem<strong>en</strong>tar <strong>en</strong> 1 el puntero <strong>de</strong> la pila.<br />

En el caso <strong>de</strong> que el array que <strong>de</strong>fine la pila t<strong>en</strong>ga TamanioPila elem<strong>en</strong>tos, las posiciones <strong>de</strong>l<br />

array, es <strong>de</strong>cir, el índice o puntero <strong>de</strong> la pila, estarán compr<strong>en</strong>didas <strong>en</strong> el rango O a TamanioPila-1<br />

elem<strong>en</strong>tos, <strong>de</strong> modo que <strong>en</strong> una pila ll<strong>en</strong>a el puntero <strong>de</strong> la pila apunta a TamanioPila- 1 y <strong>en</strong> una pila<br />

vaciu el puntero <strong>de</strong> la pila apunta a - 1, ya que O, teóricam<strong>en</strong>te, será el índice <strong>de</strong>l primer elem<strong>en</strong>to.<br />

Ejemplo 15.1<br />

Una pila <strong>de</strong> 7 elem<strong>en</strong>tos se pue<strong>de</strong> repres<strong>en</strong>tar gráfi~~am<strong>en</strong>te así:<br />

1<br />

Cima<br />

A 0 1 2 3 4 5 6 A<br />

Pila vacía<br />

puntero <strong>de</strong> la pila = -1<br />

t<br />

puntero <strong>de</strong> la pila<br />

Pila ll<strong>en</strong>a<br />

puntero <strong>de</strong> la pila = 6<br />

Si se almac<strong>en</strong>an los <strong>datos</strong> A, B , c , ... <strong>en</strong> la pila se pue<strong>de</strong> repres<strong>en</strong>tar gráficam<strong>en</strong>te por alguno <strong>de</strong> estos<br />

métodos


Pilas y coias 475<br />

*<br />

Cima = 2 A H (' ...<br />

lndice<br />

*<br />

Veamos ahora como queda la pila <strong>en</strong> función <strong>de</strong> difer<strong>en</strong>tes situaciones <strong>de</strong> un posible programa.<br />

15.2.1. Especificación <strong>de</strong>l tipo pila<br />

La <strong>de</strong>claración <strong>de</strong> una pila incluye los <strong>datos</strong> y operaciones ya citados anteriorm<strong>en</strong>te.<br />

1. Datos <strong>de</strong> la pila (tipo Ti pouat-a, que es conv<strong>en</strong>i<strong>en</strong>te <strong>de</strong>finirlo mediante type<strong>de</strong>f).<br />

2. Verificar que la pila no está ll<strong>en</strong>a antes <strong>de</strong> int<strong>en</strong>tar insertar o poner (


476 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Decluración<br />

/* archivo pi1asrray.h */<br />

#iriclu<strong>de</strong> ~stdio.h><br />

# inclu<strong>de</strong> ictdl i b. h><br />

#dcf I ne Mdx'l'dmaPild 1 O0<br />

type<strong>de</strong>f struct<br />

I<br />

*I<br />

' ,<br />

int r'ilaVcicia(PilLi PI;<br />

int. Pilciill<strong>en</strong>a(Pila P);<br />

Antes <strong>de</strong> incluir el archivo pi 1 cid~.rc~y. h <strong>de</strong>be <strong>de</strong> <strong>de</strong>clararse el ';' i pxiL)at-o. Así si se quiere una pila<br />

<strong>de</strong> <strong>en</strong>teros:<br />

type<strong>de</strong> f i n t Ti poila to ;<br />

il i nc lu<strong>de</strong> I'p i I ad r r-ay . 'ri"<br />

En el caso <strong>de</strong> que la pila fuera <strong>de</strong> números complejos:<br />

type<strong>de</strong>f st ruct<br />

IlOdt x,y;<br />

}Tjpo»ato;<br />

# i n c 1 u d e " p i 1 a ci I' I' a y . h "<br />

Ejemplo 15.2<br />

Escribir un programu que nzunipule una<br />

introduzcu un dato <strong>de</strong> tipo <strong>en</strong>tero.<br />

i 1 d <strong>de</strong> <strong>en</strong>teros, COF~ el tipo <strong>de</strong>finido unteriormrnte e<br />

El programa crea una pila <strong>de</strong> números <strong>en</strong>teros, inserta <strong>en</strong> la pila un dato leído <strong>de</strong>l teclado y visualiza el<br />

elem<strong>en</strong>to cimu.<br />

typc<strong>de</strong> f int Ti poDdto ;<br />

# i n c 1 u d e " p i 1 a a r r ay . h I' ;<br />

# i riclu<strong>de</strong> <br />

void main0<br />

i<br />

Pila P;<br />

irit x;<br />

CrearPi La (&P); / * Crea iini.3 p i 1 u vric ía * /<br />

scanf ("%d", hx) ;


Pilas ycoias 477<br />

i<br />

Insertar (hP,x); /* inserta x <strong>en</strong> la pila P */<br />

printf ("Bd\n",Cirna(P));/* visud izd el Último elern<strong>en</strong>to */<br />

/* Elimina el elem<strong>en</strong>t-o cima (x) y <strong>de</strong>ja la pila vacía */<br />

if ( ! Pilavacia (P )<br />

aux = QUiLdr(&P);<br />

printf ("%d \n",aiix);<br />

1,impiari)i ld (&I1) ; /* 1 impia la pild, qucdd .Jdcí> y <strong>de</strong>be terminar el programa.<br />

/* poner un elem<strong>en</strong>to <strong>en</strong> la pila */<br />

void Insertar(Pila* P,const TLpuUiito i-lemerito)<br />

i<br />

/* si la pila est5 llcn,i, Lermirici el progrdmd */<br />

if (P->cima == MaxTarndPi l,i-I)<br />

i<br />

puts ("Desborddmi<strong>en</strong>to pi Id") ;<br />

exit (1);<br />

i<br />

/* increm<strong>en</strong>Ldr puriLero cirnC\ y copidr- elern<strong>en</strong>to <strong>en</strong> I~isLdpila */<br />

P->cima++;<br />

p- > 1 i s t ap i 1 a >c i ma 1 = e 1 em<strong>en</strong> t-o ;<br />

Antes <strong>de</strong> 01 I I ,I I<br />

I I I I I I<br />

I I I elern<strong>en</strong>to I l l<br />

I I I I I I<br />

t<br />

(' i 171,l<br />

Despues <strong>de</strong> ( - 1 1 I 1 I t<br />

=<br />

t<br />

( I I I r 1171 1 I<br />

se <strong>de</strong>vuelve<br />

1 I 1


d<br />

478 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

La operación Quitar- elimina un elem<strong>en</strong>to <strong>de</strong> la pila copiando primero el valor <strong>de</strong> la cima <strong>de</strong> la pila<br />

<strong>en</strong> una variable local aux y a continuación <strong>de</strong>crem<strong>en</strong>ta el puntero <strong>de</strong> la pila <strong>en</strong> 1. La variable aux se<br />

<strong>de</strong>vuelve <strong>en</strong> la ejecución <strong>de</strong> la operación Quitar. Si se int<strong>en</strong>ta eliminar o borrar un elem<strong>en</strong>to <strong>en</strong> una pila<br />

vacía se <strong>de</strong>be producir un m<strong>en</strong>saje <strong>de</strong> error y el programa <strong>de</strong>be terminar.<br />

/* Quitar un elem<strong>en</strong>to <strong>de</strong> la piid */<br />

TipoDato Quitar(Pila* P)<br />

{<br />

1<br />

TipoDato aux;<br />

/* si la pila está vacía, termina el programa */<br />

if (P->cima == -1)<br />

i<br />

puts("Se int<strong>en</strong>ta sacar un elem<strong>en</strong>to <strong>en</strong> pila vacía");<br />

exit (1);<br />

I<br />

/* guardar elem<strong>en</strong>to <strong>de</strong> la cima */<br />

aux = P->listapilaiP->cim~];<br />

/* <strong>de</strong>crem<strong>en</strong>tar cima y <strong>de</strong>volver vulor <strong>de</strong>l elem<strong>en</strong>to */<br />

P->cima--;<br />

return aux;<br />

15.2.3. Operaciones <strong>de</strong> verificación <strong>de</strong>l estado <strong>de</strong> la pila<br />

Se <strong>de</strong>be proteger la integridad <strong>de</strong> la pila, para lo cual el tipo P i 1 a ha <strong>de</strong> proporcionar operaciones que<br />

comprueb<strong>en</strong> el estado <strong>de</strong> la pila: pila vacia o pila ll<strong>en</strong>u. Asimismo se ha <strong>de</strong> <strong>de</strong>finir una operación que<br />

restaure la condición inicial <strong>de</strong> la pila, que fue <strong>de</strong>terminada por el constructor CrearPi la (cima <strong>de</strong> la<br />

pila a -I), Limpiarpila.<br />

La función Pilavacia comprueba (verifica) si la cima <strong>de</strong> la pila es -1. En ese caso, la pila está<br />

vacía y se <strong>de</strong>vuelve un 1 (verda<strong>de</strong>ro); <strong>en</strong> caso contrario, se <strong>de</strong>vuelve O (falso).<br />

/* verificar pila vacía */<br />

int PilaVacid(P11d P)<br />

{ /*<strong>de</strong>vuelve el valor lógico rrsultdnte <strong>de</strong> expresibn cima == -1 */<br />

return P.cima == -1;<br />

i<br />

La función PilaLl<strong>en</strong>a comprueba (verifica) si la cima es MaxTamaPila-I. En ese caso, la pila<br />

está ll<strong>en</strong>a y se <strong>de</strong>vuelve un 1 (verda<strong>de</strong>ro); <strong>en</strong> caso contrario, se <strong>de</strong>vuelve O (falso).<br />

/* verificar si la pila está ll<strong>en</strong>ci */<br />

int PilaLl<strong>en</strong>a (Pila P)<br />

i<br />

/* <strong>de</strong>vuelve valor lbqico <strong>de</strong> Id exprecióri cimd == MaxTdrndPila-1 */<br />

return P.cima == MaxTamaPila-1;<br />

1<br />

Por último la operación Limpiar g'i 1 ~ reinicializa ~ 1 la cima a su valor inicial con la pila vacía (-1).<br />

/* quitar todos los elem<strong>en</strong>tos di? la pi la */<br />

void LimpiarPila(Pila* P)<br />

{<br />

i<br />

P->cima = -1;


Pilas v coias 479<br />

Ejercicio 15.1<br />

Escribir un progranza que utilice la clase iJi 1 d para comprobar si una <strong>de</strong>terminada ,frase/paluhru<br />

(cad<strong>en</strong>a <strong>de</strong> caracteres) es un palíndromo. Nota. Una palabra ofrase es un palíndromo rumio la Irctmm<br />

directa e indirecta <strong>de</strong> la niisma ti<strong>en</strong>e igual valor: alila, es un palíndromo; cara (arac) no es un<br />

palíndromo.<br />

I<br />

I<br />

Análisis<br />

La palabra se lee carácter a carácter, <strong>de</strong> tal forma que a la vez que se aña<strong>de</strong> a un str-inq se inserta <strong>en</strong><br />

una pila <strong>de</strong> caracteres. Una vez leída la palabra, se compara el primer carácter <strong>de</strong>l string con el carácter<br />

que se extrae <strong>de</strong> la pila, si son iguales sigue la comparación con sigui<strong>en</strong>te carácter <strong>de</strong>l string y <strong>de</strong> la<br />

pila; así hasta que la pila se queda vacía o hay un carácter no coincid<strong>en</strong>te.<br />

Al guardar los caracteres <strong>de</strong> la palabra <strong>en</strong> la pila se garantiza que las comparaciones son <strong>en</strong>tre<br />

caracteres que están <strong>en</strong> ord<strong>en</strong> inverso: primero con Último.. .<br />

La codificación consta <strong>de</strong> tres archivos, el archivo pi lar-ray . h con las <strong>de</strong>claraciones <strong>de</strong> la pila; el<br />

archivo pilarray . c con la implem<strong>en</strong>tación <strong>de</strong> las operaciones <strong>de</strong> la pila y el archivo pditir omo. c<br />

para leer la palabra y comprobar con ayuda <strong>de</strong> la pila si es palíndromo.<br />

/* Archivo pi1array.h */<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine MaxTarnaE’ila 100<br />

type<strong>de</strong>f struct<br />

i<br />

TipoDato listapila[MaxTamaPila];<br />

int cima;<br />

} Pila;<br />

/* Operaciones sobre la Pila */<br />

void CrearPila(Pila* P);<br />

void Insertar(P¡la* P,const TipoDat.0 elem<strong>en</strong>to);<br />

TipoDato Quitar(Pila* P);<br />

void LimpiarPila(Pila* P);<br />

/* Operación <strong>de</strong> acceso Pild */<br />

TipoDato Cima(Pi1a P);<br />

/* verificación estado <strong>de</strong> la Pild */<br />

int Pilavacia (Pila il) ;<br />

int PilaLl<strong>en</strong>a(Pi1a P);<br />

/* Archivo pi1array.c<br />

lmplem<strong>en</strong>tación <strong>de</strong> operacioncs sobre pilas<br />

*/<br />

type<strong>de</strong>f char TipoDato;<br />

# i nc I u<strong>de</strong> “pi 1 ar ray . h“<br />

/* Inicializa la pila a pila vdcía */<br />

void CrearPila(Pila* P)<br />

i<br />

P -> cima = -1;<br />

1<br />

/* poner iiri elem<strong>en</strong>to <strong>en</strong> lCi pila */


480 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

void Insertar(Pila* P,const TipoDato elem<strong>en</strong>to)<br />

i<br />

1<br />

/* si la pila está ll<strong>en</strong>a, termina el programa */<br />

if (PilaLl<strong>en</strong>a (*PI)<br />

{<br />

1<br />

puts ( "Desbordami<strong>en</strong>to pila" ) ;<br />

exit (1);<br />

/* increm<strong>en</strong>tar puntero cima y copiar elem<strong>en</strong>to <strong>en</strong> listapila */<br />

P->cima++;<br />

P->listapila[P->cimal = elem<strong>en</strong>to;<br />

/* Quitar un elem<strong>en</strong>to <strong>de</strong> la pila */<br />

TipoDato Quitar (Pila" P)<br />

I<br />

TipoDato Aux;<br />

/* si la pila está vacía, termina el programa */<br />

if (Pilavacia (*E?))<br />

i<br />

puts("Se int<strong>en</strong>ta sacar un elem<strong>en</strong>to <strong>en</strong> pila vacía");<br />

exit (1);<br />

1<br />

I<br />

/* guardar elem<strong>en</strong>to <strong>de</strong> la cima */<br />

Aux = P->listapila[P->cima];<br />

/* <strong>de</strong>crem<strong>en</strong>tar cima y <strong>de</strong>volver valor <strong>de</strong>l elem<strong>en</strong>to */<br />

P->cima--;<br />

return Aux;<br />

/* verificar pila vacía */<br />

int Pilavacia (Pila P)<br />

{ /*<strong>de</strong>vuelve el valor lógico resultante <strong>de</strong> expresión cima == -1 */<br />

return P.cima == -1;<br />

1<br />

/* verificar si la pila está ll<strong>en</strong>a */<br />

int PilaLl<strong>en</strong>a (Pila P)<br />

i<br />

return P.cima == MaxTamaPila-1;<br />

1<br />

/* quitar todos los elem<strong>en</strong>tos <strong>de</strong> la pila */<br />

void LimpiarPila(Pila* P)<br />

i<br />

P->cima = -1;<br />

1<br />

TipoDato Cima (Pila P)<br />

i<br />

if (P.cima == -1)<br />

i<br />

puts("Se int<strong>en</strong>ta sacar un elem<strong>en</strong>to <strong>en</strong> pila vacía");<br />

exit (1);<br />

1<br />

return P.listapila[P.cim& ;


Pilas y coias 481<br />

I<br />

/* Archivo pa1dromo.c<br />

type<strong>de</strong>f char TipoDato;<br />

#inclu<strong>de</strong> 'pilarray. h"<br />

#inclu<strong>de</strong> <br />

*/<br />

int main( )<br />

i<br />

char palabra[lOOl, ch;<br />

Pila P;<br />

int j, palmo;<br />

Crearpila (&P);<br />

/* Lee la palabra */<br />

do i<br />

puts("\n Palabra a comprobar si es palíndromo");<br />

for (j=O; (ch=getchar())!='\n'; j<br />

I<br />

t<br />

palabra[j++l = ch;<br />

Insertar(&P,ch); /* pone <strong>en</strong> la pila */<br />

I<br />

palabra[jl = '\O';<br />

/* comprueba si es palíndromo */<br />

palmo = 1;<br />

for (j=O; palmo && !PilaVacia(P); )<br />

{<br />

palmo = palabra[j++l == Quitar(&P);<br />

I<br />

LimpiarPila(&P);<br />

if (palmo)<br />

printf("\n La palabra %s es un palíndromo \n",palabra);<br />

else<br />

printf("\n La palabra %s no es un palíndromo \n',palabraj;<br />

printf ("\n¿ Otra palabra ?: "j; scanf ("%c%*c", &chj;<br />

}while (tolower(chj == 's'j;<br />

return O;<br />

15.3.<br />

COLAS<br />

Una cola es una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> que almac<strong>en</strong>a elem<strong>en</strong>tos <strong>en</strong> una lista y permite acce<strong>de</strong>r a los <strong>datos</strong><br />

por uno <strong>de</strong> los dos extremos <strong>de</strong> la lista (Fig. 15.4). Un elem<strong>en</strong>to se inserta <strong>en</strong> la cola (parte final) <strong>de</strong> la<br />

lista y se suprime o elimina por la fr<strong>en</strong>te (parte inicial, cabeza) <strong>de</strong> la lista. Las aplicaciones utilizan una<br />

cola para almac<strong>en</strong>ar elem<strong>en</strong>tos <strong>en</strong> su ord<strong>en</strong> <strong>de</strong> aparición o concurr<strong>en</strong>cia<br />

1" 2" 3" 4" Ultimo<br />

t<br />

Fr<strong>en</strong>te<br />

Figura 15.4. Una cola.<br />

t<br />

Final


482 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Los elem<strong>en</strong>tos se eliminan (se quitan) <strong>de</strong> la cola <strong>en</strong> el mismo ord<strong>en</strong> <strong>en</strong> que se almac<strong>en</strong>an y, por<br />

consigui<strong>en</strong>te, una cola es una <strong>estructura</strong> <strong>de</strong> tipo FIFO (first-iidfirs-out, primero <strong>en</strong> ciitrar//?rimero <strong>en</strong><br />

salir o bi<strong>en</strong> primero <strong>en</strong> llegar/primero <strong>en</strong> ser servido). El servicio <strong>de</strong> at<strong>en</strong>ción a cli<strong>en</strong>tes <strong>en</strong> un almacén<br />

es un ejemplo típico <strong>de</strong> cola. La acción <strong>de</strong> gestión <strong>de</strong> memoria intermedia (hufering) <strong>de</strong> trabajos o tareas<br />

<strong>de</strong> impresora <strong>en</strong> un distribuidor <strong>de</strong> impresoras (spooler) es otro ejemplo típico <strong>de</strong> cola'. Dado que la<br />

impresión es una tarea (un trabajo) que requiere más tiempo que el proceso <strong>de</strong> la transmisión real <strong>de</strong> los<br />

<strong>datos</strong> <strong>de</strong>s<strong>de</strong> la computadora a la impresora, se organiza una cola <strong>de</strong> trabajos <strong>de</strong> modo que los trabajos<br />

se imprim<strong>en</strong> <strong>en</strong> el mismo ord<strong>en</strong> <strong>en</strong> que se recibieron por la impresora. Este sistema ti<strong>en</strong>e el gran<br />

inconv<strong>en</strong>i<strong>en</strong>te <strong>de</strong> que si su trabajo personal consta <strong>de</strong> una Única página para imprimir y <strong>de</strong>lante <strong>de</strong> su<br />

petición <strong>de</strong> impresión existe otra petición para imprimir un informe <strong>de</strong> 300 páginas. <strong>de</strong>berá esperar a la<br />

impresión <strong>de</strong> esas 300 páginas antes <strong>de</strong> que se imprima su página.<br />

Des<strong>de</strong> el punto <strong>de</strong> vista <strong>de</strong> <strong>estructura</strong> <strong>de</strong> <strong>datos</strong>, una cola es similar a una pila, <strong>en</strong> don<strong>de</strong> los <strong>datos</strong> se<br />

almac<strong>en</strong>an <strong>de</strong> un modo lineal y el acceso a los <strong>datos</strong> sólo está permitido <strong>en</strong> los extremos <strong>de</strong> la cola. Las<br />

acciones que están permitidas <strong>en</strong> una cola son:<br />

O<br />

O<br />

O<br />

O<br />

Creación <strong>de</strong> una cola vacía.<br />

Verificación <strong>de</strong> que una cola estd vacía.<br />

Añadir un dato al final <strong>de</strong> una cola.<br />

Eliminación <strong>de</strong> los <strong>datos</strong> <strong>de</strong> la cabeza <strong>de</strong> la cola.<br />

fr<strong>en</strong>te<br />

final<br />

fr<strong>en</strong>te<br />

final<br />

fr<strong>en</strong>te<br />

final<br />

fr<strong>en</strong>te<br />

final<br />

fr<strong>en</strong>te<br />

final<br />

Figura 15.5. Operaciones <strong>de</strong> , 11 >(>: +


Pilas y colas 483<br />

15.4. EL TIPO COLA IMPLEMENTADA CON ARRAYS<br />

AI igual que las pilas, las colas se pued<strong>en</strong> implem<strong>en</strong>tar utilizando arrays o listas <strong>en</strong>lazadas. En esta<br />

sección se consi<strong>de</strong>ra la iinplem<strong>en</strong>tación utilizando arrays.<br />

La <strong>de</strong>finición <strong>de</strong> una cold ha <strong>de</strong> cont<strong>en</strong>er un array para almac<strong>en</strong>ar los elem<strong>en</strong>tos <strong>de</strong> la cola, y dos<br />

marcadores o punteros (variables) que manti<strong>en</strong><strong>en</strong> las posiciones fr<strong>en</strong>te y final <strong>de</strong> la cola ; es <strong>de</strong>cir, un<br />

marcador apuntando a la posición <strong>de</strong> la cabeza <strong>de</strong> la cola y el otro al primer espacio vacío que sigue al<br />

final <strong>de</strong> la cola. Cuando un elem<strong>en</strong>to se aña<strong>de</strong> a la cola, se verifica si el inarcador final apunta a una<br />

posición válida, <strong>en</strong>tonces se aña<strong>de</strong> el elem<strong>en</strong>to a la cola y se increm<strong>en</strong>ta el marcador final <strong>en</strong> 1. Cuando<br />

un elem<strong>en</strong>to se elimina <strong>de</strong> la cola, se hace una prueba para ver si la cola está vacía y, si no es así, se<br />

recupera el elem<strong>en</strong>to <strong>de</strong> la posición apuntada por el marcador (puntero) <strong>de</strong> cabeza y éste se increm<strong>en</strong>ta<br />

<strong>en</strong> 1. Este procedimi<strong>en</strong>to funciona bi<strong>en</strong> hasta la primera vez que el puntero <strong>de</strong> cabeza o cabecera alcanza<br />

el extremo <strong>de</strong>l array y el array queda o bi<strong>en</strong> vacío o bi<strong>en</strong> ll<strong>en</strong>o.<br />

15.4.1. Definición <strong>de</strong> la especificación <strong>de</strong> una cola<br />

Una cola <strong>de</strong>be manejar difer<strong>en</strong>tes tipos <strong>de</strong> <strong>datos</strong>; por esta circunstancia, se <strong>de</strong>fine <strong>en</strong> primer lugar el<br />

tipo g<strong>en</strong>érico TipoDato. La clase Cola conti<strong>en</strong>e una lista (listaQ) cuyo máximo tamaño se <strong>de</strong>termina<br />

por la constante MaxTamQ. Se <strong>de</strong>fin<strong>en</strong> dos tipos <strong>de</strong> variables puntero o marcadores, fr<strong>en</strong>te y f i.nd1.<br />

Éstas son los punteros <strong>de</strong> cabecera y cola o final respectivam<strong>en</strong>te.<br />

Las operaciones típicas <strong>de</strong> la cola son: InsertarQ, EliminarQ, Qvacia, Ql l<strong>en</strong>a, y Fr<strong>en</strong>teu.<br />

InsertarQ toma un elern<strong>en</strong>to <strong>de</strong>l tipo TipoDato y 10 inserta <strong>en</strong> el final <strong>de</strong> la cola. EliminarQ elimina<br />

(quita) y <strong>de</strong>vuelve el elem<strong>en</strong>to <strong>de</strong> la cabeza o fr<strong>en</strong>te <strong>de</strong> la cola. La operación Fr<strong>en</strong>teQ <strong>de</strong>vuelve el valor<br />

<strong>de</strong>l elem<strong>en</strong>to <strong>en</strong> el fr<strong>en</strong>te <strong>de</strong> la cola, sin eliminar el elem<strong>en</strong>to y, por tanto, no modifica la cola.<br />

La operación Qvacia comprueba si la cola está vacía, es necesario esta comprobación antes <strong>de</strong><br />

eliminar un elem<strong>en</strong>to. ll<strong>en</strong>a comprueba si la pila esta ll<strong>en</strong>a, esta comprobación se realiza antes <strong>de</strong><br />

insertar un nuevo miembro. Si las precondiciones para InsertarQ y EliminarQ se violan, el programa<br />

<strong>de</strong>be imprimir un m<strong>en</strong>saje <strong>de</strong> error y terminar.<br />

15.4.2. Especificación <strong>de</strong>l tipo cola<br />

La <strong>de</strong>claración <strong>de</strong>l tipo <strong>de</strong> dato Cola y los prototipos <strong>de</strong> las operaciones <strong>de</strong> la cola se almac<strong>en</strong>a <strong>en</strong> un<br />

archivo <strong>de</strong> cabecera "colaarray. h".<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#<strong>de</strong>fine Max'i'amQ 1 O O<br />

type<strong>de</strong>f struct<br />

i<br />

int fr<strong>en</strong>te;<br />

int final;<br />

TipoDato listaQ[MaxTamQ];<br />

}Cola;<br />

/* Operaciones <strong>de</strong>l tipo <strong>de</strong> <strong>datos</strong> C ola */<br />

/* operaciones <strong>de</strong> modiíicación dc la cola */<br />

void CrearCola(Cola* Q ); /* inicializa la cola como vacid */<br />

void InsertarQ(Cola* Q,TipoDaLo elem<strong>en</strong>to);<br />

TipoDato EliminarQ(Cola* Q );<br />

void Borrarcola (Cola* Q ) ;


v-<br />

,<br />

I<br />

484 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

/* acceso a Id cola */<br />

TipoDato Fr<strong>en</strong>teQ(Co1a Q );<br />

/* métodos <strong>de</strong> verificación <strong>de</strong>l estado <strong>de</strong> la cola */<br />

in1 LongitudQ(Co1a Q) ;<br />

in1 Qvacia(Co1a Q);<br />

int Qll<strong>en</strong>a(Co1a Q);<br />

15.4.3. Implem<strong>en</strong>tación <strong>de</strong>l tipo cola<br />

La <strong>de</strong>claración que se ha hecho <strong>de</strong>l tipo Cola conti<strong>en</strong>e un array para el almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong> los elem<strong>en</strong>tos<br />

<strong>de</strong> la cola y dos marcadores o punteros: uno apuntando a la posición <strong>de</strong> la cabeza o cabecera <strong>de</strong> la cola<br />

y la otra al primer espacio vacío a continuación <strong>de</strong>l final <strong>de</strong> la cola. Cuando un elem<strong>en</strong>to se aña<strong>de</strong> a la<br />

cola, se hace un test (prueba) para ver si el marcador final apunta a una posición válida, a continuación<br />

se aña<strong>de</strong> el elem<strong>en</strong>to a la cola y el marcador final se increm<strong>en</strong>ta <strong>en</strong> 1. Cuando se quita (elimina) un<br />

elem<strong>en</strong>to <strong>de</strong> la cola, se realiza un test (prueba) para ver si la cola está vacía, y si no es así, se recupera<br />

el elem<strong>en</strong>to que se <strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> la posición apuntada por el marcador <strong>de</strong> cabeza y el marcador <strong>de</strong> cabeza<br />

se increm<strong>en</strong>ta <strong>en</strong> 1.<br />

Este procedimi<strong>en</strong>to funciona bi<strong>en</strong> hasta la primera vez que el marcador final alcanza el final <strong>de</strong>l<br />

array. Si durante este tiempo se han producido eliminaciones, habrá espacio vacío al principio <strong>de</strong>l array.<br />

Sin embargo, puesto que el marcador final apunta al extremo <strong>de</strong>l array, implicará que la cola está ll<strong>en</strong>a<br />

y ningún dato más se añadirá. Se pued<strong>en</strong> <strong>de</strong>splazar los <strong>datos</strong> <strong>de</strong> modo que la cabeza <strong>de</strong> la cola vuelve<br />

al principio <strong>de</strong>l array cada vez que esto suce<strong>de</strong>, pero el <strong>de</strong>splazami<strong>en</strong>to <strong>de</strong> <strong>datos</strong> es costoso <strong>en</strong> términos<br />

<strong>de</strong> tiempo <strong>de</strong> computadora, especialm<strong>en</strong>te si los <strong>datos</strong> almac<strong>en</strong>ados <strong>en</strong> el array son <strong>estructura</strong>s <strong>de</strong> <strong>datos</strong><br />

gran<strong>de</strong>s.<br />

El medio más efici<strong>en</strong>te, sin embargo, para almac<strong>en</strong>ar una cola <strong>en</strong> un array, es utilizar un tipo especial<br />

<strong>de</strong> array que junte el extremo final <strong>de</strong> la cola con su extremo cabeza. Tal array se d<strong>en</strong>omina array<br />

circular y permite que el array completo se utilizará para almac<strong>en</strong>ar elem<strong>en</strong>tos <strong>de</strong> la cola sin necesidad<br />

<strong>de</strong> que ningún dato se <strong>de</strong>splace. Un array circular con n elem<strong>en</strong>tos se visualiza <strong>en</strong> la Figura 15.6.<br />

Figura 15.6. Un array circular.<br />

El array se almac<strong>en</strong>a <strong>de</strong> modo natural <strong>en</strong> la memoria tal como un bloque lineal <strong>de</strong> n elem<strong>en</strong>tos. Se<br />

necesitan dos marcadores (punteros) cabeza y,finaZ para indicar la posición <strong>de</strong>l elem<strong>en</strong>to que prece<strong>de</strong> a<br />

la cabeza y la posición <strong>de</strong>l final, don<strong>de</strong> se almac<strong>en</strong>ó el Último elem<strong>en</strong>to añadido. Una cola vacía se<br />

repres<strong>en</strong>ta por la condición cabeza = final.


Pilas ycolas 485<br />

a cabeza<br />

final<br />

Figura 15.7. Una cola vacía.<br />

La variable fr<strong>en</strong>te o cabeza es siempre la posición <strong>de</strong>l elem<strong>en</strong>to que prece<strong>de</strong> al primero <strong>de</strong> la cola y<br />

se avanza <strong>en</strong> el s<strong>en</strong>tido <strong>de</strong> las agujas <strong>de</strong>l reloj. La variable final es la posición <strong>en</strong> don<strong>de</strong> se hizo la<br />

última inserción. Después que se ha producido una inserción, final se mueve circularm<strong>en</strong>te a la<br />

<strong>de</strong>recha. La implem<strong>en</strong>tación <strong>de</strong>l movimi<strong>en</strong>to circular se realiza utilizando la teda <strong>de</strong> los restos:<br />

Mover final a<strong>de</strong>lante -<br />

Mover cabeza a<strong>de</strong>lante -<br />

( final + 1) R MaxTdmQ<br />

(fr<strong>en</strong>te i 1) 'o MdxTamQ<br />

cabeza<br />

Figura 15.8. Una cola que conti<strong>en</strong>e un elem<strong>en</strong>to<br />

Los <strong>algoritmos</strong> que formalizan la gestión <strong>de</strong> colas <strong>en</strong> un array circular han <strong>de</strong> incluir al m<strong>en</strong>os las<br />

sigui<strong>en</strong>tes tareas:<br />

Creación <strong>de</strong> una cola vacía: cabeza = final = O.<br />

Comprobar si una cola está vacía:<br />

escabeza == findl ?<br />

Comprobar si una cola está ll<strong>en</strong>a:<br />

(final + 1) R MaxTamQ == cabeza ?<br />

Añadir un elem<strong>en</strong>to a la cola: si la cola no está ll<strong>en</strong>a, añadir un elem<strong>en</strong>to <strong>en</strong> la posición sigui<strong>en</strong>te<br />

a final y se establece:<br />

final = (final + 1) % MaxTamQ (%operadorresto)<br />

Eliminación <strong>de</strong> un elem<strong>en</strong>to <strong>de</strong> una cola: si la cola no está vacía, eliminarlo <strong>de</strong> la posición<br />

sigui<strong>en</strong>te a cabeza y establecer cabeza = (cabeza + 1) % MaxTamQ.


486 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

15.4.4. Operaciones <strong>de</strong> la cola<br />

Una cola permite un conjunto limitado <strong>de</strong> operaciones, para inicializar la cola, para añadir un nuevo<br />

elem<strong>en</strong>to ( InsertarQ) o quitar/eliminar un elem<strong>en</strong>to (EliminarQ). El tipo Cola proporciona<br />

también f r<strong>en</strong>teQ, que permite «ver» el primer elem<strong>en</strong>to <strong>de</strong> la cola. Para esta implem<strong>en</strong>tación, con<br />

array circular, el tipo cola es el sigui<strong>en</strong>te:<br />

#<strong>de</strong>fine MaxTamQ 100<br />

type<strong>de</strong>f struct<br />

int fr<strong>en</strong>te;<br />

int final;<br />

TipoDato listaQ[MaxTamQ];<br />

}Cola;<br />

Crearcola<br />

La primera operación que se realiza sobre una cola es inicializarla para que a continuación puedan<br />

añadirse elem<strong>en</strong>tos a la cola.<br />

void CrearCola(Cola* Q)<br />

i<br />

Q->fr<strong>en</strong>te = O;<br />

Q->final = O;<br />

1<br />

InsertarQ<br />

Antes <strong>de</strong> que comi<strong>en</strong>ce el proceso <strong>de</strong> inserción, el índice final apunta al Último elem<strong>en</strong>to insertado. El<br />

nuevo elem<strong>en</strong>to se sitúa <strong>en</strong> la posición sigui<strong>en</strong>te. El cálculo <strong>de</strong> las posiciones sucesivas se consigue<br />

mediante el operador resto ( % ) . Después <strong>de</strong> situar el elem<strong>en</strong>to <strong>de</strong> la lista, el índice final se <strong>de</strong>be<br />

actualizar para apuntar <strong>en</strong> la sigui<strong>en</strong>te posición.<br />

/* insertar elem<strong>en</strong>to <strong>en</strong> la cola */<br />

void InsertarQ(Cola* QITipoDato elem<strong>en</strong>to)<br />

{ /* terminar si la cola est& ll<strong>en</strong>a * /<br />

if (Qll<strong>en</strong>a(Q))<br />

i<br />

puts ("<strong>de</strong>sbordami<strong>en</strong>to cold") ;<br />

exit (1);<br />

I<br />

/* asignar elem<strong>en</strong>to d listdQ y actudlizar final */<br />

Q->tinal = (Q->final + 1)& MdxTdmQ;<br />

Q->listdQ [Q->f inall = elem<strong>en</strong>to;<br />

EliminarQ<br />

La operación ~l iminarQ borra o elimina un elem<strong>en</strong>to <strong>de</strong>l fr<strong>en</strong>te <strong>de</strong> la cola, una posición que se refer<strong>en</strong>cia<br />

por el índice f r <strong>en</strong>te. Comi<strong>en</strong>za el proceso <strong>de</strong> eliminación avanzando fr<strong>en</strong>te ya que se estableció<br />

que refer<strong>en</strong>cia al anterior elem<strong>en</strong>to.<br />

fr<strong>en</strong>te = (fr<strong>en</strong>t-e + 1)% MaxTamQ;<br />

En el mo<strong>de</strong>lo circular, la cabeza se <strong>de</strong>be volver a posicionar <strong>en</strong> el sigui<strong>en</strong>te elem<strong>en</strong>to <strong>de</strong> la lista<br />

utilizando el operador resto ( & ) . El código fu<strong>en</strong>te es:<br />

/* borrar elem<strong>en</strong>to <strong>de</strong>l fr<strong>en</strong>te <strong>de</strong> Id cold y <strong>de</strong>vuelve su valor */<br />

TipoDato EliminarQ(Cola* Q)


Pilas ycolas 487<br />

i<br />

J<br />

Tipomto dux;<br />

/*si listaQ está vacía, tcrminar eL programa */<br />

if (Uvacid (I>) )<br />

{<br />

puts("C1iminu.ción <strong>de</strong> unci cold vacía") ;<br />

exit (1);<br />

1<br />

/* dvdnzu.1 fr<strong>en</strong>te y <strong>de</strong>volvcr primero <strong>de</strong>l fr<strong>en</strong>te */<br />

Q->fr<strong>en</strong>te = (Q->tr<strong>en</strong>tc + 1) % Mdx'IdmO;<br />

aux = Q->l~ct~Q[Q->fr<strong>en</strong>tc];<br />

ieturn aux;<br />

Fr<strong>en</strong>teQ<br />

La operación E'r<strong>en</strong>teQ obti<strong>en</strong>e el elem<strong>en</strong>to <strong>de</strong>l fr<strong>en</strong>te <strong>de</strong> la cola, una posición que se refer<strong>en</strong>cia por el<br />

índice fr<strong>en</strong>te.<br />

TipoDato Fr<strong>en</strong>teQ(Co1a Q)<br />

i<br />

Tipohto aux;<br />

/*si la cola est& vcicíci, tprminiir el programa */<br />

if (Qvacia ((1) )<br />

puts ("Elem<strong>en</strong>to €r<strong>en</strong>te <strong>de</strong> und cold vacíu.") ;<br />

exit (1);<br />

Qvacia<br />

Las operaciones que preguntan por el estado <strong>de</strong> la cola pued<strong>en</strong> implem<strong>en</strong>tarse preguntando por los<br />

campos fr<strong>en</strong>te y f inu.1. La operación ovacia. prueba si la cola no ti<strong>en</strong>e elem<strong>en</strong>tos.<br />

int Qvacia(Co1a O)<br />

I<br />

return (Q. fr<strong>en</strong>te == O. f L t ' k i l ) ;<br />

i<br />

Qll<strong>en</strong>a<br />

La operación 01 I <strong>en</strong>d prueba si la cola no pue<strong>de</strong> cont<strong>en</strong>er mas elem<strong>en</strong>tos.<br />

int Qll<strong>en</strong>a(Co1d Q)<br />

i<br />

return (Q.fr<strong>en</strong>te =:<br />

1<br />

(C).fiiiu.l+l)RMax'ramC));<br />

15.5. REALIZACIÓN DE UNA COLA CON UNA LISTA ENLAZADA<br />

La realización <strong>de</strong> una cola mediante una lista <strong>en</strong>lazada permite ajustarse exactam<strong>en</strong>te al número <strong>de</strong><br />

elem<strong>en</strong>tos <strong>de</strong> la cola. Esta implem<strong>en</strong>tación utiliza dos punteros para acce<strong>de</strong>r a la lista. El puntero<br />

Fr<strong>en</strong>te y el puntero i7i nu. 1.


L<br />

I 488 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Fr<strong>en</strong>te<br />

Final<br />

e,<br />

e,<br />

Figura 15.9. Cola con lista <strong>en</strong>lazada (repres<strong>en</strong>tación gráfica típica)<br />

El puntero Fr<strong>en</strong>te refer<strong>en</strong>cia al primer elem<strong>en</strong>to <strong>de</strong> la cola, el primero <strong>en</strong> ser retirado <strong>de</strong> la cola. El<br />

puntero Final refer<strong>en</strong>cia al último elem<strong>en</strong>to <strong>en</strong> ser añadido, el último que será retirado.<br />

Con esta repres<strong>en</strong>tación no ti<strong>en</strong>e s<strong>en</strong>tido la operación que prueba si la cola está ll<strong>en</strong>a. Al ser una<br />

<strong>estructura</strong> dinámica pue<strong>de</strong> crecer y <strong>de</strong>crecer según las necesida<strong>de</strong>s (el límite está <strong>en</strong> la memoria libre <strong>de</strong>l<br />

computador).<br />

15.5.1. Declaración <strong>de</strong>l tipo cola con listas<br />

Para esta repres<strong>en</strong>tación se <strong>de</strong>clara una <strong>estructura</strong> que repres<strong>en</strong>te al nodo <strong>de</strong> la lista <strong>en</strong>lazada, un puntero<br />

a esta <strong>estructura</strong> y la <strong>estructura</strong> cola con los punteros Fr<strong>en</strong>te y Final. Las operaciones son las mismas,<br />

excepto la operación Qll<strong>en</strong>a que no es necesaria al ser una <strong>estructura</strong> dinámica. La <strong>de</strong>claración se<br />

almac<strong>en</strong>a<strong>en</strong>elarchivo cola1ist.h.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

struct nodo<br />

{<br />

TipoDato elem<strong>en</strong>to;<br />

struct nodo* sigui<strong>en</strong>te;<br />

1;<br />

type<strong>de</strong>f struct nodo Nodo;<br />

type<strong>de</strong>f struct<br />

i<br />

Nodo* Fr<strong>en</strong>te;<br />

Nodo* Final;<br />

}Cola;<br />

/* Los prototipos <strong>de</strong> las operaciones */<br />

void CrearCola(Cola* Q); /* Tnicializa la cola como vacía */<br />

void InsertarQ(Cola* Q,Tipo»dto elem<strong>en</strong>to);<br />

TipoDato EliminarQ(Cola* Q);<br />

void BorrarCola(Cola* Q);<br />

/* acceso a la cola */<br />

TipoDato Fr<strong>en</strong>teQ(Co1a Q);<br />

/* métodos <strong>de</strong> verificación <strong>de</strong>l estado <strong>de</strong> la cola */<br />

int Qvacia(Co1a Q );


Pilas y colas 489<br />

15.5.2. Codificación <strong>de</strong> las operaciones <strong>de</strong>l tipo cola con listas<br />

Estas operaciones se van a almac<strong>en</strong>ar <strong>en</strong> el archivo fu<strong>en</strong>te coial i st . c. En primer lugar hay que incluir<br />

el archivo colal i st. h y <strong>de</strong>clarar el tipo <strong>de</strong> dato <strong>de</strong> los elem<strong>en</strong>tos <strong>de</strong> la cola.<br />

La inicialización <strong>de</strong> la cola, al ser una implem<strong>en</strong>tación con punteros, consiste <strong>en</strong> asignar el puntero<br />

nulo a Fr<strong>en</strong>te y ~~ndl. La operación <strong>de</strong> insertar se realiza creando un nuevo nodo (función auxiliar<br />

credrnodo ( ) ) y <strong>en</strong>lazándolo a partir <strong>de</strong>l nodo final. La operación <strong>de</strong> eliminar se realiza sobre el otro<br />

extremo.<br />

Codificación <strong>de</strong> las operaciones.<br />

type<strong>de</strong>f char TipoLUto;<br />

#inclu<strong>de</strong> "colal I st. h"<br />

void CrearCola(Cola* 0)<br />

1<br />

Q->Fr<strong>en</strong>te = Q->Findl = NULL;<br />

i<br />

Nodo* crearnodo (TipoDdto elern<strong>en</strong>to)<br />

i<br />

Nodo" t ;<br />

t = (Nodo*)rnalloc(sizeof(Nodo) 1;<br />

t->elem<strong>en</strong>to = el em<strong>en</strong>to;<br />

t->sigui<strong>en</strong>te = NUT,T,;<br />

return t;<br />

i<br />

int Qvacia(Co1a Q)<br />

i<br />

return (Q.Fr<strong>en</strong>te == NULL);<br />

i<br />

void InsertarQ (Cola* Q,'I'ipoDato elem<strong>en</strong>to)<br />

i<br />

Nodo* a;<br />

a = crearnodo(e¡em<strong>en</strong>to);<br />

if (Qvacia(*Q )<br />

i<br />

Q->Fr<strong>en</strong>te = d;<br />

i<br />

else<br />

i<br />

Q->Final->sigui<strong>en</strong>te = a;<br />

i<br />

Q->Final = a;<br />

1<br />

TipoDato E¡imindrQ(Cola* 0)<br />

i<br />

TipoDato dux;<br />

if (!Qvacia(*Q))<br />

i<br />

Nodo" d;<br />

a = Q->Fr<strong>en</strong>te;<br />

aux = Q->Fr<strong>en</strong>te->elcrn<strong>en</strong>to;<br />

Q->Fr<strong>en</strong>te = Q->Frcnte->siqui<strong>en</strong>te;<br />

free (a) ;


490 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

else /* error: elimiridr dc urid cold vdcía */<br />

I<br />

}<br />

return dux;<br />

'1' .poIlcito I'r<strong>en</strong>teQ (Cola Q)<br />

puts ("Frror: cold vdrídl') ;<br />

ex1t ( 1);<br />

}<br />

ret urn ( Q . Fr <strong>en</strong>t-e - >c 1 e mcri i (3 ) ;<br />

void BorrdrCola (Col a* 0)<br />

i<br />

/* Elimina y libera t.ocios los riodos dc Lci colci */<br />

for (; O->Fr<strong>en</strong>te! =NULL; )<br />

í<br />

Nodo* n;<br />

n = Q->Fr<strong>en</strong>t.e;<br />

0- >Fr<strong>en</strong>te = O->Is'rerite--,s iyii i cnt e;<br />

free ( n) ;<br />

i<br />

}<br />

Ejercicio 15.2<br />

Una vuriacicín <strong>de</strong>1,famoso problema maternútico llarnado «problema <strong>de</strong> José» permite g<strong>en</strong>erar niirneros<br />

<strong>de</strong> la suerte. Se parte <strong>de</strong> una lista inicial <strong>de</strong> n números, c>,sta listu se va reduci<strong>en</strong>do sigui<strong>en</strong>do el sigui<strong>en</strong>te<br />

algoritmo:<br />

1. Se g<strong>en</strong>era un número aleatorio n,.<br />

2. Si PI, > n fin <strong>de</strong>l algoritmo.<br />

3. Si n,


Archivo con el tipo cola y prototipos <strong>de</strong> las operaciones<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

struct nodo<br />

1<br />

TipoDato elem<strong>en</strong>to;<br />

struct nodo* sigui<strong>en</strong>te;<br />

I;<br />

type<strong>de</strong>f struct nodo Nodo;<br />

type<strong>de</strong>f struct<br />

1<br />

Nodo* Fr<strong>en</strong>te;<br />

Nodo* Final;<br />

1 Cola;<br />

/* Los prototipos <strong>de</strong> las operaciones */<br />

void CrearCola(Cola* u ); /* Inicializa la cola como vdcía */<br />

void InsertarQ(Cola* Q,TipoDato elem<strong>en</strong>to);<br />

TipoDato EliminarQ(Cola* Q);<br />

void BorrarCola(Cola* Q);<br />

/* acceso a la cola */<br />

TipoDato Fr<strong>en</strong>teQ ( Cola Q) ;<br />

/* métodos <strong>de</strong> verificación <strong>de</strong>l estado <strong>de</strong> la cola */<br />

int Qvacia(Co1a Q);<br />

Archivo con la implem<strong>en</strong>taciÓn* <strong>de</strong> las operaciones<br />

/* co1alist.c */<br />

type<strong>de</strong>f int TipoDato;<br />

#inclu<strong>de</strong> "colalist .h"<br />

Archivo con el algoritmo para obt<strong>en</strong>er números <strong>de</strong> la suerte<br />

type<strong>de</strong>f int TipoDato;<br />

#inclu<strong>de</strong> "colalist. h"<br />

#inclu<strong>de</strong> <br />

void MostrarCola(Cola* Q);<br />

int main( )<br />

.<br />

i<br />

Cola Q;<br />

int n, nl, n2, n3, i;<br />

randomize ();<br />

/* Número <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong> Id lista */<br />

ri = 1 + random(50);<br />

Crearcola (&Q);<br />

/* Se g<strong>en</strong>eran n números dleatorios */<br />

for (i=l; i


492 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

i<br />

n2 = O; /* Contador <strong>de</strong> elem<strong>en</strong>tos que quedan */<br />

for (i=l; i


Pilas y colas 493<br />

qvac ia , <strong>de</strong>termina si una cola ti<strong>en</strong>e o no elem<strong>en</strong>tos.<br />

Devuelve hue si no ti<strong>en</strong>e elem<strong>en</strong>tos.<br />

ql l<strong>en</strong>a , <strong>de</strong>termina si no se pued<strong>en</strong> almac<strong>en</strong>ar<br />

más elem<strong>en</strong>tos <strong>en</strong> una cola. Se aplica esta operación<br />

cuando se utiliza un array para guardar los elem<strong>en</strong>tos<br />

<strong>de</strong> la cola.<br />

insertarq, aña<strong>de</strong> un nuevo elem<strong>en</strong>to a la cola,<br />

por el extremo final.<br />

f r<strong>en</strong>t eq , <strong>de</strong>vuelve el elem<strong>en</strong>to que está <strong>en</strong> el<br />

extremo fr<strong>en</strong>te sin sacarlo <strong>de</strong> la cola.<br />

* el iminarq, extrae el elem<strong>en</strong>to fr<strong>en</strong>te <strong>de</strong> la cola.<br />

Numerosos mo<strong>de</strong>los <strong>de</strong> sistemas <strong>de</strong>l mundo real<br />

son <strong>de</strong> tipo cola: cola <strong>de</strong> impresión <strong>en</strong> un servidor<br />

<strong>de</strong> impresoras, programas <strong>de</strong> simulación, colas <strong>de</strong><br />

priorida<strong>de</strong>s <strong>en</strong> organización <strong>de</strong> viajes. Una cola es<br />

la <strong>estructura</strong> típica que se suele utilizar como almac<strong>en</strong>ami<strong>en</strong>to<br />

<strong>de</strong> <strong>datos</strong>, cuando se <strong>en</strong>vían <strong>datos</strong> <strong>de</strong>s<strong>de</strong><br />

un compon<strong>en</strong>te rápido <strong>de</strong> una computadora a un<br />

compon<strong>en</strong>te l<strong>en</strong>to @or ejemplo, una impresora).<br />

15.7. EJERCICIOS<br />

15.1. ¿Cuál es la salida <strong>de</strong> este segm<strong>en</strong>to <strong>de</strong> código,<br />

t<strong>en</strong>i<strong>en</strong>do <strong>en</strong> cu<strong>en</strong>ta que el tipo <strong>de</strong> dato <strong>de</strong> la pila<br />

es int?<br />

Pila P;<br />

int x=4, y;<br />

CrearPila(&P);<br />

Insertar (&P, x) ;<br />

printf ("\n%d",Cima(P ) ;<br />

y = Quitar(&P) ;<br />

Insertar(&P,32);<br />

Insertar (&P,Quitar(&PI ) ;<br />

do {<br />

printf ("\n%d',Quitar(&P) ;<br />

).while (!PilaVacia(P));<br />

15.2. Escribir <strong>en</strong> el archivo pila. h los tipos <strong>de</strong><br />

<strong>datos</strong> y los prototipos <strong>de</strong> las operaciones básicas<br />

sobre pilas con <strong>estructura</strong>s dinámicas.<br />

15.3. Escribir la función MostrarPila ( ) para<br />

escribir los elem<strong>en</strong>tos <strong>de</strong> una pila <strong>de</strong> cad<strong>en</strong>as<br />

<strong>de</strong> caracteres, utilizando sólo las operaciones<br />

básicas y una pila auxiliar.<br />

15.4. Obt<strong>en</strong>er una secu<strong>en</strong>cia <strong>de</strong> 10 elem<strong>en</strong>tos reales,<br />

guardarlos <strong>en</strong> un array y ponerlos <strong>en</strong> una pila.<br />

Imprimir la secu<strong>en</strong>cia original y, a continuación,<br />

imprimir la pila extray<strong>en</strong>do los elem<strong>en</strong>tos.<br />

15.5. Consi<strong>de</strong>rar una cola <strong>de</strong> nombres repres<strong>en</strong>tada<br />

por una array circular con 6 posiciones, el cam-<br />

PO fr<strong>en</strong>te con el valor: Fr<strong>en</strong>te = 2. Y los<br />

elem<strong>en</strong>tos <strong>de</strong> la Cola: Mar, Sella, C<strong>en</strong>turión.<br />

Escribir los elem<strong>en</strong>tos <strong>de</strong> la cola y los campos<br />

Fr<strong>en</strong>te y Final según se realizan estas<br />

operaciones :<br />

kladir Gloria y G<strong>en</strong>erosa a la cola.<br />

Eliminar <strong>de</strong> la cola.<br />

Añadir Positivo.<br />

* Añadir Horche a la cola.<br />

Eliminar todos los elem<strong>en</strong>tos <strong>de</strong> la cola.<br />

15.6. Una bicola es una <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> lineal <strong>en</strong><br />

la que la inserción y borrado se pued<strong>en</strong> hacer<br />

tanto por el extremo fr<strong>en</strong>te como por el<br />

extremo final. Suponer que se ha elegido<br />

una repres<strong>en</strong>tación dinámica, con punteros, y<br />

que los extremos <strong>de</strong> la lista se d<strong>en</strong>ominan<br />

fr<strong>en</strong>te y final. Escribir la implem<strong>en</strong>tación<br />

<strong>de</strong> las operaciones: InsertarFr<strong>en</strong>te ( ) ,<br />

InsertarFinalO, EliminarFr<strong>en</strong>te<br />

( ) y EliminarFinal( ).<br />

15.7. Consi<strong>de</strong>re una bicola <strong>de</strong> caracteres, repres<strong>en</strong>tada<br />

<strong>en</strong> un array circular. El array consta <strong>de</strong> 9<br />

posiciones. Los extremos actuales y los elem<strong>en</strong>tos<br />

<strong>de</strong> la bicola:<br />

fr<strong>en</strong>te = 5 final = 7<br />

Bicola: A,C,E<br />

Escribir los extremos y los elem<strong>en</strong>tos <strong>de</strong> la<br />

bicola según se realizan estas operaciones:


494 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Añadir los elem<strong>en</strong>tos F y K por el final <strong>de</strong><br />

Añadir el elem<strong>en</strong>to S por el fr<strong>en</strong>te <strong>de</strong> la<br />

la bicola. bicola. -<br />

Añadir los elem<strong>en</strong>tos R, W y V por el f r <strong>en</strong>te<br />

<strong>de</strong> la bicola.<br />

15.8. Se ti<strong>en</strong>e una pila <strong>de</strong> <strong>en</strong>teros positivos. Con las<br />

Añadir el elem<strong>en</strong>to M por el final <strong>de</strong> la operaciones básicas <strong>de</strong> pilas y colas escribir un<br />

bicola.<br />

fragm<strong>en</strong>to <strong>de</strong> código para poner todos los ele-<br />

Eliminar dos caracteres por el fr<strong>en</strong>te.<br />

m<strong>en</strong>tos que son par <strong>de</strong> la pila <strong>en</strong> la cola.<br />

Añadir los elem<strong>en</strong>tos K y L por el final <strong>de</strong><br />

la bicola.<br />

15.8. PROBLEMAS<br />

15.1. Escribir una función, copiarpila, que copie<br />

el cont<strong>en</strong>ido <strong>de</strong> una pila <strong>en</strong> otra. La función t<strong>en</strong>drá<br />

dos argum<strong>en</strong>tos <strong>de</strong> tipo pila, uno para la pila<br />

fu<strong>en</strong>te y otro para la pila <strong>de</strong>stino. Utilizar las<br />

operaciones <strong>de</strong>finidas sobre el tipo <strong>de</strong> <strong>datos</strong> pila.<br />

153. Con un archivo <strong>de</strong> texto se quier<strong>en</strong> realizar las<br />

sigui<strong>en</strong>tes acciones: formar una lista <strong>de</strong> colas,<br />

<strong>de</strong> tal forma que <strong>en</strong> cada nodo <strong>de</strong> la lista t<strong>en</strong>ga<br />

la dirección <strong>de</strong> una cola que ti<strong>en</strong>e todas las<br />

palabras <strong>de</strong>l archivo que empiezan por una<br />

misma letra. Visualizar las palabras <strong>de</strong>l archivo,<br />

empezando por la cola que conti<strong>en</strong>e las<br />

palabras que comi<strong>en</strong>zan por a, a continuación<br />

las <strong>de</strong> la letra b, y así sucesivam<strong>en</strong>te.<br />

15.3. Escribir una función para <strong>de</strong>terminar si una<br />

secu<strong>en</strong>cia <strong>de</strong> caracteres <strong>de</strong> <strong>en</strong>trada es <strong>de</strong> la forma:<br />

X & Y<br />

don<strong>de</strong> x es una cad<strong>en</strong>a <strong>de</strong> caracteres e Y es la<br />

cad<strong>en</strong>a inversa. El carácter & es el separador.<br />

15.4. Escribir un programa que haci<strong>en</strong>do uso <strong>de</strong>l tipo<br />

Pila <strong>de</strong> caracteres, procese cada uno <strong>de</strong> los<br />

caracteres <strong>de</strong> una expresión que vi<strong>en</strong>e dada <strong>en</strong><br />

una línea <strong>de</strong> caracteres. La finalidad es verificar<br />

el equilibrio <strong>de</strong> par&ntesis, llaves y corchetes.<br />

Por ejemplo, la sigui<strong>en</strong>te expresión ti<strong>en</strong>e un<br />

número <strong>de</strong> paréntesis equilibrado:<br />

((a+b)*5) - 7<br />

A esta otra expresión le falta un corchete:<br />

2*[(a+b)/2.5 + x - 7*y<br />

15.5. Se ti<strong>en</strong>e un archivo <strong>de</strong> texto <strong>de</strong>l cual se quiere<br />

<strong>de</strong>terminar las frases que son palíndromo. Para<br />

lo cual se ha <strong>de</strong> seguir la sigui<strong>en</strong>te estrategia:<br />

Consi<strong>de</strong>rar cada línea <strong>de</strong>l texto una frase.<br />

Añadir cada carácter <strong>de</strong> la frase a una pila y<br />

a la vez a una cola.<br />

Extraer carácter a carácter, y simultáneam<strong>en</strong>te<br />

<strong>de</strong> la pila y <strong>de</strong> la cola. Su comparación<br />

<strong>de</strong>termina si es palíndromo o no.<br />

Escribir un programa <strong>en</strong> C que lea cada línea<br />

<strong>de</strong>l archivo y <strong>de</strong>termine si es paiíndromo.<br />

15.6. Escribir un programa <strong>en</strong> el que se g<strong>en</strong>er<strong>en</strong> 100<br />

números aleatonos <strong>en</strong> el rango - 2 5 . . + 2 5<br />

y se guard<strong>en</strong> <strong>en</strong> una cola implem<strong>en</strong>tada<br />

mediante un array consi<strong>de</strong>rado circular. Una<br />

vez creada la cola, el usuario pue<strong>de</strong> pedir que<br />

se forme otra cola con los números negativos<br />

que ti<strong>en</strong>e la cola original.<br />

15.7. Escribir una función que t<strong>en</strong>ga como argum<strong>en</strong>tos<br />

dos colas <strong>de</strong>l mismo tipo. Devuelva cierto<br />

si las dos colas son idénticas.<br />

15.8. Escribir un programa <strong>en</strong> el que se manej<strong>en</strong> un<br />

totald<strong>en</strong>=5pilas: P,, P,, P,, P, y P,. La<br />

<strong>en</strong>trada <strong>de</strong> <strong>datos</strong> será pares <strong>de</strong> <strong>en</strong>teros ( i , j 1


Pilas y colas 495<br />

tal que ,I< abs ( i 1 < n. De tal forma que el<br />

criterio <strong>de</strong> seleccidn <strong>de</strong> pila:<br />

Si i es positivo, <strong>de</strong>be <strong>de</strong> insertarse el<br />

elem<strong>en</strong>toj <strong>en</strong> la pila P,.<br />

Si i es negativo, <strong>de</strong>be <strong>de</strong> eliminarse el<br />

elem<strong>en</strong>toj <strong>de</strong> la pila P,.<br />

Si i es cero, fin <strong>de</strong>l proceso <strong>de</strong> <strong>en</strong>trada.<br />

Los <strong>datos</strong> <strong>de</strong> <strong>en</strong>trada se introduc<strong>en</strong> por<br />

teclado. Cuando termina el proceso el programa<br />

<strong>de</strong>be <strong>de</strong> escribir el cont<strong>en</strong>ido <strong>de</strong> la n pilas<br />

<strong>en</strong> pantalla.<br />

15.9. Modificar el Problema 15.8 para que la <strong>en</strong>trada<br />

sean triplas <strong>de</strong> números <strong>en</strong>teros ( i , j , k) ,<br />

don<strong>de</strong> i , j ti<strong>en</strong><strong>en</strong> el mismo significado que<br />

<strong>en</strong> 15.8, y k es un número <strong>en</strong>tero que pue<strong>de</strong><br />

tomar los valores - 1 , 0 con este significado:<br />

- 1, hay que borrar todos los elem<strong>en</strong>tos <strong>de</strong><br />

la pila.<br />

O, el proceso es el indicado <strong>en</strong> 15.8 con i<br />

y j.<br />

15.10. Un pequeño supermercado dispone <strong>en</strong> la<br />

salida <strong>de</strong> tres cajas <strong>de</strong> pago. En el local hay<br />

25 carritos <strong>de</strong> compra. Escribir un programa<br />

que simule el funcionami<strong>en</strong>to, sigui<strong>en</strong>do las<br />

sigui<strong>en</strong>tes reglas:<br />

Si cuando llega un cli<strong>en</strong>te no hay ningún<br />

carrito disponible, espera a que lo haya.<br />

Ningún cli<strong>en</strong>te se impaci<strong>en</strong>ta y abandona el<br />

supermercado sin pasar por alguna <strong>de</strong> las<br />

colas <strong>de</strong> las cajas.<br />

Cuando un cli<strong>en</strong>te finaliza su compra, se<br />

coloca <strong>en</strong> la cola <strong>de</strong> la caja que hay m<strong>en</strong>os<br />

g<strong>en</strong>te, y no se cambia <strong>de</strong> cola.<br />

En el mom<strong>en</strong>to <strong>en</strong> que un cli<strong>en</strong>te paga <strong>en</strong> la<br />

caja, el carro <strong>de</strong> la compra que ti<strong>en</strong>e queda<br />

disponible.<br />

Repres<strong>en</strong>tar la lista <strong>de</strong> carritos <strong>de</strong> la compra y<br />

las cajas <strong>de</strong> salida mediante colas.


CAPíTULO 16<br />

ÁRBOLES<br />

CONTENIDO<br />

16.1. Arboles g<strong>en</strong>erales.<br />

16.B. Resum<strong>en</strong> <strong>de</strong> <strong>de</strong>finiciones.<br />

16.3. Arboles binarios.<br />

16.4. Estructura <strong>de</strong> un árbol<br />

binario.<br />

16.6. Operaciones <strong>en</strong> árboles<br />

binarios .<br />

16.6. Arbol <strong>de</strong> expresiones.<br />

16.7. Recorrido <strong>de</strong> un árbol.<br />

16.8. Arbol binario <strong>de</strong> búsqueda.<br />

16.9. Operaciones <strong>en</strong> árboles<br />

binarios <strong>de</strong> búsqueda.<br />

16.10. Aplicaciones <strong>de</strong> árboles <strong>en</strong><br />

<strong>algoritmos</strong> <strong>de</strong> exploración.<br />

16.11. Resum<strong>en</strong>.<br />

16.1% Ejercicios.<br />

16.13. Problemw.<br />

16.14. Refer<strong>en</strong>cia8 bibliográficas.<br />

I<br />

496


El árbol es una, <strong>estructura</strong> <strong>de</strong> <strong>datos</strong> muy importante <strong>en</strong> informática y <strong>en</strong> ci<strong>en</strong>cias<br />

<strong>de</strong> la computación. Los árboles son <strong>estructura</strong>s no lineales al contrario que<br />

los arrays y las listas <strong>en</strong>lazadas que constituy<strong>en</strong> <strong>estructura</strong>s lineales.<br />

Los árboles son muy utilizados <strong>en</strong> informática para repres<strong>en</strong>tar fórmulas<br />

algebraicas como un método efici<strong>en</strong>te para búsquedas gran<strong>de</strong>s y complejas, lis<br />

tas dinámicas y aplicaciones diversas tales como intelig<strong>en</strong>cia artificial o <strong>algoritmos</strong><br />

<strong>de</strong> cifrado. Casi todos los sistemas operativos almac<strong>en</strong>an sus archivos<br />

<strong>en</strong> árboles o <strong>estructura</strong>s similares a árboles. A<strong>de</strong>más <strong>de</strong> las aplicaciones citadas,<br />

los árboles se utilizan <strong>en</strong> diseño <strong>de</strong> compiladores, proceso <strong>de</strong> texto y <strong>algoritmos</strong><br />

<strong>de</strong> búsqueda.<br />

En el capítulo se estudiara el concepto <strong>de</strong> árbol g<strong>en</strong>eral y los tipos <strong>de</strong> árboles<br />

más usuales, binario y binario <strong>de</strong> búsqueda. Asimismo se estudiarán algunas<br />

aplicaciones típicas <strong>de</strong>l diseño y construcción <strong>de</strong> árboles.<br />

CONCEPTOS CLAVE<br />

Enord<strong>en</strong>.<br />

* &bol binario . Nodo.<br />

Árbol binario <strong>de</strong> búsqueda.<br />

Preord<strong>en</strong>.<br />

Conceptos teóricos (nívei,<br />

Postord<strong>en</strong>.<br />

profundidad, raíz, hoja,<br />

Recorrido <strong>de</strong> un árbol.<br />

Subárbol.<br />

497


498 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

16.1. ÁRBOLES GENERALES<br />

Intuitivam<strong>en</strong>te el concepto <strong>de</strong> árbol implica una <strong>estructura</strong> <strong>en</strong> la que los <strong>datos</strong> se organizan <strong>de</strong> modo<br />

que los elem<strong>en</strong>tos <strong>de</strong> información están relacionados <strong>en</strong>tre sí a través <strong>de</strong> ramas. El árbol g<strong>en</strong>ealógico es<br />

el ejemplo típico más repres<strong>en</strong>tativo <strong>de</strong>l concepto <strong>de</strong> árbol g<strong>en</strong>eral. La Figura 16.1 repres<strong>en</strong>ta dos<br />

ejemplos <strong>de</strong> árboles g<strong>en</strong>erales.<br />

Luis<br />

(bisabuelo)<br />

Micaela<br />

(hermana) *I<br />

$q (hermana)<br />

Juana María<br />

(hija)<br />

pk---pqfi<br />

(hija) (hija) (hijo)<br />

Figura 16.1. Árbol g<strong>en</strong>ealógico (bisabuelo-bisnietos).<br />

Director<br />

g<strong>en</strong>eral<br />

Director <strong>de</strong> Director <strong>de</strong> Director <strong>de</strong> Director<br />

recursos<br />

marketing humanos informática financiero<br />

Director <strong>de</strong> Director <strong>de</strong> Director red<br />

software<br />

intranet<br />

Ing<strong>en</strong>iero <strong>de</strong><br />

1 I Analista 1 I Programador 1<br />

1 software<br />

Figura 16.2. Estructura jerárquica tipo árbol.


Árboles 499<br />

Un árbol consta <strong>de</strong> un conjunto finito <strong>de</strong> elem<strong>en</strong>tos, d<strong>en</strong>ominados nodos y un conjunto finito <strong>de</strong><br />

líneas dirigidas, d<strong>en</strong>ominadas ramas, que conectan los nodos. El número <strong>de</strong> ramas asociado con un<br />

nodo es el grado <strong>de</strong>l nodo.<br />

Definición 1: Un árbol consta <strong>de</strong> un conjunto finito <strong>de</strong> elem<strong>en</strong>tos, llamados nodos y un conjunto<br />

finito <strong>de</strong> líneas dirigidas, llamadas ramas, que conectan los nodos.<br />

Definición 2: Un árbol es un conjunto <strong>de</strong> uno o más nodos tales que:<br />

1. Hay un nodo diseñado especialm<strong>en</strong>te llamado raíz.<br />

2. Los nodos restantes se divid<strong>en</strong> <strong>en</strong> n2o conjuntos disjuntos tales que T, ... T,,, <strong>en</strong><br />

don<strong>de</strong> cada uno <strong>de</strong> estos conjuntos es un árbol. A T,, T?, ... T,, se les d<strong>en</strong>omina<br />

subárboles <strong>de</strong>l raíz.<br />

Si un árbol no está vacío, <strong>en</strong>tonces el primer nodo se llama raíz. Obsérvese <strong>en</strong> la <strong>de</strong>finición 2 que<br />

el árbol ha sido <strong>de</strong>finido <strong>de</strong> modo recursivo ya que los subárboles se <strong>de</strong>fin<strong>en</strong> como árboles. La Figura<br />

16.3 muestra un árbol.<br />

Figura 16.3. Árbol.<br />

Terminología<br />

A<strong>de</strong>más <strong>de</strong>l raíz exif :n muchos términos utilizados <strong>en</strong> la <strong>de</strong>scripción <strong>de</strong> los atributos <strong>de</strong> un árbol. En<br />

la Figura 16.4, el nodo A es el raíz. Utilizando el concepto <strong>de</strong> árboles g<strong>en</strong>ealógicos, un nodo pue<strong>de</strong> ser<br />

consi<strong>de</strong>rado como padre si ti<strong>en</strong>e nodos sucesores.<br />

Figura 16.4. Árbol g<strong>en</strong>eral.


500 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Estos nodos sucesores se llaman hijos. Por ejemplo, el nodo B es el padre <strong>de</strong> los hijos E y F. El<br />

padre <strong>de</strong> H es el nodo D. Un árbol pue<strong>de</strong> repres<strong>en</strong>tar diversas g<strong>en</strong>eraciones <strong>en</strong> la familia. Los hijos <strong>de</strong><br />

un nodo y los hijos <strong>de</strong> estos hijos se llaman <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes y el padre y abuelos <strong>de</strong> un nodo son sus<br />

asc<strong>en</strong>di<strong>en</strong>tes. Por ejemplo, los nodos E, F, I y J son <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes <strong>de</strong> B. Cada nodo no raíz ti<strong>en</strong>e un<br />

único padre y cada padre ti<strong>en</strong>e cero o más nodos hijos. Dos o más nodos con el mismo padre se llaman<br />

hermanos. Un nodo sin hijos, tales como E, I, J, G y H se llaman nodos hoja.<br />

El nivel <strong>de</strong> un nodo es su distancia al raíz. El raíz ti<strong>en</strong>e una distancia cero <strong>de</strong> sí misma, por lo que<br />

se dice que el raíz está <strong>en</strong> el nivel O. Los hijos <strong>de</strong>l raíz están <strong>en</strong> el nivel I, sus hijos están <strong>en</strong> el nivel 2 y<br />

así sucesivam<strong>en</strong>te. Una cosa importante que se aprecia <strong>en</strong>tre los niveles <strong>de</strong> nodos es la relación <strong>en</strong>tre<br />

niveles y hermanos. Los hermanos están siempre al mismo nivel, pero no todos los nodos <strong>de</strong> un mismo<br />

nivel son necesariam<strong>en</strong>te hermanos. Por ejemplo, <strong>en</strong> el nivel 2 (Fig. 16.5), C y D son hermanos, al igual<br />

que lo son G, H e I, pero D y G no son hermanos ya que ellos ti<strong>en</strong><strong>en</strong> difer<strong>en</strong>tes padres.<br />

Nivel 0 -<br />

Nivel 1 -<br />

Rama FI<br />

Nivel 2 -<br />

padre: A, B, F<br />

hijos: B, E, F, C, D, G, H, I<br />

hermanos:{B, E, F), {C, D}, {G, H, I}<br />

hojas: C, D, E, G, H, I<br />

Figura 16.5. Terminología <strong>de</strong> árboles.<br />

Exist<strong>en</strong> varias formas <strong>de</strong> dibujar los atributos <strong>de</strong> los árboles y sus nodos. Un camino es una<br />

secu<strong>en</strong>cia <strong>de</strong> nodos <strong>en</strong> los que cada nodo es adyac<strong>en</strong>te al sigui<strong>en</strong>te. Cada nodo <strong>de</strong>l árbol pue<strong>de</strong> ser<br />

alcanzado (se llega a él) sigui<strong>en</strong>do un único camino que comi<strong>en</strong>za <strong>en</strong> el raíz. En la Figura 16.5, el<br />

camino <strong>de</strong>s<strong>de</strong> el raíz a la hoja I, se repres<strong>en</strong>ta por AFI. Incluye dos ramas distintas AF y FI.<br />

La altura o profundidad <strong>de</strong> un árbol es el nivel <strong>de</strong> la hoja <strong>de</strong>l camino más largo <strong>de</strong>s<strong>de</strong> la raíz más uno.<br />

Por <strong>de</strong>finición' la altura <strong>de</strong> un árbol vacío es O. La Figura 16.5 conti<strong>en</strong>e nodos <strong>en</strong> tres niveles : O, 1 y 2.<br />

Su altura es 3.<br />

Definición<br />

El nivel <strong>de</strong> un nodo es su distancia <strong>de</strong>s<strong>de</strong> el raíz. La altura <strong>de</strong> un árbol es el nivel <strong>de</strong> la hoja <strong>de</strong>l<br />

camino más largo <strong>de</strong>s<strong>de</strong> el raíz más uno.<br />

' También se suele <strong>de</strong>finir la profundidad dc un irbol con10 el nivel iiiixiino dc c;idn nodo. En consecu<strong>en</strong>cia. la prolundidad <strong>de</strong>l<br />

nodo raír es O, la <strong>de</strong> $11 hi,jo 1, etc. Las do\ ieriiiiiiologías son accptadas.


Árboles 501<br />

(a) Profundidad 4 (b) Profundidad 4<br />

(c) Profundidad 5<br />

Figura 16.6. Árboles <strong>de</strong> profundida<strong>de</strong>s difer<strong>en</strong>tes.<br />

t<br />

Un árbol se divi<strong>de</strong> <strong>en</strong> subárboles. Un subárbol es cualquier <strong>estructura</strong> conectada por <strong>de</strong>bajo <strong>de</strong>l<br />

raíz. Cada nodo <strong>de</strong> un árbol es la raíz <strong>de</strong> un subárbol que se <strong>de</strong>fine por el nodo y todos los <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes<br />

<strong>de</strong>l nodo. El primer nodo <strong>de</strong> un subárbol se conoce como el raíz <strong>de</strong>l subárbol y se utiliza para nombrar<br />

el subárbol. A<strong>de</strong>más, los subárboles se pued<strong>en</strong> subdividir <strong>en</strong> subárboles. En la Figura 16.5, BCD es un<br />

subárbol al igual que E y FGHI. Obsérvese que por esta <strong>de</strong>finición, un nodo simple es un subárbol. Por<br />

consigui<strong>en</strong>te, el subárbol B se pue<strong>de</strong> dividir <strong>en</strong> subárboles C y D mi<strong>en</strong>tras que el subárbol F conti<strong>en</strong>e los<br />

subárboles G, H e I. Se dice que G, H, I, C y D son subárboles sin <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes. El concepto <strong>de</strong><br />

subárbol conduce a una <strong>de</strong>finición recursiva <strong>de</strong> un árbol. Un árbol es un conjunto <strong>de</strong> nodos que:<br />

1. O bi<strong>en</strong> es vacío, o bi<strong>en</strong><br />

2. Ti<strong>en</strong>e un nodo <strong>de</strong>terminado llamado raíz <strong>de</strong>l que jerárquicam<strong>en</strong>te <strong>de</strong>sci<strong>en</strong>d<strong>en</strong> cero o mh\<br />

subárboles, que son también árboles.<br />

Un árbol está equilibrado cuando, dado un número máximo <strong>de</strong> k hijos para cada nodo y la altura<br />

<strong>de</strong>l árbol h, cada nodo <strong>de</strong> nivel 1 < h - 1 ti<strong>en</strong>e exactam<strong>en</strong>te k hijos. El árbol está equilibrado<br />

perfectam<strong>en</strong>te cuando cada nodo <strong>de</strong> nivel 1 < h ti<strong>en</strong>e exactam<strong>en</strong>te k hijos.


502 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

n<br />

Figura 16.7. (a) Un árbol equilibrado; (b) Un árbol perfectam<strong>en</strong>te equilibrado.<br />

( b)<br />

16.1 .I. Repres<strong>en</strong>tación <strong>de</strong> un árbol<br />

Aunque un árbol se implem<strong>en</strong>ta <strong>en</strong> un l<strong>en</strong>guaje <strong>de</strong> programación como C mediante punteros, cuando se<br />

ha <strong>de</strong> repres<strong>en</strong>tar <strong>en</strong> papel, exist<strong>en</strong> tres formas difer<strong>en</strong>tes <strong>de</strong> repres<strong>en</strong>tación. La primera es el diagrama<br />

o carta <strong>de</strong> organización utilizada hasta ahora <strong>en</strong> las difer<strong>en</strong>tes figuras. El término que se utiliza para esta<br />

notación es el <strong>de</strong> árbol g<strong>en</strong>eral.<br />

Repres<strong>en</strong>tación <strong>en</strong> niveles <strong>de</strong> profundidad<br />

Este tipo <strong>de</strong> repres<strong>en</strong>tación es el utilizado para repres<strong>en</strong>tar sistemas jerárquicos <strong>en</strong> modo texto o número<br />

<strong>en</strong> situaciones tales como facturación, gestión <strong>de</strong> stocks <strong>en</strong> almac<strong>en</strong>es, etc.<br />

Por ejemplo, <strong>en</strong> las Figuras 16.8 y 16.9 se aprecia una <strong>de</strong>scomposición <strong>de</strong> una computadora <strong>en</strong> sus<br />

diversos compon<strong>en</strong>tes <strong>en</strong> una <strong>estructura</strong> árbol. Otro ejemplo podría ser una distribución <strong>en</strong> árbol <strong>de</strong> las<br />

piezas <strong>de</strong> una ti<strong>en</strong>da <strong>de</strong> recambios <strong>de</strong> automóviles distribuidas <strong>en</strong> niveles <strong>de</strong> profundidad según los<br />

números <strong>de</strong> parte o códigos <strong>de</strong> cada repuesto (motor, bujía, batería, piloto, faro, embellecedor, etc.).


1<br />

Árboles 503<br />

Figura 16.8. Árbol g<strong>en</strong>eral (computadora).<br />

Número código<br />

501<br />

501-1 1<br />

...<br />

501-21<br />

501-21 1<br />

501 -212<br />

...<br />

501-219<br />

501 -3 1<br />

...<br />

501-41<br />

501-411<br />

501-412<br />

501-51<br />

501-51 1<br />

501-512<br />

501-513<br />

Descripción<br />

Computadora<br />

Monitor<br />

CPU<br />

Controlador<br />

ALU<br />

...<br />

ROM<br />

Teclado<br />

...<br />

Periféricos<br />

Escáner<br />

impresora<br />

Discos<br />

CD-ROM<br />

CD-RW<br />

DVD<br />

Figura 16.9. Árbol <strong>en</strong> nivel <strong>de</strong> profundidad (computadora).<br />

Repres<strong>en</strong>tación <strong>de</strong> lista<br />

Otro formato utilizado para repres<strong>en</strong>tar un árbol es la lista <strong>en</strong>tre paréntesis. Ésta es la notación utilizada<br />

con expresiones algebraicas. En esta repres<strong>en</strong>tación, cada paréntesis abierto indica el comi<strong>en</strong>zo <strong>de</strong> un<br />

nuevo nivel; cada paréntesis cerrado completa un nivel y se mueve hacia arriba un nivel <strong>en</strong> el irbol. La<br />

notación <strong>en</strong> paréntesis <strong>de</strong> la Figura 16.3 es: A (a (c, D) , E, F , ( G , H, I ) ) .<br />

I<br />

Ejemplo 16.1<br />

Convertir el Urbol g<strong>en</strong>eml sigui<strong>en</strong>te <strong>en</strong> repre.s<strong>en</strong>tuc.icín <strong>en</strong> listu.


504 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

l'! I<br />

16.2. RESUMEN DE DEFINICIONES<br />

1. Dado un conjunto E <strong>de</strong> elem<strong>en</strong>tos:<br />

0 Un árbol pue<strong>de</strong> estar vacio; es <strong>de</strong>cir, no conti<strong>en</strong>e ningún elem<strong>en</strong>to,<br />

0 Un árbol no vacío pue<strong>de</strong> constar <strong>de</strong> un único elem<strong>en</strong>to e E E d<strong>en</strong>ominado un nodo, o bi<strong>en</strong><br />

Un árbol consta <strong>de</strong> un nodo e t' E, conectado por arcos directos a un número finito <strong>de</strong> otros<br />

árboles.<br />

2. Definiciones:<br />

O El primer nodo <strong>de</strong> un árbol, normalm<strong>en</strong>te dibujado <strong>en</strong> la posición superior, se d<strong>en</strong>omina raíz<br />

<strong>de</strong>l árbol.<br />

O Las flechas que conectan un nodo a otro se llaman arcos o ramas.<br />

Los nodos terminales, esto es, nodos <strong>de</strong> los cuales no se <strong>de</strong>duce ningún nodo, se d<strong>en</strong>ominan<br />

hojas.<br />

O Los nodos que no son hojas se d<strong>en</strong>ominan nodos internos o nodos no terminales.<br />

En un árbol una rama va <strong>de</strong> un nodo n, a un nodo n2, se dice que n, es el padre <strong>de</strong> n2 y que n2<br />

es un hijo <strong>de</strong> n,.<br />

O TI, se llama asc<strong>en</strong>di<strong>en</strong>te <strong>de</strong> nL si n, es el padre <strong>de</strong> nL o si n, es el padre <strong>de</strong> un asc<strong>en</strong>di<strong>en</strong>te <strong>de</strong> n2.<br />

O 11: se llama <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>te <strong>de</strong> n, si n, es un asc<strong>en</strong>di<strong>en</strong>te <strong>de</strong> n2.<br />

O Un camino <strong>de</strong> n, a n2 es una secu<strong>en</strong>cia <strong>de</strong> arcos contiguos que van <strong>de</strong> n, a n2.<br />

O La longitud <strong>de</strong> un camino es el número <strong>de</strong> arcos que conti<strong>en</strong>e (<strong>en</strong> otras palabras el número <strong>de</strong><br />

nodos -1).<br />

El nivel <strong>de</strong> un nodo es la longitud <strong>de</strong>l camino que lo conecta al raíz.<br />

O La profundidad o altura <strong>de</strong> un árbol es la longitud <strong>de</strong>l camino más largo que conecta el raíz<br />

a una hoja.


Árboles 505<br />

Un subárbol <strong>de</strong> un árbol es un subconjunto <strong>de</strong> nodos <strong>de</strong>l árbol, conectados por ramas <strong>de</strong>l<br />

propio árbol, esto es a su vez un árbol.<br />

O Sea S un subárbol <strong>de</strong> un árbol A: si para cada nodo n <strong>de</strong> SA, SA conti<strong>en</strong>e también todos los<br />

<strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes <strong>de</strong> n <strong>en</strong> A. SA se llama un subárbol completo <strong>de</strong> A.<br />

Un árbol está equilibrado cuando, dado un número máximo K <strong>de</strong> hijos <strong>de</strong> cada nodo y la<br />

altura <strong>de</strong>l árbol h, cada nodo <strong>de</strong> nivel k < h-1 ti<strong>en</strong>e exactam<strong>en</strong>te K hijos. El árbol está<br />

equilibrado perfectam<strong>en</strong>te <strong>en</strong>tre cada nodo <strong>de</strong> nivel I


506 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

I<br />

(9) (h)<br />

Figura 16.10. Árboles binarios.<br />

Nota<br />

Un árbol binario no pue<strong>de</strong> t<strong>en</strong>er más <strong>de</strong> dos subárboles.<br />

Un árbol binario es una <strong>estructura</strong> recursiva. Cada nodo es el raíz <strong>de</strong> su propio subárbol y ti<strong>en</strong>e<br />

hijos, que son raíces <strong>de</strong> árboles llamados los subárboles <strong>de</strong>recho e izquierdo <strong>de</strong>l nodo, respectivam<strong>en</strong>te.<br />

Un árbol binario se divi<strong>de</strong> <strong>en</strong> tres subconjuntos disjuntos:<br />

{R)<br />

{I, I, ... I,><br />

{D, D, ... D}<br />

Nodo ruíz<br />

Suhárbol izquierdo <strong>de</strong> R<br />

Suhdrhol <strong>de</strong>recho <strong>de</strong> R


Árboles 507<br />

Subárbol izquierdo<br />

Figura 16.11. Árbol binario.<br />

En cualquier nivel n, un árbol binario pue<strong>de</strong> cont<strong>en</strong>er <strong>de</strong> 1 a 2 nodos. El número <strong>de</strong> nodos por nivel<br />

contribuye a la d<strong>en</strong>sidad <strong>de</strong>l árbol.<br />

(a) ( b)<br />

Figura 16.12. Árboles binarios: (a) profundidad 4; (b) profundidad 5.<br />

En la Figura 16. I2 (u) el árbol A conti<strong>en</strong>e 8 nodos <strong>en</strong> una profundidad <strong>de</strong> 4, mi<strong>en</strong>tras que el árbol<br />

16. I2 (h) conti<strong>en</strong>e 5 nodos y una profundidad 5. Este último caso es una forma especial, d<strong>en</strong>ominado<br />

árbol <strong>de</strong>g<strong>en</strong>erado, <strong>en</strong> el que existe un solo nodo hoja ( E ) y cada nodo no hoja sólo ti<strong>en</strong>e un hijo. Un<br />

árbol <strong>de</strong>g<strong>en</strong>erado es equival<strong>en</strong>te a una lista <strong>en</strong>lazada.<br />

16.3.1. Equilibrio<br />

La distancia <strong>de</strong> un nodo al raíz <strong>de</strong>termina la efici<strong>en</strong>cia con la que pue<strong>de</strong> ser localizado. Por ejemplo,<br />

dado cualquier nodo <strong>de</strong> un árbol, a sus hijos se pue<strong>de</strong> acce<strong>de</strong>r sigui<strong>en</strong>do sólo un camino <strong>de</strong> bifurcación


508 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

o <strong>de</strong> ramas, el que conduce al nodo <strong>de</strong>seado. De modo similar, los nodos a nivel 2 <strong>de</strong> un árbol sólo<br />

pued<strong>en</strong> ser accedidos sigui<strong>en</strong>do sólo dos ramas <strong>de</strong>l árbol.<br />

La característica anterior nos conduce a una característica muy importante <strong>de</strong> un árbol binario, su<br />

balance o equilibrio. Para <strong>de</strong>terminar si un árbol está equilibrado, se calcula su factor <strong>de</strong> equilibrio. El<br />

factor <strong>de</strong> equilibrio <strong>de</strong> un árbol binario es la difer<strong>en</strong>cia <strong>en</strong> altura <strong>en</strong>tre los subárboles <strong>de</strong>recho e<br />

izquierdo. Si <strong>de</strong>finimos la altura <strong>de</strong>l subárbol itquierdo como H, y la altura <strong>de</strong>l subárbol <strong>de</strong>recho como<br />

H,,, <strong>en</strong>tonces el factor <strong>de</strong> equilibrio <strong>de</strong>l árbol B se <strong>de</strong>termina por la sigui<strong>en</strong>te fórmula: B = H - H .<br />

Utilizando esta fórmula el equilibrio <strong>de</strong>l nodo raíz los ocho árboles <strong>de</strong> la Figura 16.10 son (u) O (h)<br />

O, (c) O por <strong>de</strong>finición, (4 -I, (e) 4, (f> -1, (g) I, (h) 2.<br />

Un árbol está perfectam<strong>en</strong>te equilibrado si su equilibrio o balance es cero y sus subárboles son<br />

también perfectam<strong>en</strong>te equilibrados. Dado que esta <strong>de</strong>finición ocurre raram<strong>en</strong>te se aplica una <strong>de</strong>finición<br />

alternativa. Un árbol binario está equilibrado si la altura <strong>de</strong> sus subárboles difiere <strong>en</strong> no más <strong>de</strong> uno (su<br />

factor <strong>de</strong> equilibrio es -1, O, +I) y sus subárboles son también equilibrados.<br />

16.3.2. Árboles binarios completos<br />

Un árbol binario completo <strong>de</strong> profundidad n es un árbol <strong>en</strong> el que para cada nivel, <strong>de</strong>l O al nivel n-1<br />

ti<strong>en</strong>e un conjunto ll<strong>en</strong>o <strong>de</strong> nodos y todos los nodos hoja a nivel n ocupan las posiciones más a la<br />

izquierda <strong>de</strong>l árbol.<br />

Un árbol binario completo que conti<strong>en</strong>e 2" nodos a nivel n es un árbol ll<strong>en</strong>o. Un árbol ll<strong>en</strong>o es un<br />

árbol binario que ti<strong>en</strong>e el máximo número <strong>de</strong> <strong>en</strong>tradas para su altura. Esto suce<strong>de</strong> cuando el Último nivel<br />

está ll<strong>en</strong>o. La Figura 16. I3 muestra un árbol binario completo; el árbol <strong>de</strong> la Figura 16.14 (6) se<br />

correspon<strong>de</strong> con uno ll<strong>en</strong>o.<br />

Figura 16.13. Árbol completo (profundidad 4).<br />

Árbol <strong>de</strong>g<strong>en</strong>erado<br />

(profundidad 5)<br />

(4<br />

Figura 16.14. Clasificación <strong>de</strong> árboles binarios: (a) <strong>de</strong>g<strong>en</strong>erado; (b) ll<strong>en</strong>o.<br />

Árbol ll<strong>en</strong>o<br />

(profundidad 3)<br />

( b)


F-<br />

Árboles 509<br />

El Último caso <strong>de</strong> árbol es un tipo especial d<strong>en</strong>ominado árbol <strong>de</strong>g<strong>en</strong>erado <strong>en</strong> el que hay un solo<br />

nodo hoja (E) y cada nodo no hoja sólo ti<strong>en</strong>e un hijo. Un árbol <strong>de</strong>g<strong>en</strong>erado es equival<strong>en</strong>te a una lista<br />

<strong>en</strong>lazada. En la Figura 16.15 se muestran árboles ll<strong>en</strong>os y completos.<br />

m<br />

Figura 16.15. (a) Árboles ll<strong>en</strong>os (<strong>en</strong> niveles O, 1 y 2); (b),(c) y (d) árboles completos (<strong>en</strong> nivel 2).<br />

Los árboles binarios y ll<strong>en</strong>os <strong>de</strong> profundidad ki 1 proporcionan algunos <strong>datos</strong> matemáticos que es<br />

necesario com<strong>en</strong>tar. En cada caso, existe un nodo (2") al nivel O (raíz), dos nodos (2') a nivel 1, cuatro<br />

nodos (2') a nivel 2, etc. A través <strong>de</strong> los primeros k-I niveles hay 2'-I nodos.<br />

1 + 2 + 4 + ... + 2h ' = 2"l


510 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

A nivel k, el número <strong>de</strong> nodos adicionados para un árbol completo está <strong>en</strong> el rango <strong>de</strong> un mínimo<br />

<strong>de</strong> 1 a un máximo <strong>de</strong> 2L (ll<strong>en</strong>o). Con un árbol ll<strong>en</strong>o, el número <strong>de</strong> nodos es<br />

1 + 2 + 4 + ... + 2k ' + 2L = 2k+' -1<br />

El número <strong>de</strong> nodos n <strong>en</strong> un árbol binario completo <strong>de</strong> profundidad k+l (O a k niveles) cumple la<br />

inigualdad<br />

2k I n I 2'+' -1 < 2'+'<br />

Aplicando logaritmos a la ecuación con <strong>de</strong>sigualdad anterior<br />

k I logz (n) < k + 1<br />

Se <strong>de</strong>duce que la altura o profundidad <strong>de</strong> un árbol binario completo <strong>de</strong> n nodos es:<br />

h = /log, 11 + 1 I (parte <strong>en</strong>tera <strong>de</strong> log,n + I)<br />

Por ejemplo, un árbol ll<strong>en</strong>o <strong>de</strong> profundidad 4 (niveles O a 3) ti<strong>en</strong>e 2' -1= 15 nodos<br />

Ejemplo 16.2<br />

Calcular la profundidad máxima y mínima <strong>de</strong> un árbol con 5 nodos.<br />

La profundidad máxima <strong>de</strong> un árbol con 5 nodos es 5<br />

La profundidad mínima n (número <strong>de</strong> niveles más uno) <strong>de</strong> un árbol con 5 nodos es<br />

kglog? (5) < k + I<br />

logz (5) = 2.32 y la profundidad n = 3<br />

Ejemplo 16.3<br />

La prqfindidad <strong>de</strong> un árbol <strong>de</strong>g<strong>en</strong>erado con n nodos es n, dudo que es la longitud <strong>de</strong>l camino más largo<br />

(rak a nodo) más I.<br />

En el árbol binario completo con n nodos, la profundidad <strong>de</strong>l árbol es el valor <strong>en</strong>tero <strong>de</strong> log, n + 1,<br />

que es a su vez la distancia <strong>de</strong>l camino más largo <strong>de</strong>s<strong>de</strong> el raíz a un nodo más uno.<br />

Suponi<strong>en</strong>do que el árbol ti<strong>en</strong>e n = 10.000 elem<strong>en</strong>tos, el camino más largo es<br />

int (log 10000) + 1 = int (13.28) + 1 = 14


~<br />

~<br />

Árboles 511<br />

16.4. ESTRUCTURA DE UN ÁRBOL BINAR10<br />

La <strong>estructura</strong> <strong>de</strong> un árbol binario se construye con nodos. Cada nodo <strong>de</strong>be cont<strong>en</strong>er el campo dato (<strong>datos</strong><br />

a almac<strong>en</strong>ar) y dos campos punteros, uno ai subárbol izquierdo y otro al suhárbol <strong>de</strong>recho, que se conoc<strong>en</strong><br />

como puntero izquierdo (izquierdo, izdo) y puntero <strong>de</strong>recho (<strong>de</strong>recho, dcho) respectivam<strong>en</strong>te.<br />

Un valor NULL indica un árbol vacío.<br />

1<br />

hoja-izquierda<br />

hoja-<strong>de</strong>recha<br />

izquierdo <strong>datos</strong> <strong>de</strong>recho<br />

El algoritmo correspondi<strong>en</strong>te a la <strong>estructura</strong> <strong>de</strong> un árbol es el sigui<strong>en</strong>te:<br />

Nodo<br />

subarbolIzquierdo<br />

<strong>datos</strong><br />

subarbolDerecho<br />

Fin Nodo<br />

< puntero a Nodo><br />

< Tipodato ><br />

< puntero a Nodo><br />

La Figura 16.16 muestra un árbol binario y su <strong>estructura</strong> <strong>en</strong> nodos:<br />

lzdo A Dch<br />

I<br />

-<br />

lzdo B Dch<br />

lzdo C Dch<br />

/ \<br />

/ \ (a) Árbol<br />

lzdo D Dch<br />

lzdo G Dch<br />

-<br />

I<br />

.;.‘;.;.I<br />

(b) Estructura<br />

Figura 16.16. Árbol binario y su <strong>estructura</strong> <strong>en</strong> nodos


512 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 16.4<br />

Repres<strong>en</strong>tar la <strong>estructura</strong> <strong>en</strong> nodos <strong>de</strong> los dos árboles binarios <strong>de</strong> raíz A:<br />

Nivel O<br />

Nivel 1<br />

Nivel 2<br />

Nivel 3<br />

Nivel 4<br />

La repres<strong>en</strong>tación <strong>en</strong>lazada <strong>de</strong> estos dos árboles binarios es:<br />

I I I<br />

I<br />

7NUL G NULL<br />

16.4.1. Difer<strong>en</strong>tes tipos <strong>de</strong> repres<strong>en</strong>taciones <strong>en</strong> C<br />

Los nodos pued<strong>en</strong> ser repres<strong>en</strong>tados con la <strong>estructura</strong> s t ruc t. Suponi<strong>en</strong>do que el nodo ti<strong>en</strong>e los campo<br />

Datos, Izquierdo y Derecho.<br />

Repres<strong>en</strong>tacicín I<br />

type<strong>de</strong>f struct nodo "puntero-arbol;<br />

type<strong>de</strong>f struct nodo {<br />

int <strong>datos</strong>;<br />

puntero-arbol hijo-izdo, hijo-dcho;<br />

I;


A<br />

<strong>datos</strong><br />

hijo-izdo hijo-dc ho<br />

Árboles 513<br />

hijo-izdo <strong>datos</strong> hijo-dcho<br />

Repres<strong>en</strong>tación 2<br />

type<strong>de</strong>f int TipoElem<strong>en</strong>to; /* Pue<strong>de</strong> ser cualquier tipo */<br />

struct Nodo {<br />

TipoElem<strong>en</strong>to Info;<br />

struct Nodo *hijo-izdo, *hijo-dcho;<br />

1;<br />

type<strong>de</strong>f struct Nodo Elem<strong>en</strong>to»eArbolBin;<br />

type<strong>de</strong>f Elem<strong>en</strong>toUeArbolHin "ArbolBinario;<br />

Para crear un nodo <strong>de</strong> un árbol binario, con la repres<strong>en</strong>tación 2, se reserva memoria para el nodo, se<br />

asigna el dato al campo info y se inicializa los punteros hi jo-izdo, hij o-dcho a NULI, .<br />

ArbolBinario CrearNodo(TipoE1em<strong>en</strong>to x)<br />

i<br />

ArbolBinario a;<br />

a = (ArbolBinario) mdlloc(si7eot (Elem<strong>en</strong>toDeArbolBin ) 1;<br />

a -> Info = x;<br />

a-> hijo-dcho = a -> hijo-izdo = NUI,T,;<br />

return a;<br />

i<br />

Si por ejemplo se <strong>de</strong>sea crear un árbol binario <strong>de</strong> raíz 9, rama izquierda 7 y rama <strong>de</strong>recha 1 1 :<br />

ArbolBinario rdiz;<br />

raiz = CrearNodo(9);<br />

raiz -> hijo-izdo = CrearNodo(7);<br />

raiz -> hijo-dcho = CrearNodo(l1);<br />

16.5. OPERACIONES EN ÁRBOLES BlNARlOS<br />

Una vez que se ti<strong>en</strong>e creado un árbol binario, se pued<strong>en</strong> realizar diversas operaciones sobre él. El hacer<br />

uso <strong>de</strong> una operación u otra <strong>de</strong>p<strong>en</strong><strong>de</strong>rá <strong>de</strong> la aplicación que se le quiera dar al árbol. Algunas <strong>de</strong> las<br />

operaciones típicas que se realizan <strong>en</strong> árboles binarios son:<br />

O Determinar su altura.<br />

O Determinar su número <strong>de</strong> elem<strong>en</strong>tos.<br />

O Hacer una copia.<br />

O Visualizar el árbol binario <strong>en</strong> pantalla o <strong>en</strong> impresora.<br />

O Determinar si dos árboles binarios son idénticos.<br />

O Borrar (eliminar el árbol).<br />

O Si es un árbol <strong>de</strong> expresión', evaluar la expresión.<br />

O Si es un árbol <strong>de</strong> expresión, obt<strong>en</strong>er la forma <strong>de</strong> paréntesis <strong>de</strong> la expresión.<br />

Todas estas operaciones se pued<strong>en</strong> realizar recorri<strong>en</strong>do el árbol binario <strong>de</strong> un modo sistemático. El<br />

' En cl apartado sigui<strong>en</strong>te se estudia el importante concepto <strong>de</strong> rírbol rlr, erpre.vicín.


514 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

recorrido <strong>de</strong> un árbol es la operación <strong>de</strong> visita al árbol, o lo que es lo mismo, la visita a cada nodo <strong>de</strong>l<br />

árbol una vez y sólo una. La visita <strong>de</strong> un árbol es necesaria <strong>en</strong> muchas ocasiones, por ejemplo, si se<br />

<strong>de</strong>sea imprimir la información cont<strong>en</strong>ida <strong>en</strong> cada nodo. Exist<strong>en</strong> difer<strong>en</strong>tes formas <strong>de</strong> visitar o recorrer<br />

un árbol que se estudiarán más tar<strong>de</strong>.<br />

16.6. ÁRBOLES DE EXPRESIÓN<br />

Una aplicación muy importante <strong>de</strong> los árboles binarios son los árbn1c.s <strong>de</strong> expresión. Una expresión es<br />

una secu<strong>en</strong>cia <strong>de</strong> tok<strong>en</strong>s (compon<strong>en</strong>tes <strong>de</strong> léxicos que sigu<strong>en</strong> unas reglas prescritas). Un tok<strong>en</strong> pue<strong>de</strong> ser<br />

o bi<strong>en</strong> un operando o bi<strong>en</strong> un operador.<br />

La Figura 16.17 repres<strong>en</strong>ta la expresión infija d* (b+c) td y su árbol <strong>de</strong> expresión. En una primera<br />

D<br />

a * (b + c) + d<br />

Figura 16.17. Una expresión infija y su árbol <strong>de</strong> expresión.<br />

observación vemos que los paréntesis no aparec<strong>en</strong> <strong>en</strong> el árbol.<br />

Un árbol <strong>de</strong> expresión es un árbol binario con las sigui<strong>en</strong>tes propieda<strong>de</strong>s:<br />

1. Cada hoja es un operando.<br />

2. Los nodos raíz e internos son operadores.<br />

3. Los subárboles son subexpresiones <strong>en</strong> las que el nodo raíz es un operador.<br />

Los árboles binarios se utilizan para repres<strong>en</strong>tar expresiones <strong>en</strong> memoria; es<strong>en</strong>cialm<strong>en</strong>te, <strong>en</strong><br />

compiladores <strong>de</strong> l<strong>en</strong>guaje <strong>de</strong> programación. La Figura 16. I8 muestra un árbol binario <strong>de</strong> expresiones<br />

para la expresión aritmética ( 3. + b) * c.<br />

Obsérvese que los paréntesis no se almac<strong>en</strong>an <strong>en</strong> el árbol pero están implicados <strong>en</strong> la forma <strong>de</strong>l<br />

Figura 16.18. Árbol binario <strong>de</strong> expresiones que repres<strong>en</strong>ta (c~ t I)) *


Árboles 515<br />

árbol. Si se supone que todos los operadores ti<strong>en</strong><strong>en</strong> dos operandos, se pue<strong>de</strong> repres<strong>en</strong>tar una expresión<br />

por un árbol binario cuya raíz conti<strong>en</strong>e un operador y cuyos subárboles izquierdo y <strong>de</strong>recho son los<br />

operandos izquierdo y <strong>de</strong>recho respectivam<strong>en</strong>te. Cada operando pue<strong>de</strong> ser una letra (x, y, a, b,<br />

etc.) o una subexpresión repres<strong>en</strong>tada como un subárbol. En la Figura 16.19 se pue<strong>de</strong> ver como el<br />

operador que está <strong>en</strong> la raíz es *, su subárbol izquierdo repres<strong>en</strong>ta la subexpresión (x + y ) y su<br />

subárbol <strong>de</strong>recho repres<strong>en</strong>ta la subexpresión (a-b) . El nodo raíz <strong>de</strong>l subárbol izquierdo conti<strong>en</strong>e el<br />

operador (+) <strong>de</strong> la subexpresión izquierda y el nodo raíz <strong>de</strong>l subárbol <strong>de</strong>recho conti<strong>en</strong>e el operador (-)<br />

<strong>de</strong> la subexpresión <strong>de</strong>recha. Todos los operandos letras se almac<strong>en</strong>an <strong>en</strong> nodos hojas.<br />

Utilizando el razonami<strong>en</strong>to anterior, se pue<strong>de</strong> escribir la expresión almac<strong>en</strong>ada <strong>en</strong> la Figura 16.20<br />

como<br />

Figura 16.19. Árbol <strong>de</strong> expresión ( x t y ) * ( 1))<br />

(x" (y-z) ) + (a-b)<br />

<strong>en</strong> don<strong>de</strong> se han insertado paréntesis alre<strong>de</strong>dor <strong>de</strong> subexpresiones <strong>de</strong>l árbol (la operación y-Z,<br />

subexpresión inás interna, ti<strong>en</strong>e el nivel <strong>de</strong> prioridad mayor).<br />

Figura 16.20. Árbol <strong>de</strong> expresión (x* (y-/) ) + (a-1)).


516 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 16.5<br />

Deducir las expresiones que repres<strong>en</strong>tan los sigui<strong>en</strong>tes árboles binarios.<br />

Soluciones<br />

(a) X*(Y/-Z)<br />

(b) A + (B*- (c+D )<br />

(c) (A* (X+Y ) *C<br />

Ejemplo 16.6<br />

Dibujar la repres<strong>en</strong>tación <strong>en</strong> árbol binario <strong>de</strong> cada una <strong>de</strong> las sigui<strong>en</strong>tes expresiones.<br />

(a) X*Y/ (A+B)*C<br />

(6) X*Y/A+B*C<br />

Soluciones<br />

16.6.1. Reglas para la construcción <strong>de</strong> árboles <strong>de</strong> expresión<br />

Los árboles <strong>de</strong> expresiones se utilizan <strong>en</strong> las coinputadoras para evaluar expresiones usadas <strong>en</strong><br />

programas. El algoritmo más s<strong>en</strong>cillo para construir un árbol <strong>de</strong> expresión es uno que lee una expresión<br />

completa que conti<strong>en</strong>e paréiitesis <strong>en</strong> la misma. Una expresión con paréntesis es aquella <strong>en</strong> que


F-<br />

(8+9)<br />

~ ~-~<br />

1<br />

Árboles 517<br />

1. La prioridad se <strong>de</strong>termina sólo por paréntesis.<br />

2. La expresión completa se sitúa <strong>en</strong>tre paréntesis.<br />

Por consigui<strong>en</strong>te ( 4 t ( 5 * 6 ) ) es un ejemplo <strong>de</strong> una expresión completa <strong>en</strong>tre paréntesis. Su valor es<br />

3 4. Si se <strong>de</strong>sean cambiar las priorida<strong>de</strong>s, se escribe ( ( 4 t 5 ) * 6 ) , su valor es 5 4. A fin <strong>de</strong> ver la prioridad<br />

<strong>en</strong> las expresiones, considérese la expresión<br />

(4*5) + 6/7 - (8+9)<br />

Los operadores con prioridad más alta son * y /: es <strong>de</strong>cir,<br />

(4*5) + (6/7) ~<br />

El ord<strong>en</strong> <strong>de</strong> los operadores aquí es + y -. Por consigui<strong>en</strong>te, se pue<strong>de</strong> escribir<br />

((4*5) + (6/7)) - (8+9)<br />

Por último la expresión completa <strong>en</strong>tre paréntesis será<br />

(((4*5) t (6/7)) - (8+9))<br />

El algoritmo para la construcción <strong>de</strong> un árbol <strong>de</strong> expresión es:<br />

1. La primera vez que se <strong>en</strong>cu<strong>en</strong>tra un paréntesis a izquierda, crea un nodo y lo hace <strong>en</strong> el raíz. Se<br />

llama a éste, el nodo actual y se sitúa su puntero <strong>en</strong> una pila.<br />

2. Cada vez que se <strong>en</strong>cu<strong>en</strong>tre un nuevo paréntesis a izquierda, crear un nuevo nodo. Si el nodo<br />

actual no ti<strong>en</strong>e un hijo izquierdo, hacer el nuevo nodo el hijo izquierdo; <strong>en</strong> caso contrario, hacerlo<br />

el hijo <strong>de</strong>recho. Hacer el nuevo nodo el nodo actual y situar su puntero <strong>en</strong> una pila.<br />

3. Cuando se <strong>en</strong>cu<strong>en</strong>tra un operando, crear un nuevo nodo y asignar el operando a su campo <strong>de</strong><br />

<strong>datos</strong>. Si el nodo actual no ti<strong>en</strong>e un hijo izquierdo, hacer el nuevo nodo el hijo izquierdo; <strong>en</strong> caso<br />

contrario, hacerlo el hijo <strong>de</strong>recho.<br />

4. Cuando se <strong>en</strong>cu<strong>en</strong>tra un operador, sacar un puntero <strong>de</strong> la pila y situar el operador <strong>en</strong> el campo<br />

<strong>datos</strong> <strong>de</strong>l nodo <strong>de</strong>l puntero.<br />

5. Ignorar paréntesis <strong>de</strong>recho y blancos.<br />

Ejemplo 16.7<br />

Calcular las expresiones correspondi<strong>en</strong>tes <strong>de</strong> los árboles <strong>de</strong> expresión.


51 8 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Las soluciones correspondi<strong>en</strong>tes son:<br />

U. (a * b) + ( c / d) c* ( ( -d ) + (X + y)) / ((+b)* (C * d))<br />

b. ((a + b) + c) + d<br />

Ejercicio 16.1 (a realizar por el lector)<br />

Dibujar los árboles hinarios <strong>de</strong> expresión c.orre.si~ondi<strong>en</strong>te a cada una <strong>de</strong> las sigui<strong>en</strong>tes expresiones:<br />

(U) (a + b) / (c - d * e) + e + 9 * h/a<br />

(h) -x -y * z + (a + b + c / d * e)<br />

(c) ((a + b) > (c - e)) I I a < f && (x < y I I y > z)<br />

16.7. RECORRIDO DE UN ÁRBOL<br />

Para visualizar o consultar los <strong>datos</strong> almac<strong>en</strong>ados <strong>en</strong> un árbol se necesita recorrer el árbol o visitar los<br />

nodos <strong>de</strong>l mismo. Al contrario que las listas <strong>en</strong>lazadas, los árboles binarios no ti<strong>en</strong><strong>en</strong> realm<strong>en</strong>te un<br />

primer valor, un segundo valor, tercer valor, etc. Se pue<strong>de</strong> afirmar que el raíz vi<strong>en</strong>e el primero, pero<br />

¿,quién vi<strong>en</strong>e a continuación? Exist<strong>en</strong> difer<strong>en</strong>tes métodos <strong>de</strong> recorrido <strong>de</strong> árbol ya que la mayoría <strong>de</strong> las<br />

aplicaciones binarias son bastante s<strong>en</strong>sibles al ord<strong>en</strong> <strong>en</strong> el que se visitan los nodos, <strong>de</strong> forma que será<br />

preciso elegir cuidadosam<strong>en</strong>te el tipo <strong>de</strong> recorrido.<br />

Un recorrido <strong>de</strong> un árbol binario requiere que cada nodo <strong>de</strong>l árbol sea procesado (visitado) una<br />

vez y sólo una <strong>en</strong> una secu<strong>en</strong>cia pre<strong>de</strong>terminada. Exist<strong>en</strong> dos <strong>en</strong>foques g<strong>en</strong>erales para la secu<strong>en</strong>cia <strong>de</strong><br />

recorrido, profundidad y anchura.<br />

En el recorrido <strong>en</strong> profundidad, el proceso exige un camino <strong>de</strong>s<strong>de</strong> el raíz a través <strong>de</strong> un hijo, al<br />

<strong>de</strong>sc<strong>en</strong>di<strong>en</strong>te más lejano <strong>de</strong>l primer hijo antes <strong>de</strong> proseguir a un segundo hijo. En otras palabras, <strong>en</strong> el<br />

recorrido <strong>en</strong> profundidad, todos los <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes <strong>de</strong> un hijo se procesan antes <strong>de</strong>l sigui<strong>en</strong>te hijo.<br />

En el recorrido <strong>en</strong> anchura, el proceso se realiza horizontalm<strong>en</strong>te <strong>de</strong>s<strong>de</strong> el raíz a todos sus hijos,<br />

a continuación a los hijos <strong>de</strong> sus hijos y así sucesivam<strong>en</strong>te hasta que todos los nodos han sido<br />

procesados. En otras palabras, <strong>en</strong> el recorrido <strong>en</strong> anchura, cada nivel se procesa totalm<strong>en</strong>te antes <strong>de</strong> que<br />

comi<strong>en</strong>ce el sigui<strong>en</strong>te nivel.<br />

El recorrido <strong>de</strong> un árbol supone visitar cada nodo sólo una vez.<br />

Dado un árbol binario que consta <strong>de</strong> un raíz, un subárbol izquierdo y un subárbol <strong>de</strong>recho se pued<strong>en</strong><br />

<strong>de</strong>finir tres tipos <strong>de</strong> secu<strong>en</strong>cia <strong>de</strong> recorrido <strong>en</strong> profundidad. Estos recorridos estándar se muestran <strong>en</strong> la<br />

Figura 16.2 1.<br />

Su bárbol Subárbol Su bárbol Su bárbol Subárbol Subárbol<br />

izquierdo <strong>de</strong>recho izquierdo <strong>de</strong>recho izquierdo <strong>de</strong>recho<br />

(a) Recorrido preord<strong>en</strong> (b) Recorrido <strong>en</strong> ord<strong>en</strong> (c) Recorrido postord<strong>en</strong><br />

Figura 16.21. Recorridos <strong>de</strong> árboles binarios


Árboles 519<br />

La <strong>de</strong>signación tradicional <strong>de</strong> los recorridos utiliza un nombre para el nodo raíz (N), para el subárbol<br />

izquierdo (I) y para el subárbol <strong>de</strong>recho (DI.<br />

Según sea la estrategia a seguir, los recorridos se conoc<strong>en</strong> como <strong>en</strong>ord<strong>en</strong> (inor<strong>de</strong>r), preord<strong>en</strong><br />

@reor<strong>de</strong>r) y postord<strong>en</strong> (postor<strong>de</strong>r)<br />

Preord<strong>en</strong> (nodo-izquierdo-<strong>de</strong>recho)(NID)<br />

Enord<strong>en</strong> (izquierdo-nodo-<strong>de</strong>recho)(IND)<br />

Postord<strong>en</strong> (izquierdo-<strong>de</strong>recho-nodo)(IDN)<br />

16.7.1. Recorrido preord<strong>en</strong><br />

El recorrido preord<strong>en</strong>' (NID) conlleva los sigui<strong>en</strong>tes pasos, <strong>en</strong> los que el raíz va antes que los subárboles:<br />

1. Recorrer el raíL (N).<br />

2. Recorrer el subárbol izquierdo (I) <strong>en</strong> preord<strong>en</strong>.<br />

3. Recorrer el subárbol <strong>de</strong>recho (D) <strong>en</strong> preord<strong>en</strong>.<br />

Dado las características recursivas <strong>de</strong> los árboles, el algoritmo <strong>de</strong> recorrido ti<strong>en</strong>e naturaleza<br />

recursiva. Primero, se procesa la raíz, a continuación el subárbol izquierdo y a continuación el subárbol<br />

<strong>de</strong>recho. Para procesar el subárbol izquierdo, se hace una llamada recursiva al procedimi<strong>en</strong>to preord<strong>en</strong><br />

y luego se hace lo mismo con el subárbol <strong>de</strong>recho. El algoritmo recursivo correspondi<strong>en</strong>te para un árbol<br />

T es:<br />

si T no es vacio <strong>en</strong>tonces<br />

inicio<br />

ver los <strong>datos</strong> <strong>en</strong> el rai/ <strong>de</strong> T<br />

Preord<strong>en</strong> (subarbol i/quierdo <strong>de</strong>l rrilz <strong>de</strong> T)<br />

Preord<strong>en</strong> (subarbol <strong>de</strong>recho <strong>de</strong>l r diL <strong>de</strong> T)<br />

fin<br />

Regla<br />

En el recorrido preord<strong>en</strong>, el raíz se procesa antes que los subárboles izquierdo y <strong>de</strong>recho.<br />

Si utilizamos el recorrido preord<strong>en</strong> <strong>de</strong>l árbol <strong>de</strong> la Figura 16.22 se visita primero el raíz (nodo A).<br />

A continuación se visita el subárbol izquierdo <strong>de</strong> A, que consta <strong>de</strong> los nodos B, D y E. Dado que el<br />

subárbol es a su vez un árbol, se visitan los nodos utilizando el ord<strong>en</strong> NID. Por consigui<strong>en</strong>te, se visita<br />

primero el nodo B, <strong>de</strong>spués D (izquierdo) y, por último, E (<strong>de</strong>recho).<br />

A<br />

Camino A, 6, 0, E, C, F, G<br />

3 4 6 7<br />

Figura 16.22. Recorrido preord<strong>en</strong> <strong>de</strong> un árbol binario.<br />

' El noiribrc prwrúr/i. vi<strong>en</strong>e <strong>de</strong>l prefijo latino prc qiic signilica (


520 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

A continuación se visita el subárbol <strong>de</strong>recho <strong>de</strong> A, que es un árbol que conti<strong>en</strong>e los nodos c , F y<br />

G. De nuevo sigui<strong>en</strong>do el ord<strong>en</strong> NID, se visita primero el nodo C, a continuación F (izquierdo) y, por<br />

Último, G (<strong>de</strong>recho). En consecu<strong>en</strong>cia el ord<strong>en</strong> <strong>de</strong>l recorrido preord<strong>en</strong> para el árbol <strong>de</strong> la Figura 16.22 es<br />

A-B-D-E-C-F-G.<br />

Un refinami<strong>en</strong>to <strong>de</strong>l algoritmo es:<br />

algoritmo preOrd<strong>en</strong> (val rai7 )<br />

Recorrer un arbol binario <strong>en</strong> secu<strong>en</strong>cia nodo-izdo-dcho<br />

Pre raiz es el nodo <strong>de</strong> <strong>en</strong>trada <strong>de</strong>l árbol o subárbol<br />

Post cada nodo se procesa <strong>en</strong> ord<strong>en</strong><br />

1 si (raiz no es nulo)<br />

1 procesar (raiz)<br />

2 preOrd<strong>en</strong> (raiz -> cubarbolTzdo)<br />

3 preOrd<strong>en</strong> (raiz -> subarbolDcho<br />

2 return<br />

La función preord<strong>en</strong> muestra el código fu<strong>en</strong>te <strong>en</strong> C <strong>de</strong>l algoritmo ya citado anteriorm<strong>en</strong>te. El tipo<br />

<strong>de</strong> los <strong>datos</strong> es <strong>en</strong>tero.<br />

type<strong>de</strong>f int TipoElem<strong>en</strong>to;<br />

struct nodo {<br />

TipoElem<strong>en</strong>to <strong>datos</strong>;<br />

struct nodo *hijo-izdo, *hijo-dcho;<br />

};<br />

type<strong>de</strong>f struct nodo Nodo;<br />

void preord<strong>en</strong> (Nodo *p)<br />

I<br />

1<br />

if (P)<br />

i<br />

printf ("%d ",p -> ddtos) ;<br />

PreOrd<strong>en</strong>(p -> hijo-izdo);<br />

PreOrd<strong>en</strong>(p -> hijo-dcho);<br />

1<br />

Gráficas <strong>de</strong> las llamadas recursivas <strong>de</strong> preord<strong>en</strong><br />

El recorrido recursivo <strong>de</strong> un árbol se pue<strong>de</strong> mostrar gráficam<strong>en</strong>te por dos métodos distintos: 1) paseo<br />

preord<strong>en</strong> <strong>de</strong>l árbol; 2) recorrido algorítmico.<br />

Un medio gráfico para visualizar el recorrido <strong>de</strong> un árbol es imaginar que se está dando un «paseo»<br />

alre<strong>de</strong>dor <strong>de</strong>l árbol com<strong>en</strong>zando por la raíz y sigui<strong>en</strong>do el s<strong>en</strong>tido contrario a las agujas <strong>de</strong>l reloj, un<br />

nodo a continuación <strong>de</strong> otro sin pasar dos veces por el mismo nodo. El camino señalado por una línea<br />

continua que comi<strong>en</strong>za <strong>en</strong> el nodo 1 (Fig. 16.2 1 ) muestra el recorrido preord<strong>en</strong> completo. En el caso <strong>de</strong><br />

la Figura 16.22 el recorrido es A H D E C F G.<br />

El otro medio gráfico <strong>de</strong> mostrar el recorrido algorítmico recursivo es similar a las difer<strong>en</strong>tes etapas<br />

<strong>de</strong>l algoritmo. Así la primera llamada procesa la raíz <strong>de</strong>l árbol A. A continuación se llama recursivam<strong>en</strong>te<br />

a procesar subárbol izquierdo, procesa el nodo B. La tercera llamada procesa el nodo D, que es un<br />

subárbol formado por un único nodo. En ese punto, se llama <strong>en</strong> preord<strong>en</strong>, con un puntero nulo, que<br />

produce un retorno inmediato al subárbol U para procesar a su subárbol <strong>de</strong>recho. Debido a que el<br />

subárbol <strong>de</strong>recho <strong>de</strong> D es también nulo, se vuelve al nodo B <strong>de</strong> modo que va a procesar (visitar) su<br />

subárbol <strong>de</strong>recho, E. Después <strong>de</strong> procesar el nodo E, se hac<strong>en</strong> dos llamadas más, una con el puntero<br />

izquierdo null <strong>de</strong> E y otra con su puntero <strong>de</strong>recho null. Como el subárbol R ha sido totalm<strong>en</strong>te procesado,<br />

se vuelve a la raíz <strong>de</strong>l árbol y se procesa su subárbol <strong>de</strong>recho, C. Después <strong>de</strong> procesar C, llama para<br />

procesar su subárbol izquierdo F'. Se hac<strong>en</strong> dos llamadas con null, vuelve al nivel don<strong>de</strong> está el nodo c<br />

para procesar su rama <strong>de</strong>recha G. Aún se realizan dos llamadas más, una al subárbol izquierdo null y otra<br />

al subárbol <strong>de</strong>recho. Entonces se retorna <strong>en</strong> el árbol, se concluye el recorrido <strong>de</strong>l árbol.<br />

j


Árboles 521<br />

16.7.2. Recorrido <strong>en</strong>ord<strong>en</strong><br />

El recorrido <strong>en</strong> ord<strong>en</strong> (inor<strong>de</strong>r) procesa primero el subárbol izquierdo, <strong>de</strong>spués el raíz y a continuación<br />

el subárbol <strong>de</strong>recho. El signiticado <strong>de</strong> in es que la raíz se procesa <strong>en</strong>tre los subárboles. Si el árbol no está<br />

vacío, el método implica los sigui<strong>en</strong>tes pasos:<br />

1. Recorrer el subjrbol izquierdo (1)<strong>en</strong> inord<strong>en</strong>.<br />

2. Visitar el nodo raíz (N).<br />

3. Recorrer el subárbol <strong>de</strong>recho (I)) <strong>en</strong> inord<strong>en</strong>.<br />

El algoritmo correspondi<strong>en</strong>te es:<br />

Enord<strong>en</strong>(A)<br />

si el arbol no esta vacio <strong>en</strong>tonces<br />

inicio<br />

Recorrer el subarbol izqu i erdo<br />

Visitar el nodo raiz<br />

Recorrer el subarbol <strong>de</strong>recho<br />

fin<br />

Un refinami<strong>en</strong>to <strong>de</strong>l algoritmo es:<br />

algoritmo <strong>en</strong>ord<strong>en</strong> (vdl rai z )<br />

Recorrer un &,bol binario <strong>en</strong> la secu<strong>en</strong>cia izquierdo-nodo-<strong>de</strong>recho<br />

pre raíz <strong>en</strong> el nodo <strong>de</strong> <strong>en</strong>tradd <strong>de</strong> un árbol o subárbol<br />

post cada nodo se ha <strong>de</strong> pr-ocesdr <strong>en</strong> ord<strong>en</strong><br />

1 si (raíz no es nulo)<br />

1 <strong>en</strong>ord<strong>en</strong> (rai z -> subarbol Izyii i erdo)<br />

2 procesar (raiz)<br />

3 <strong>en</strong>ord<strong>en</strong> (rd i z~-siibiirbolDerecho)<br />

2 retorno<br />

fin <strong>en</strong>ord<strong>en</strong><br />

En el árbol <strong>de</strong> la Figura 16.23, los nodos se han numerado <strong>en</strong> el ord<strong>en</strong> <strong>en</strong> que son visitados durante<br />

el recorrido <strong>en</strong>ord<strong>en</strong>. El primer subárbol recorrido es el subárbol izquierdo <strong>de</strong>l nodo raíz (árbol cuyo<br />

nodo conti<strong>en</strong>e la letra 5. Este subárbol consta <strong>de</strong> los nodos 5, D y E y es a su vez otro árbol con el nodo<br />

B como raíz, por lo que sigui<strong>en</strong>do el ord<strong>en</strong> IND, se visita primero D, a continuación B (nodo raíz) y,<br />

por último, E (<strong>de</strong>recha). Después <strong>de</strong> la visita a este subárbol izquierdo se visita el nodo raíz A y, por<br />

Último, se visita el subárbol <strong>de</strong>recho que consta <strong>de</strong> los nodos c , F y G. A continuación, sigui<strong>en</strong>do el<br />

ord<strong>en</strong> IND para el subárbol <strong>de</strong>recho, se visita primero F, <strong>de</strong>spués c (nodo raíz) y, por Último, G. Por<br />

consigui<strong>en</strong>te, el ord<strong>en</strong> <strong>de</strong>l recorrido inord<strong>en</strong> <strong>de</strong> la Figura 16.23 es D-B-E-A-F-C-G.<br />

i<br />

1 3 5 7<br />

Figura 16.23. Recorrido <strong>en</strong>ord<strong>en</strong> <strong>de</strong> un árbol binario.


522 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

La sigui<strong>en</strong>te función visita y escribe el cont<strong>en</strong>ido <strong>de</strong> los nodos <strong>de</strong> un árbol binario <strong>de</strong> acuerdo al<br />

recorrido EnOrd<strong>en</strong>. La función ti<strong>en</strong>e como parámetro un puntero al nodo raíz <strong>de</strong>l árbol.<br />

void <strong>en</strong>ord<strong>en</strong> (Nodo *p)<br />

I<br />

if (P)<br />

1<br />

<strong>en</strong>ord<strong>en</strong>(p -> hijo-izqdo);<br />

printf ("%d ",p -> <strong>datos</strong>) ;<br />

<strong>en</strong>ord<strong>en</strong> (p -> hijo-dcho);<br />

/* recorrer subárbol izquierdo */<br />

/* visitar la raíz */<br />

/* recorrer subárbol <strong>de</strong>recho */<br />

16.7.3. Recorrido postord<strong>en</strong><br />

El recorrido postord<strong>en</strong> (IDN) procesa el nodo raíz (post) <strong>de</strong>spués <strong>de</strong> que los subárboles izquierdo y<br />

<strong>de</strong>recho se han procesado. Se comi<strong>en</strong>za situándose <strong>en</strong> la hoja más a la izquierda y se procesa. A<br />

continuación se procesa su subárbol <strong>de</strong>recho. Por último se procesa el nodo raíz. Las etapas <strong>de</strong>l<br />

algoritmo son:<br />

1. Recorrer el subárbol izquierdo (I) <strong>en</strong> postord<strong>en</strong>.<br />

2. Recorrer el subárbol <strong>de</strong>recho (D) <strong>en</strong> postord<strong>en</strong>.<br />

3. Visitar el nodo raíz (N).<br />

El algoritmo recursivo para un árbol A es:<br />

si A no esta vacio <strong>en</strong>tonces<br />

inicio<br />

Postord<strong>en</strong> (subarbol izquierdo <strong>de</strong>l r aíz <strong>de</strong> A)<br />

Postord<strong>en</strong> (subarbol <strong>de</strong>recho <strong>de</strong>l r aíz <strong>de</strong> A)<br />

Visitar la raíz <strong>de</strong> A<br />

fin<br />

El refinami<strong>en</strong>to <strong>de</strong>l algoritmo es:<br />

algoritmo postord<strong>en</strong> (val raiz )<br />

Recorrer un árbol binario <strong>en</strong> secu<strong>en</strong>cia izquierda-<strong>de</strong>recha-nodo<br />

pre raíz es el nodo <strong>de</strong> <strong>en</strong>trada <strong>de</strong> un árbol a un subárbol<br />

post cada nodo ha sido procesado <strong>en</strong> ord<strong>en</strong><br />

í Si (raíznoesnulo)<br />

IpostOrd<strong>en</strong> (raíz -> SubarbolIzdo)<br />

2postOrd<strong>en</strong> (raíz -> SubarbolDcho)<br />

3procesar (raiz)<br />

2 retorno<br />

fin postord<strong>en</strong><br />

Si se utiliza el recorrido postord<strong>en</strong> <strong>de</strong>l árbol <strong>de</strong> la Figura 16.24, se visita primero el subárbol<br />

izquierdo A. Este subárbol consta <strong>de</strong> los nodos B , D y E y sigui<strong>en</strong>do el ord<strong>en</strong> IDN, se visitará primero D<br />

(izquierdo), luego E (<strong>de</strong>recho) y, por Último, B (nodo). A continuación, se visita el subárbol <strong>de</strong>recho A<br />

que consta <strong>de</strong> los nodos c , F y G. Sigui<strong>en</strong>do el ord<strong>en</strong> IDN para este árbol, se visita primero F (izquierdo),<br />

<strong>de</strong>spués G (<strong>de</strong>recho) y, por Último, c (nodo). Finalm<strong>en</strong>te se visita el raíz A (nodo). Así el ord<strong>en</strong> <strong>de</strong>l<br />

recorrido postord<strong>en</strong> <strong>de</strong>l árbol <strong>de</strong> la Figura 16.24 es D-E-B-F-G-C-A.


Árboles 523<br />

1 2 4 5<br />

Figura 16.24. Recorrido postord<strong>en</strong> <strong>de</strong> un árbol binario.<br />

La función postord<strong>en</strong> que implem<strong>en</strong>ta <strong>en</strong> C el código fu<strong>en</strong>te <strong>de</strong>l algoritmo correspondi<strong>en</strong>te<br />

void postord<strong>en</strong> (Nodo *p)<br />

{<br />

1<br />

if (P)<br />

t<br />

postord<strong>en</strong> (p -> hijo-izqdo);<br />

postord<strong>en</strong> (p -> hijo-dcho);<br />

printf ("%d ",p -> <strong>datos</strong>) ;<br />

1


524 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejercicio 16.2<br />

( 4<br />

Figura 16.25. Árboles <strong>de</strong> expresión.<br />

Si la,función visitar (1 se reemplaza por la .s<strong>en</strong>t<strong>en</strong>cia.<br />

printf ("%d ",t -> dato) ;<br />

<strong>de</strong>ducir los elem<strong>en</strong>tos <strong>de</strong> los árboles binarios sigui<strong>en</strong>tes <strong>en</strong> cada uno <strong>de</strong> los tres recorridos<br />

fundam<strong>en</strong>tales.<br />

Los elem<strong>en</strong>tos <strong>de</strong> los árboles binarios listados <strong>en</strong> preord<strong>en</strong>, <strong>en</strong>ord<strong>en</strong> y postord<strong>en</strong>.<br />

I Árbola Árbol 15 Árbol c<br />

PreOrd<strong>en</strong> +*ab/cd +++abcd /+- a + xy * +b * cd<br />

En Ord<strong>en</strong> a*c+c/d a+b+c+d -a+x+y/+b* c * d<br />

Pos tord<strong>en</strong> ab*cd/ + ab+c+d+ a - xy ++ b + cd ** /<br />

16.7.4. Profundidad <strong>de</strong> un árbol binario<br />

La profundidad <strong>de</strong> un árbol binario es una característica que se necesita conocer con frecu<strong>en</strong>cia durante<br />

el <strong>de</strong>sarrollo <strong>de</strong> una aplicación con árboles. La función Prof undidad evalúa la profundidad <strong>de</strong> un árbol<br />

binario. Para ello ti<strong>en</strong>e un parámetro que es un puntero a la raíz <strong>de</strong>l árbol.<br />

El caso más s<strong>en</strong>cillo <strong>de</strong> cálculo <strong>de</strong> la profundidad es cuando el árbol está vacío <strong>en</strong> cuyo caso la<br />

profundidad es O. Si el árbol no está vacío, cada subárbol <strong>de</strong>be t<strong>en</strong>er su propia profundidad, por lo que<br />

se necesita evaluar cada una por separado. Las variables profundidad1, profundidadD almac<strong>en</strong>arán<br />

las profundida<strong>de</strong>s <strong>de</strong> los subárboles izquierdo y <strong>de</strong>recho respectivam<strong>en</strong>te.<br />

El método <strong>de</strong> cálculo <strong>de</strong> la profundidad <strong>de</strong> los subárboles utiliza llamadas recursivas a la función<br />

Profundidad con punteros a los respectivos subárboles como parámetros <strong>de</strong> la misma. La fun-


Árboles 525<br />

ción Profundidad <strong>de</strong>vuelve como resultado la profundidad <strong>de</strong>l subárbol mas profundo más I (la misma<br />

<strong>de</strong>l raíz).<br />

int Profundidad (Nodo *p)<br />

{<br />

i<br />

if (!p)<br />

return O ;<br />

else<br />

i<br />

int profundidad1 = Profundiddd (p -> hijo-izqdo);<br />

int profundidadD = Profundidad (p -> hijo-dcho) ;<br />

if (profundidad1 ’> profimdidadD)<br />

return profundidad1 + 1;<br />

else<br />

return profundidadD + 1;<br />

1<br />

16.8. ÁRBOL BINAR10 DE BÚSQUEDA<br />

Los árboles vistos hasta ahora no ti<strong>en</strong><strong>en</strong> un ord<strong>en</strong> <strong>de</strong>finido; sin embargo, los árboles binarios ord<strong>en</strong>ados<br />

ti<strong>en</strong><strong>en</strong> s<strong>en</strong>tido. Estos árboles se d<strong>en</strong>ominan árboles binarios <strong>de</strong> búsqueda, <strong>de</strong>bido a que se pued<strong>en</strong> buscar<br />

<strong>en</strong> ellos un término utilizando un algoritmo <strong>de</strong> búsqueda binaria similar al empleado <strong>en</strong> arrays.<br />

Un árbol binario <strong>de</strong> búsqueda es aquel que dado un nodo, todos los <strong>datos</strong> <strong>de</strong>l subárbol izquierdo<br />

son m<strong>en</strong>ores que los <strong>datos</strong> <strong>de</strong> ese nodo, mi<strong>en</strong>tras que todos los <strong>datos</strong> <strong>de</strong>l subárbol <strong>de</strong>recho son mayores<br />

que sus propios <strong>datos</strong>. El árbol binario <strong>de</strong>l Ejemplo 16.8 es <strong>de</strong> búsqueda.<br />

Ejemplo 16.8<br />

Árbol binario <strong>de</strong> búsqueda.<br />

30 m<strong>en</strong>or que 55<br />

41 mayor que 30<br />

75 mayor que 55<br />

85 mayor que 75<br />

4 m<strong>en</strong>or que 30<br />

16.8.1. Creación <strong>de</strong> un árbol binario <strong>de</strong> búsqueda<br />

Supongamos que se <strong>de</strong>sea almac<strong>en</strong>ar los números 8 3 1 20 10 5 4 <strong>en</strong> un árbol binario <strong>de</strong> búsqueda.<br />

Sigui<strong>en</strong>do la regla, dado un nodo <strong>en</strong> el árbol todos los <strong>datos</strong> a su izquierda <strong>de</strong>b<strong>en</strong> ser m<strong>en</strong>ores que todos<br />

los <strong>datos</strong> <strong>de</strong>l nodo actual, mi<strong>en</strong>tras que todos los <strong>datos</strong> a la <strong>de</strong>recha <strong>de</strong>b<strong>en</strong> ser mayores que los <strong>datos</strong>.<br />

Inicialm<strong>en</strong>te el árbol está vacío y se <strong>de</strong>sea insertar el 8. La única elección es almac<strong>en</strong>ar el 8 <strong>en</strong> el raíz:


L<br />

526 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

A continuación vi<strong>en</strong>e el 3. Ya que 3 es m<strong>en</strong>or que 8, el 3 <strong>de</strong>be ir <strong>en</strong> el subárbol izquierdo.<br />

A continuación se ha <strong>de</strong> insertar 1 que es m<strong>en</strong>or que 8 y que 3, por consigui<strong>en</strong>te irá a la izquierda<br />

y <strong>de</strong>bajo <strong>de</strong> 3.<br />

El sigui<strong>en</strong>te número es 20, mayor que 8, lo que implica <strong>de</strong>be ir a la <strong>de</strong>recha <strong>de</strong> 8.<br />

Cada nuevo elem<strong>en</strong>to se inserta como una hoja <strong>de</strong>l árbol. Los restantes elem<strong>en</strong>tos se pued<strong>en</strong> situar<br />

fácilm<strong>en</strong>te.<br />

Una propiedad <strong>de</strong> los árboles binarios <strong>de</strong> búsqueda es que no son únicos para los mismos <strong>datos</strong>.


Árboles 527<br />

Ejemplo 16.9<br />

Construir un árbol binario para almac<strong>en</strong>ar los <strong>datos</strong> 12, 8, 7, 16 y 14.<br />

Solución<br />

Ejemplo 16.1 O<br />

Construir un árbol binario <strong>de</strong> búsqueda que corresponda a un recorrido <strong>en</strong>ord<strong>en</strong> cuyos elem<strong>en</strong>tos<br />

son: 1, 3, 4, 5, 6, 7, 8, 9 y 10.<br />

16.8.2. Implem<strong>en</strong>tación <strong>de</strong> un nodo <strong>de</strong> un árbol binario <strong>de</strong> búsqueda<br />

Un árbol binario <strong>de</strong> búsqueda se pue<strong>de</strong> utilizar cuando se necesita que la información se <strong>en</strong>cu<strong>en</strong>tre<br />

rápidam<strong>en</strong>te. Estudiemos un ejemplo <strong>de</strong> árbol binario <strong>en</strong> el que cada nodo conti<strong>en</strong>e información relativa<br />

a una persona. Cada nodo almac<strong>en</strong>a un nombre <strong>de</strong> una persona y el número <strong>de</strong> matrícula <strong>en</strong> su<br />

universidad (dato <strong>en</strong>tero).<br />

Declaración <strong>de</strong> tipos<br />

Nombre<br />

Matrícula<br />

Tipo <strong>de</strong> dato cad<strong>en</strong>a ( string )<br />

Tipo <strong>en</strong>tero<br />

struct nodo {<br />

int nummat;<br />

char nombre [ 3 O 1 ;<br />

struct nodo *izda, *dcha;<br />

1;<br />

type<strong>de</strong>f struct nodo Nodo;<br />

Nombre<br />

nummat<br />

A


528 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Creación <strong>de</strong> un nodo<br />

La función ti<strong>en</strong>e como <strong>en</strong>trada un dato <strong>en</strong>tero que repres<strong>en</strong>ta un número <strong>de</strong> matrícula y el nombre.<br />

Devuelve un puntero al nodo creado.<br />

Nodo* CredrNodo ( int id, char* n)<br />

I<br />

Nodo* t ;<br />

L = (Nodo*) malloc(si/eoí (Nodo));<br />

t --, nummdt = ,id;<br />

ctrcpy(t->nombre,n) ;<br />

t ->izdd = t -> dchd = NULL;<br />

return t;<br />

16.9. OPERACIONES EN ÁRBOLES BlNARlOS DE BÚSQUEDA<br />

De lo expuesto se <strong>de</strong>duce que los árboles binarios ti<strong>en</strong><strong>en</strong> naturaleza recursiva y <strong>en</strong> consecu<strong>en</strong>cia las<br />

operaciones sobre los árboles son recursivas, si bi<strong>en</strong> siempre t<strong>en</strong>emos la opción <strong>de</strong> realizarlas <strong>de</strong> form?<br />

iterativa. Estas operaciones son:<br />

O Búsquedu <strong>de</strong> un nodo.<br />

Inserción <strong>de</strong> un nodo.<br />

Kecorriúo <strong>de</strong> un árbol.<br />

Borrudo <strong>de</strong> un nodo.<br />

16.9.1. Búsqueda<br />

La búsqueda <strong>de</strong> un nodo comi<strong>en</strong>za <strong>en</strong> el nodo raíz y sigue estos pasos:<br />

1. La clave buscada se compara con la clave <strong>de</strong>l nodo raíz.<br />

2. Si las claves son iguales, la búsqueda se <strong>de</strong>ti<strong>en</strong>e.<br />

3. Si la clave buscada es mayor que la clave raíz, la búsqueda se reanuda <strong>en</strong> el subárbol <strong>de</strong>recha. Si<br />

la clave buscada es m<strong>en</strong>or que la clave raíz, la búsqueda se reanuda con el subárbol izquierdo.<br />

Buscar una información específica<br />

Si se <strong>de</strong>sea <strong>en</strong>contrar un nodo <strong>en</strong> el árbol que cont<strong>en</strong>ga la información sobre una persona específica. La<br />

función buscar ti<strong>en</strong>e dos parámetros, un puntero al árbol y un número <strong>de</strong> inatrícula para la persona<br />

requerida. Como resultado, la función <strong>de</strong>vuelve un puntero al nodo <strong>en</strong> el que se almac<strong>en</strong>a la información<br />

sobre esa persona; <strong>en</strong> el caso <strong>de</strong> que la información sobre la persona no se <strong>en</strong>cu<strong>en</strong>tra se <strong>de</strong>vuelve el<br />

valor O. El algoritmo <strong>de</strong> búsqueda es el sigui<strong>en</strong>te:<br />

1. Comprobar si el árbol está vacío.<br />

En caso afirmativo se <strong>de</strong>vuelve O.<br />

Si la raíz conti<strong>en</strong>e la persona, la tarea es fácil: el resultado es, simplem<strong>en</strong>te, un puntero a la raíz.<br />

2. Si el árbol no está vacío, el subárbol específico <strong>de</strong>p<strong>en</strong><strong>de</strong> <strong>de</strong> que el número <strong>de</strong> matrícula requerido<br />

es más pequeño o mayor que el número <strong>de</strong> matrícula <strong>de</strong>l nodo raíz.<br />

3. La función <strong>de</strong> búsqueda se consigue llamando recursivam<strong>en</strong>te a la función buscar con un<br />

puntero al subárbol izquierdo o <strong>de</strong>recho como paráinetro.<br />

El código C <strong>de</strong> la función buscar. es:<br />

Nodo* buscar (Nodo* p, int buscddo)<br />

i


P-<br />

Árboles 529<br />

i<br />

if (!p)<br />

return O;<br />

else if (buscado == p -> nummdt)<br />

return p;<br />

else if (buscado < p -> nummdt)<br />

return buscar (p -> izdii, buscado);<br />

else /<br />

return buscar (p -> dcha, buscado);<br />

16.9.2. Insertar un nodo<br />

Una característica fundam<strong>en</strong>tal que <strong>de</strong>be poseer el algoritmo <strong>de</strong> inserción es que el árbol resultante <strong>de</strong><br />

una inserción <strong>en</strong> un árbol <strong>de</strong> búsqueda ha <strong>de</strong> ser también <strong>de</strong> búsqueda. En es<strong>en</strong>cia, el algoritmo <strong>de</strong><br />

inserción se apoya <strong>en</strong> la localización <strong>de</strong> un elem<strong>en</strong>to, <strong>de</strong> modo que si se <strong>en</strong>cu<strong>en</strong>tra el elem<strong>en</strong>to (cluve)<br />

buscado, no es necesario hacer nada; <strong>en</strong> caso contrario, se inserta el nuevo elem<strong>en</strong>to justo <strong>en</strong> el lugar<br />

don<strong>de</strong> ha acabado la búsqueda (es <strong>de</strong>cir, <strong>en</strong> el lugar don<strong>de</strong> habría estado <strong>en</strong> el caso <strong>de</strong> existir).<br />

Antes <strong>de</strong> insertar 8 Después <strong>de</strong> insertar 8<br />

Figura 16.26. Inserción <strong>en</strong> un árbol binario <strong>de</strong> búsqueda.<br />

Por ejemplo, considérese el caso <strong>de</strong> añadir el nodo 8 al árbol <strong>de</strong> la Figura 16.26. Se comi<strong>en</strong>za el<br />

recorrido <strong>en</strong> el nodo raíz 25; la posición 8 <strong>de</strong>be estar <strong>en</strong> el subárbol izquierdo <strong>de</strong> 25 (8 < 25). En el nodo<br />

10, la posición <strong>de</strong> 8 <strong>de</strong>be estar <strong>en</strong> el subárbol izquierdo <strong>de</strong> 10, que está actualm<strong>en</strong>te vacío. El nodo 8 se<br />

introduce como un hijo izquierdo <strong>de</strong>l nodo 1 O.<br />

Ejemplo 16.1 1<br />

Insertar un elem<strong>en</strong>to con clave 80 <strong>en</strong> el árbol hinario <strong>de</strong> húsquedu sigui<strong>en</strong>te:


530 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

A continuación insertar un elem<strong>en</strong>to con clave 36 <strong>en</strong> el árbol binario <strong>de</strong> búsqueda resultante.<br />

Solución<br />

(a) Inserción <strong>de</strong> 80 (a) Inserción <strong>de</strong> 36<br />

16.9.3. Función insertar ( )<br />

La función insertar que pone nuevos nodos es s<strong>en</strong>cilla. Se <strong>de</strong>b<strong>en</strong> <strong>de</strong>clarar tres argum<strong>en</strong>tos: un puntero<br />

al raíz <strong>de</strong>l árbol, el nuevo nombre y número <strong>de</strong> matrícula <strong>de</strong> la persona. La función creará un nuevo<br />

nodo para la nueva persona y lo inserta <strong>en</strong> el lugar correcto <strong>en</strong> el árbol <strong>de</strong> modo que el árbol permanezca<br />

como binario <strong>de</strong> búsqueda.<br />

La operación <strong>de</strong> inserción <strong>de</strong> un nodo es una ext<strong>en</strong>sión <strong>de</strong> la operación <strong>de</strong> búsqueda. Los pasos a<br />

seguir son:<br />

1. Asignar memoria para una nueva <strong>estructura</strong> nodo.<br />

2. Buscar <strong>en</strong> el árbol para <strong>en</strong>contrar la posición <strong>de</strong> inserción <strong>de</strong>l nuevo nodo, que se colocará como<br />

nodo hoja.<br />

3. Enlazar el nuevo nodo al árbol.<br />

El código C <strong>de</strong> la función:<br />

void insertar (Nodo** raiz, int nuevomat, char *nuevo-nombre)<br />

i<br />

if (!(*raiz))<br />

*raiz = CrearNodo(nuevo-mat, nuevo-nombre) ;<br />

else if (nuevomat i (*raiz) -> nummat)<br />

insertar (&((*raiz) -> izda), nuevomat, nuevo-nombre);<br />

else<br />

insertar (&((*raiz) -> dcha), nuevomat, nuevo-nombre);<br />

1<br />

Si el árbol está vacío, es fácil insertar la <strong>en</strong>trada <strong>en</strong> el lugar correcto. El nuevo nodo es la raíz <strong>de</strong>l<br />

árbol y el puntero raiz se pone apuntando a ese nodo. El parámetro raiz <strong>de</strong>be ser un parámetro<br />

refer<strong>en</strong>cia ya que <strong>de</strong>be ser leído y actualizado, por esa razón se <strong>de</strong>clara puntero a puntero (Nodo * *) . Si<br />

el árbol no está vacío, se <strong>de</strong>be elegir <strong>en</strong>tre insertar el nuevo nodo <strong>en</strong> el subárbol izquierdo o <strong>de</strong>recho,<br />

<strong>de</strong>p<strong>en</strong>di<strong>en</strong>do <strong>de</strong> que el número <strong>de</strong> matrícula <strong>de</strong> 12 nueva persona sea más pequeño o mayor que el<br />

número <strong>de</strong> matrícula <strong>en</strong> la raíz <strong>de</strong>l árbol.


Árboles 531<br />

16.9.4. Eliminación<br />

La operación <strong>de</strong> eliminación <strong>de</strong> un nodo es también una ext<strong>en</strong>sión <strong>de</strong> la operación <strong>de</strong> búsqueda, si bi<strong>en</strong><br />

más compleja que la inserción <strong>de</strong>bido a que el nodo a suprimir pue<strong>de</strong> ser cualquiera y la operación <strong>de</strong><br />

supresión <strong>de</strong>be mant<strong>en</strong>er la <strong>estructura</strong> <strong>de</strong> árbol binario <strong>de</strong> búsqueda <strong>de</strong>spués <strong>de</strong> la eliminación <strong>de</strong> <strong>datos</strong>.<br />

Los pasos a seguir son:<br />

1. Buscar <strong>en</strong> el árbol para <strong>en</strong>contrar la posición <strong>de</strong> nodo a eliminar.<br />

2. Reajustar los punteros <strong>de</strong> sus antecesores si el nodo a suprimir ti<strong>en</strong>e m<strong>en</strong>os <strong>de</strong> 2 hijos, o subir a<br />

la posición que éste ocupa el nodo más próximo <strong>en</strong> clave (inmediatam<strong>en</strong>te superior o<br />

inmediatam<strong>en</strong>te inferior) con objeto <strong>de</strong> mant<strong>en</strong>er la <strong>estructura</strong> <strong>de</strong> árbol binario.<br />

Ejemplo 16.12<br />

Suprimir el elem<strong>en</strong>to <strong>de</strong> clave 36 <strong>de</strong>l sigui<strong>en</strong>te árbol binario <strong>de</strong> búsqueda:<br />

El árbol resultante es:


~~ ~<br />

532 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

Ejemplo 16.13<br />

Borrar el elem<strong>en</strong>to <strong>de</strong> clave 60 <strong>de</strong>l sigui<strong>en</strong>tc árbol:<br />

i$o,<br />

d<br />

Se reemplaza 60 bi<strong>en</strong> con el elem<strong>en</strong>to mayor (5.5) <strong>en</strong> su subárbol izquierdo o el elem<strong>en</strong>to más<br />

Se reemplaza 60 bi<strong>en</strong> con el elem<strong>en</strong>to mayor (5.5) <strong>en</strong> su subárbol izquierdo o el elem<strong>en</strong>to más<br />

pequeño (70) <strong>en</strong> su subárbol <strong>de</strong>recho. Si se opta por reemplazar con el elem<strong>en</strong>to mayor <strong>de</strong>l subárbol<br />

izquierdo. Se mueve el 5.5 al raíz <strong>de</strong>l subárbol y se reajusta el árbol.<br />

Ejercicio 16.3<br />

Con los registros <strong>de</strong> estudiantes formar un úrbol hinario <strong>de</strong> búsqueda, ord<strong>en</strong>ado respecto al campo<br />

clave numma t. El programa <strong>de</strong>be <strong>de</strong> t<strong>en</strong>er las opciones <strong>de</strong> mostrar los registros ord<strong>en</strong>ados y eliminar<br />

un registro dando el número <strong>de</strong> matrícula.<br />

Análisis<br />

Cada registro ti<strong>en</strong>e sólo dos campos <strong>de</strong> información: nombre y nummat. A<strong>de</strong>más los campos <strong>de</strong> <strong>en</strong>lace<br />

con el subárbol izquierdo y <strong>de</strong>recho.


Árboles 533<br />

Las operaciones que se van a implem<strong>en</strong>tar son las <strong>de</strong> insertar, eliminar, buscdr y<br />

visual izar el árbol. Los <strong>algoritmos</strong> <strong>de</strong> las tres primeras operaciones ya están <strong>de</strong>scritos anteriorm<strong>en</strong>te.<br />

La operación <strong>de</strong> visual izar va a consistir <strong>en</strong> un recorrido <strong>en</strong> inord<strong>en</strong>, cada vez que se visite el nodo<br />

raíz se escribe los <strong>datos</strong> <strong>de</strong>l estudiante.<br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

#inclu<strong>de</strong> <br />

struct nodo {<br />

int nummat;<br />

char nombre [ 3 O I ;<br />

struct nodo *izda, *dcha;<br />

I;<br />

type<strong>de</strong>f struct nodo Nodo;<br />

Nodo* CrearNodo(int id, char* n );<br />

Nodo* buscar (Nodo* p, int buscado);<br />

void insertar (Nodo** raiz, int nuevo-mat, chdr *nuevo-nombre);<br />

void eliminar (Nodo** r, int mat);<br />

void visualizar (Nodo* r);<br />

int main()<br />

i<br />

int nm;<br />

char nom[30] ;<br />

Nodo* R = O;<br />

I<br />

/* Crea el árbol */<br />

do I<br />

printf ("Numero <strong>de</strong> matricula (O -> Fin) : ") ; scanf ("%d%*c", &nm);<br />

if (nm)<br />

i<br />

printf ("Nombre: ") ; gets (nom);<br />

insertar(&R,nm,nom);<br />

I<br />

}while (nmj;<br />

/* Opciones <strong>de</strong> escribir el árbol o borrar una registro */<br />

clrscr ();<br />

do {<br />

puts(" 1. Mostrar el árboi\n") ;<br />

puts ('I 2. Eliminar un registro\n");<br />

puts (" 3. Salir\n 'I);<br />

do scanf("%d%*c", &nm); while(nm


534 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

i<br />

Nodo * t ;<br />

t = (Nodo") malloc(sizeof(Nodo));<br />

t -> nummat = id;<br />

strcpy(t->nombre,n);<br />

t -> izda = t-> dcha = NU1,L;<br />

return t ;<br />

Nodo* buscar (Nodo* p, int buscado)<br />

i<br />

if (!p)<br />

return O;<br />

else if (buscado == p -> nummat)<br />

return p;<br />

else if (buscado < p -> nummat)<br />

return buscar (p -> izda, buscado);<br />

else<br />

return buscar (p -> dcha, buscado);<br />

void insertar (Nodo** raiz, int nuevomat, char *nuevo-nombre)<br />

i<br />

if (!("raiz))<br />

*raiz = CrearNodo(nuevomat, nuevo-nombre);<br />

else if (nuevomat i (*raiz) -> nummat)<br />

insertar (&((*raiz) -> izda), nuevomat, nuevo-nombre);<br />

else<br />

insertar (&((*raiz) -> dcha), nuevo-mat, nuevo-nombre);<br />

I<br />

void visualizar (Nodo" r)<br />

{<br />

if (r)<br />

i<br />

visualizar(r -> izda);<br />

printf('Matricu1a %d \t %s \n",r->nummat,r->nombre);<br />

visualizar(r -> dcha);<br />

void eliminar (Nodo** r, int mat)<br />

if (!(*r )<br />

printf("!! Registro con clave %d no se <strong>en</strong>cu<strong>en</strong>tra ! !. \n",mat);<br />

else if (mat i (*r)->nummat)<br />

eliminar (&(*r)->i.zda, mat) ;<br />

else if (mat > (*r)->numat)<br />

eliminar(&(*r)->dcha,mat);<br />

else /* Matricula <strong>en</strong>contrada */<br />

i<br />

Nodo* q; /* puntero al nodo a suprimir */<br />

q = (*y);<br />

if (q->izda == NULL)<br />

(*r) = q->dcha;<br />

else if (q->dcha == NULL)<br />

(*r) = q->izda;<br />

else


Árboles 535<br />

1<br />

1<br />

{ /* ti<strong>en</strong>e rama izda y dcha. Se reemplaza por el mayor<br />

<strong>de</strong> los m<strong>en</strong>ores */<br />

Nodo* a, *p;<br />

P = q;<br />

a = q->izda;<br />

while (a->dcha){<br />

p = a;<br />

a = a->dcha;<br />

1<br />

q->nummat = a->nummat;<br />

strcpy(q->nombre,a->nombre);<br />

if (p == q)<br />

p->izda = a->izda;<br />

else<br />

p->dcha = a-iizda;<br />

q = a;<br />

1<br />

free(q) ;<br />

16.9.5. Recorridos <strong>de</strong> un árbol<br />

Exist<strong>en</strong> dos tipos <strong>de</strong> recorrido <strong>de</strong> los nodos <strong>de</strong> un árbol: el recorrido <strong>en</strong> anchura y el recorrido <strong>en</strong><br />

profundidad. En el recorrido <strong>en</strong> anchura se visitan los nodos por niveles. Para ello se utiliza una<br />

<strong>estructura</strong> auxiliar tipo cola <strong>en</strong> la que <strong>de</strong>spués <strong>de</strong> mostrar el cont<strong>en</strong>ido <strong>de</strong> un nodo, empezando por el<br />

nodo raíz, se almac<strong>en</strong>an los punteros correspondi<strong>en</strong>tes a sus hijos izquierdo y <strong>de</strong>recho. De esta forma si<br />

recorremos los nodos <strong>de</strong> un nivel, mi<strong>en</strong>tras mostramos su cont<strong>en</strong>ido, almac<strong>en</strong>amos <strong>en</strong> la cola los<br />

punteros a todos los nodos <strong>de</strong>l nivel sigui<strong>en</strong>te.<br />

El recorrido <strong>en</strong> profundidad se realiza por uno <strong>de</strong> tres métodos recursivos: preord<strong>en</strong>, inord<strong>en</strong> y<br />

postord<strong>en</strong>. El primer método consiste <strong>en</strong> visitar el nodo raíz, su árbol izquierdo y su árbol <strong>de</strong>recho, por<br />

este ord<strong>en</strong>. El recorrido inord<strong>en</strong> visita el árbol izquierdo, a continuación el nodo raíz y finalm<strong>en</strong>te el<br />

árbol <strong>de</strong>recho. El recorrido postord<strong>en</strong> consiste <strong>en</strong> visitar primero el árbol izquierdo, a continuación el<br />

<strong>de</strong>recho y finalm<strong>en</strong>te el raíz.<br />

preord<strong>en</strong> Raíz I zdo Dcho<br />

<strong>en</strong> ord<strong>en</strong> Izdo Raíz Dcho<br />

postord<strong>en</strong> I zdo Dcho Raíz<br />

16.9.6. Determinación <strong>de</strong> la altura <strong>de</strong> un árbol<br />

La alturu <strong>de</strong> un árbol <strong>de</strong>p<strong>en</strong><strong>de</strong>rá <strong>de</strong>l criterio que se siga para <strong>de</strong>finir dicho concepto. Así, si <strong>en</strong> el caso <strong>de</strong><br />

un árbol que ti<strong>en</strong>e nodo raíz, se consi<strong>de</strong>ra que su altura es 1, la altura <strong>de</strong>l árbol es 2, y la altura <strong>de</strong>l árbol


1<br />

536 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

es 4. Por último, si la altura <strong>de</strong> un árbol con un nodo es 1, la altura <strong>de</strong> un árbol vacío (el puntero es<br />

NULL) es O.<br />

\<br />

Nota<br />

La altura <strong>de</strong> un árbol es 1 más que la mayor <strong>de</strong> las alturas <strong>de</strong> sus subárboles izquierdo y <strong>de</strong>recho.<br />

A continuación, <strong>en</strong> el Ejemplo 16.14 se escribe una función <strong>en</strong>tera que <strong>de</strong>vuelve la altura <strong>de</strong> un<br />

árbol.<br />

Ejemplo 16.14<br />

Fuiicicín que <strong>de</strong>termina la altura <strong>de</strong> un árbol binario <strong>de</strong> maneru recursiva. Se consi<strong>de</strong>ra que la altura<br />

<strong>de</strong> un árbol vacío es O; si no está vacío. la ulturu es I + múximo <strong>en</strong>tre las alturas <strong>de</strong> rama izquierda y<br />

<strong>de</strong>recha.<br />

int altura(Nodo* r)<br />

J<br />

if (r == NULL)<br />

return O;<br />

else<br />

return (1 + max(alturd(r-zizda),altura(r>dcha))));<br />

16.10. APLICACIONES DE ÁRBOLES EN ALGORITMOS DE EXPLORACIÓN<br />

Los <strong>algoritmos</strong> recursivos <strong>de</strong> recorridos <strong>de</strong> árboles son el fundam<strong>en</strong>to <strong>de</strong> muchas aplicaciones <strong>de</strong><br />

árboles. Proporcionan un acceso ord<strong>en</strong>ado y metódico a los nodos y a sus <strong>datos</strong>. Vamos a consi<strong>de</strong>rar <strong>en</strong><br />

esta sección una serie <strong>de</strong> <strong>algoritmos</strong> <strong>de</strong> recorrido usuales <strong>en</strong> numerosos problemas <strong>de</strong> programación,<br />

tales como: contar el número <strong>de</strong> nodos hoja, calcular la profundidad <strong>de</strong> un árbol, imprimir un árbol o<br />

copiar y eliminar árboles.


Árboles 537<br />

16.10.1. Visita a los nodos <strong>de</strong> un árbol<br />

En muchas aplicaciones se <strong>de</strong>sea explorar (recorrer) los nodos <strong>de</strong> un árbol pero sin t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta un<br />

ord<strong>en</strong> <strong>de</strong> recorrido preestablecido. En esos casos, el cli<strong>en</strong>te o usuario es libre para utilizar el algoritmo<br />

oportuno.<br />

La función ContarHoj as recorre el árbol y cu<strong>en</strong>ta el número <strong>de</strong> nodos hoja. Para realizar esta<br />

operación se ha <strong>de</strong> visitar cada nodo comprobando si es un nodo hoja. El recorrido utilizado será el<br />

postord<strong>en</strong>.<br />

/* Función ContarHojas<br />

la función utiliza recorrido postord<strong>en</strong><br />

<strong>en</strong> cada visita se comprueba si el nodo es un nodo hoja<br />

(no ti<strong>en</strong>e <strong>de</strong>sc<strong>en</strong>di<strong>en</strong>tes)<br />

*/<br />

void contarhojas (Nodo" r, int* nh)<br />

{<br />

I<br />

if (r != NULL)<br />

I<br />

contarhojas (r -> izda, nh) ;<br />

contarhojas (r -> dcha, nh) ;<br />

/* procesar raíz: <strong>de</strong>terminar si es hoja */<br />

if (r->izda==NULL&& r->dcha==NULL) (*nh)++;<br />

1<br />

La función eliminarbol utiliza un recorrido postord<strong>en</strong> para liberar todos los nodos <strong>de</strong>l árbol<br />

binario. Este recorrido asegura la liberación <strong>de</strong> la memoria ocupada por un nodo <strong>de</strong>spués <strong>de</strong> haber<br />

liberado su rama izquierda y <strong>de</strong>recha.<br />

/* Función eliminarbol<br />

Recorre <strong>en</strong> postord<strong>en</strong> el árbol. Procesar la raíz, <strong>en</strong> esta<br />

función es liberar el nodo con free().<br />

*/<br />

void eliminarbol (Nodo" r)<br />

{<br />

1<br />

if (r != NULL)<br />

i<br />

eliminarbol(r -> izda);<br />

eliminarbol(r -> dcha);<br />

printf ("\tNodo borrado: %d ",r->numat);<br />

free(r);<br />

I<br />

16.11. RESUMEN<br />

En este capítulo se introdujo y <strong>de</strong>sarrolló la <strong>estructura</strong> La <strong>estructura</strong> árbol más utilizada normalm<strong>en</strong>te es<br />

<strong>de</strong> <strong>datos</strong> dinámica árbol. Esta <strong>estructura</strong>, muy pot<strong>en</strong>te, el drbol binario. Un árbol binario es un árbol <strong>en</strong> el<br />

se pue<strong>de</strong> utilizar <strong>en</strong> una gran variedad <strong>de</strong> aplicaciones que cada nodo ti<strong>en</strong>e como máximo dos hijos, llama-<br />

' <strong>de</strong> programación. dos subá&ol izquierdo y subárbol <strong>de</strong>recho.


1<br />

538 Programación <strong>en</strong> C. Metodología, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

tambi6n como profindi-<br />

antecesor o<br />

A<br />

A A<br />

B C c B<br />

25 equilibrado<br />

equitibrado<br />

A<br />

A<br />

55<br />

no equilibrado<br />

totalm<strong>en</strong>te<br />

Los árboles binarios pres<strong>en</strong>tan dos tipos caracteristicos:<br />

árboles binanos <strong>de</strong> búsqueda y árboles binanos<br />

<strong>de</strong> expresiones. Los &boles binarios <strong>de</strong> búsqueda<br />

se utilizan fundam<strong>en</strong>talm<strong>en</strong>te para mant<strong>en</strong>er una<br />

colección ord<strong>en</strong>ada <strong>de</strong> <strong>datos</strong> y los árboles binarios <strong>de</strong><br />

expresiones para almac<strong>en</strong>ar expresiones.


Árboles 539<br />

arios que repres<strong>en</strong>tan las<br />

Lis& los hijos <strong>de</strong>l nodo R.<br />

Listar los sucesores <strong>de</strong>l nodo R.<br />

16.6, El recorrído preord<strong>en</strong> <strong>de</strong> un cierto &bol binario<br />

produce.


540 Programación <strong>en</strong> C. Metodologia, <strong>algoritmos</strong> y <strong>estructura</strong> <strong>de</strong> <strong>datos</strong><br />

ADFGHKLPQRWZ<br />

y <strong>en</strong> recorrido <strong>en</strong>ord<strong>en</strong> produce<br />

GFHKDLAWRQPZ<br />

Dibujar el árbol binario.<br />

16.7. Escribir una función no recursiva que cu<strong>en</strong>te<br />

las hojas <strong>de</strong> un árbol binario.<br />

16.8. Escribir un programa que procese un árbol<br />

binario cuyos nodos cont<strong>en</strong>gan caracteres y a<br />

partir <strong>de</strong>l sigui<strong>en</strong>te m<strong>en</strong>ú <strong>de</strong> opciones:<br />

I (seguido <strong>de</strong> un carácter): Insertar un carácter<br />

B (seguido <strong>de</strong> un carácter): Buscar un carácter<br />

RE<br />

: Recorrido <strong>en</strong> ord<strong>en</strong><br />

RP : Recorrido <strong>en</strong> preord<strong>en</strong><br />

RT : Recorrido postord<strong>en</strong><br />

SA : Salir<br />

16.9. Escribir una función que tome un árbol como<br />

<strong>en</strong>trada y <strong>de</strong>vuelva el número <strong>de</strong> hijos <strong>de</strong>l<br />

árbol.<br />

16.10. Escribir una función booleana a la que se le<br />

pase un puntero a un árbol binario y <strong>de</strong>vuelva<br />

verda<strong>de</strong>ro (true) si el árbol es completo y false<br />

<strong>en</strong> caso contrario.<br />

16.11. Diseñar una función recursiva <strong>de</strong> búsqueda,<br />

que <strong>de</strong>vuelva un puntero a un elem<strong>en</strong>to <strong>en</strong> un<br />

árbol binario <strong>de</strong> búsqueda; si no está el elem<strong>en</strong>to,<br />

<strong>de</strong>vuelva NULL.<br />

16.12. Diseñar una función iterativa que <strong>en</strong>cu<strong>en</strong>tre el<br />

número <strong>de</strong> nodos hoja <strong>en</strong> un árbol binario.<br />

16.13. PROBLEMAS<br />

16.1. Crear un archivo <strong>de</strong> <strong>datos</strong> <strong>en</strong> el que cada lima<br />

cont<strong>en</strong>ga la sigui<strong>en</strong>te información<br />

Columnas 1-20<br />

21-31<br />

Nombre<br />

Número <strong>de</strong> la Seguridad<br />

Social<br />

32-78 Mcción<br />

Escribir un programa que lea cada regisíro<br />

<strong>de</strong> <strong>datos</strong> <strong>de</strong> un &bol, <strong>de</strong> modo que CUaRdo el<br />

áibol se mmra utili recamdo <strong>en</strong> ord<strong>en</strong>,<br />

los ntfmeros <strong>de</strong> la seguridad social se ord<strong>en</strong><strong>en</strong><br />

em ord<strong>en</strong> asc<strong>en</strong>d<strong>en</strong>te. Imprimir una cabecera<br />

"DATOS DE EMPLEaDOS ORDE-NADOS<br />

POR hTUlvERO SECURXDAD SOCIAL". A<br />

continuación se han <strong>de</strong> imprimir los tres <strong>datos</strong><br />

utilizando el sigui<strong>en</strong>te formato <strong>de</strong> salida.<br />

Columnas 1- 11 Número <strong>de</strong> la Seguridad<br />

Social<br />

25-44 Nombre<br />

58-104 Dirección<br />

163. Escribir un programa que lea un texto <strong>de</strong> longitud<br />

in<strong>de</strong>terminada y que produzca como<br />

resultado la lista <strong>de</strong> todas las palabras difer<strong>en</strong>-<br />

tes cont<strong>en</strong>idas <strong>en</strong> e$ texto, así como su frecu<strong>en</strong>cia<br />

<strong>de</strong> aparicibn.<br />

Hacer uso <strong>de</strong> la <strong>estructura</strong> árbol binario <strong>de</strong><br />

búsqueda, cada nodo <strong>de</strong>l árbol que t<strong>en</strong>ga una<br />

palabra y su frecu<strong>en</strong>cia.<br />

163. Se dispone <strong>de</strong> un árbol binario <strong>de</strong> elem<strong>en</strong>tos <strong>de</strong><br />

tip <strong>en</strong>tero. Escribir funciones que calcul<strong>en</strong>:<br />

a) La suma <strong>de</strong> sus elem<strong>en</strong>tos<br />

b) La suma <strong>de</strong> sus elem<strong>en</strong>tos que son múltiplos<br />

<strong>de</strong> 3.<br />

16.4. Escribir una func-ión boogeana IDENTICOS<br />

que -ita <strong>de</strong>cir si dos árboles binarios son<br />

iguales.<br />

16.5. Disefiar un programa interactivo que permita<br />

etc., <strong>en</strong> un árbol bib0<br />

<strong>de</strong> búsqueda<br />

16.6. Construir un procedimi<strong>en</strong>to recursivo para<br />

escribir todos los nodos <strong>de</strong> un &bol binario <strong>de</strong><br />

búsqueda cuyo campo clave sea mayor que un<br />

valor dado (el campo clave es <strong>de</strong> tipo <strong>en</strong>tero).


Árboles 541<br />

16.7. Escribir una función que,<strong>de</strong>termine la altura<br />

<strong>de</strong> un nodo. Escribir un programa que cree<br />

un árbol binario con números g<strong>en</strong>erados aleatoriam<strong>en</strong>te<br />

y muestre por pantalla:<br />

La altura <strong>de</strong> cada nodo <strong>de</strong>l árbol.<br />

La difer<strong>en</strong>cia <strong>de</strong> altura <strong>en</strong>tre rama izquierda<br />

y <strong>de</strong>recha <strong>de</strong> cada nodo.<br />

16.8. Diseñar procedimi<strong>en</strong>tos no recursivos que<br />

list<strong>en</strong> los nodos <strong>de</strong> un árbol <strong>en</strong> inord<strong>en</strong>, preord<strong>en</strong><br />

y postord<strong>en</strong>.<br />

16.9. Dados dos árboles binarios <strong>de</strong> búsqueda<br />

indicar mediante un programa si los árboles<br />

ti<strong>en</strong><strong>en</strong> o no elem<strong>en</strong>tos comunes.<br />

16.10. Dado un árbol binario <strong>de</strong> búsqueda construir<br />

su árbol espejo. (&bol espejo es el<br />

que se construye a partir <strong>de</strong> uno dado, convirti<strong>en</strong>do<br />

el subárbol izquierdo <strong>en</strong> subárbol<br />

<strong>de</strong>recho y viceversa.)<br />

16.11. Un árbol binario <strong>de</strong> búsqueda pue<strong>de</strong> impiem<strong>en</strong>tarse<br />

con un array. La repres<strong>en</strong>tación no<br />

<strong>en</strong>lazada correspondi<strong>en</strong>te consiste <strong>en</strong> que para<br />

cualquier nodo <strong>de</strong>l árbol almac<strong>en</strong>ado <strong>en</strong> la<br />

posición I <strong>de</strong>l array, su hijo izquierdo se<br />

<strong>en</strong>cu<strong>en</strong>tra <strong>en</strong> la posición 2*1 y su hijo <strong>de</strong>recho<br />

<strong>en</strong> la posición 21 + 1. Diseñar a partir <strong>de</strong> esta<br />

repres<strong>en</strong>tación los correspondi<strong>en</strong>tes procedimi<strong>en</strong>tos<br />

y funciones para gestionar interactivam<strong>en</strong>te<br />

un árbol <strong>de</strong> números <strong>en</strong>teros.<br />

(Com<strong>en</strong>te el inconv<strong>en</strong>i<strong>en</strong>te <strong>de</strong> esta repres<strong>en</strong>tación<br />

<strong>de</strong> cara al máximo y mínimo número<br />

<strong>de</strong> nodos que pued<strong>en</strong> almac<strong>en</strong>arse.)<br />

16.12. Una matriz <strong>de</strong> N elem<strong>en</strong>tos almac<strong>en</strong>a cad<strong>en</strong>as<br />

<strong>de</strong> caracteres. Utilizando un árbol binario <strong>de</strong><br />

búsqueda como <strong>estructura</strong> auxiliar ord<strong>en</strong>e<br />

asc<strong>en</strong>d<strong>en</strong>tem<strong>en</strong>te la cad<strong>en</strong>a <strong>de</strong> caracteres.<br />

16.13. Dado un árbol binario <strong>de</strong> búsqueda diseñe un<br />

procedimi<strong>en</strong>to que liste los nodos <strong>de</strong>l árbol<br />

ord<strong>en</strong>ados <strong>de</strong>sc<strong>en</strong>d<strong>en</strong>tem<strong>en</strong>te.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!