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",¬a);<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", ¬a);<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", ¬a);<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", ¬a);<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", ¬a);<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(®-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.