Manual de C

H´ctor Tejeda Villela e

´ Indice general
1. Compilaci´n de un programa en C/C++ o 1.1. Creaci´n, compilaci´n y ejecuci´n de un programa . . . . o o o 1.1.1. Creaci´n del programa . . . . . . . . . . . . . . . . o 1.1.2. Compilaci´n . . . . . . . . . . . . . . . . . . . . . o 1.1.3. Ejecuci´n del programa . . . . . . . . . . . . . . . o 1.2. El modelo de compilaci´n de C . . . . . . . . . . . . . . . o 1.3. El preprocesador . . . . . . . . . . . . . . . . . . . . . . . 1.4. Compilador de C . . . . . . . . . . . . . . . . . . . . . . . 1.5. Ensamblador . . . . . . . . . . . . . . . . . . . . . . . . . 1.6. Ligador . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.7. Algunas opciones utiles del compilador . . . . . . . . . . . ´ 1.8. Uso de las bibliotecas . . . . . . . . . . . . . . . . . . . . 1.9. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.9.1. Creaci´n de una biblioteca est´tica . . . . . . . . . o a 1.9.2. Creaci´n de una biblioteca compartida . . . . . . . o 1.10. Funciones de la biblioteca de UNIX . . . . . . . . . . . . . 1.10.1. Encontrando informaci´n acerca de las bibliotecas. o 1.11. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Principios de C 2.1. Or´ ıgenes del C . . . . . . . . . . . . . 2.2. Caracter´ ısticas de C . . . . . . . . . . 2.3. Estructura de un programa en C . . . 2.4. Variables . . . . . . . . . . . . . . . . 2.4.1. Definici´n de variables globales o 2.4.2. Lectura y escritura de variables 2.5. Constantes . . . . . . . . . . . . . . . 2.6. Operadores Aritm´ticos . . . . . . . . e 2.7. Operadores de Comparaci´n . . . . . . o 2.8. Operadores l´gicos . . . . . . . . . . . o 2.9. Orden de precedencia . . . . . . . . . 2.10. Ejercicios . . . . . . . . . . . . . . . . 3. Estructuras Condicionales 3.1. La sentencia if . . . . 3.2. El operador ? . . . . . 3.3. La sentencia switch . . 3.4. Ejercicios . . . . . . . . 1 1 1 1 2 2 2 3 3 3 3 4 5 5 6 7 7 8 10 10 10 11 12 13 14 15 15 16 16 16 18 19 19 20 21 23

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . . . . . . . . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . . i

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

´ INDICE GENERAL 4. Iteraci´n o 4.1. La sentencia for . . . . . 4.2. La sentencia while . . . 4.3. La sentencia do-while . 4.4. Uso de break y continue 4.5. Ejercicios . . . . . . . . .

ii 24 24 25 26 28 29

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

5. Arreglos y cadenas 32 5.1. Arreglos unidimensionales y multidimensionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 5.2. Cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 5.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 6. Funciones 6.1. Funciones void . . . . . 6.2. Funciones y arreglos . . 6.3. Prototipos de funciones 6.4. Ejercicios . . . . . . . . 36 37 37 38 39 40 40 41 42 43 44 44 45 46 46 49 49 51 51 53 53 54 55 56 56 57 58 58 61

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

7. M´s tipos de datos a 7.1. Estructuras . . . . . . . . . . . . 7.1.1. Definici´n de nuevos tipos o 7.2. Uniones . . . . . . . . . . . . . . 7.3. Conversi´n de tipos (casts) . . . o 7.4. Enumeraciones . . . . . . . . . . 7.5. Variables est´ticas . . . . . . . . a 7.6. Ejercicios . . . . . . . . . . . . .

. . . . . de datos . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

8. Apuntadores 8.1. Definici´n de un apuntador . . . . . . . . . . . . o 8.2. Apuntadores y Funciones . . . . . . . . . . . . . 8.3. Apuntadores y arreglos . . . . . . . . . . . . . . . 8.4. Arreglos de apuntadores . . . . . . . . . . . . . . 8.5. Arreglos multidimensionales y apuntadores . . . 8.6. Inicializaci´n est´tica de arreglos de apuntadores o a 8.7. Apuntadores y estructuras . . . . . . . . . . . . . 8.8. Fallas comunes con apuntadores . . . . . . . . . . 8.9. Ejercicios . . . . . . . . . . . . . . . . . . . . . . 9. Asignaci´n din´mica de memoria y o a 9.1. Uso de malloc, sizeof y free . 9.2. calloc y realloc . . . . . . . . . 9.3. Listas ligadas . . . . . . . . . . . . 9.4. Programa de revisi´n . . . . . . . . o 9.5. Ejercicios . . . . . . . . . . . . . . Estructuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

. . . . . . . . .

din´micas a . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

10.T´picos avanzados con apuntadores o 63 10.1. Apuntadores a apuntadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 10.2. Entrada en la l´ ınea de comandos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 10.3. Apuntadores a funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66

. . . perror() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . #include . . . . . . 16. . . . .2. . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . B´squeda y ordenamiento u 14. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . Funciones aritm´ticas . . . . . . . . . Flujos . . . . . . . . . . . . . . . . . . . . . . . . 16. . 11.7. . . .4. . 86 a 15.4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . Reportando errores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . .4. . . .1. . . . . . . . . . . . . . . 15. . . . . . . . . . Flujos predefinidos . . E/S de bajo nivel o sin almacenamiento intermedio 88 88 88 88 89 89 90 90 90 91 92 92 93 93 94 94 . . . .1. . . .1. . . . . . . . . . . . . . 16. . Directivas del preprocesador . . . . . . . o 16. . . . . . . . . . . . . . . . . . . . . . . . . . . . . o 14. . . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . scanf . . . . . . . . . . E/S formateada . . . . . . . . . . 11. . . . . . . .1. . . . . . . . . . . . . . Petici´n del estado del flujo . . 12. . . . . . . . . . sprintf y sscanf . . . . . . printf . . . . . Funciones matem´ticas . . . . . . . . . . . . . . . . . .h> 14. . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . .3.´ INDICE GENERAL iii 10. . . . . . . .2. . . . . . . . 16. . . . . . . . .1. Campos de bit . . . . . . . . .1. . . .3. . . . . . . . . . . errno . . . . . . 16. . . . . . . . . . . . . . Control del preprocesador del compilador 12. .1. . . . . . . . . . . . . . . . . . .Entrada y salida (E/S) stdio.1. . . . . . . . . . . . . . . . . . de . . . . . . . . . . . . . . . . . . . 16. . 12. . . . . . . . . . .1. . . . . . . . . . . . . . . . . Conversi´n de cadenas . . . . . . . . . . . . . . . . . . . .2. 16. . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16. . . . . . . . . . . . . . . . . . . . u 14. . . . . . . . . . . . . . . . . Ejercicios . . . . . . . . . . . . . . . . . . UNIX y las bibliotecas est´ndar a 78 13. . . . . . . . . . . . .5. . . . . . . . . .5. . . . . . . . . . exit . . . . . . . . .Biblioteca <math. . . . . . . 13. . . . . . . . . . . . . . . . . . . . . . . Ventajas del usar UNIX con C . . . .3. . . . . . . . . . . . . bit . . Ejercicios . . . . . . . . . . . Otras directivas del preprocesador . . . . . . . . . . . .1. . Lectura y escritura de archivos . . . . . 16. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . E/S Basica . . . . . . . . . . . . . . . . . . . . . . . . Operadores sobre bits . . . . . . . . . . . . . . . . . . . . . . e 14. . . . . 16. . . . . . . . . . . . . . . . . . . . . . . .Biblioteca <stdlib. . . . . . #undef . . . . . . . . . 87 a 16. . . . . 78 14. . . . . .6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 80 81 82 83 85 . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . .2. o 12. . . . . . . . . . . . . . . . . . . . . . . . . 12. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Uso de funciones de bibliotecas y llamadas del sistema . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . Archivos . . . . . . . . . . . . . 12. 11. . . . . . . . . . . .4.Operadores de bajo nivel y campos 11. . . . . . . . . . . . .1. . . . . . . . . . . . . . . .h> 86 15. . . . . . .1. . . . . . . Portabilidad . . . . . Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 13. . . . 69 69 70 71 72 73 73 73 74 74 75 76 76 77 . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.1. . . . .C. . . . . . . . . . . . . . . . N´meros aleatorios . . . . . . . . . . #define . . . . .2. . . . . 12. . . . .6. . . . . . . . . . . . . #if Inclusi´n condicional . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . 16. . . . . . . . . 67 11. . . . . . . . . . . . . 16. . . . . .4. . . . . . . . . . .3. .h 16. . . . . . . .El preprocesador de C 12. . . . . . . . Constantes matem´ticas . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . .3. . . . . 16. . . . . . . . . . . . . . . . . .1. . . . . . . .

. . . . . . . . . . . . 114 . . . . . . Ejercicios . . . . . . . . . . . . .2. . . . . . . . . . 21. . . . . . . . . . . . . 21. Ejecutando comandos de UNIX desde C . . . . . . . . 20. . . . . . . . .6.2. . . . . execl() . . . . . . . . . . . . . . o . . . . . . . . . .4. 20. . . . . .Manejo de cadenas <string. . . . . . . . . . . . . . . . . . . . . . . 112 . . . . . . . .h. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Ejercicios . . . 20. . . . . .2. . .h 18. . . . . Como dividir un programa en varios archivos . . . . . . . . . . . .4. . . La utiler´ Make . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . <unistd. . 19. Ejemplo 2: Inicializar la semilla de un n´mero aleatorio. . . . . . . . u 19. . . . . . . . Ventajas de Usar Varios Archivos . . . . . . . . . . . . . . . . . . . 20. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8. . . . . . . . . . . . . . . . . . . . . . . . . . . Funciones b´sicas para el manejo de cadenas a 17. . . .2. . .1. . . ıa 21.Acceso de Archivos y llamadas al sistema de directorios 18. . . . . . . . . . . . . . . . Busqueda y ordenamiento de directorios: sys/types. . . . . . . . . . . . . . . . 18. . . . . . . . . Ejemplo 1: Tiempo (en segundos) para hacer alg´n c´lculo.1. . .1. . . 21. . . . . . . . . . . . . . . 21. . . . 110 . . . . . . . . . . . . .2. . . exit() . .1. . . . . . . . 18. . . . . . . . Funciones b´sicas para el tiempo . . . . . .1. . . . . .4. . . . . . . . . . . . . . 20. 111 112 . . Creaci´n de archivos temporales: <stdio. . . Archivos Cabezera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . .h> 17. . . . . . . . .h> o 17. . . . 21. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Permisos de accesos a archivos .2. . . . . . 21. . . . . . . . . . . . . . .Compilaci´n de Programas con Archivos M´ ltiples o u 21. . . . . . . . B´squeda en cadenas . . 21. . . . . . . . . . . . .8. 110 .1. . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . sys/stat. . . . . . 109 . . . . . . . . . . . . . . . . . . . . 97 17. Organizaci´n de los Datos en cada Archivo . . . . . . . . . . . . .4. . . . . . . . . .2. . . . . . . . . . . . . . .3. . . . . . o 18. . . . . . . . .2. .h. . . . . . . . . . . . . . . . . . . . . . . .h> . . . . . . . . . . 117 118 118 120 120 121 121 122 123 123 124 125 126 . . . . . . . . . . 112 . . Estado de un archivo .1. . . . . . . . . . . . . . . o 18. . . . . . . . . . . Manipulaci´n de archivos: stdio. . . . . . . . . . . . . . . . . unistd. . . . . . Ejemplos de aplicaciones de funciones del tiempo.Funciones para el tiempo 19. . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 . . . . . . . . . . 19. . . .3. . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . Variables y Funciones Externas . . . . . . . . . . . . . . . . . . Ejecuci´n de Make . . . . . . . .h>. . . . . . . . .h . . . . . Alcance de las variables externas . .h> . .5. . . o 21. . . . . . . . . . . . . . . . . Rutinas de manipulaci´n de archivos: unistd. . . . . . .2. . . . .1. . . . . . . . . . .1. . . Programando Make . . . . . . . . . . . . . . . . . . . . . . . . . . a 19. . . . . . . . . . . .h. . . . . . . . . . . . . . . . . . . o 21. . u 17. . . . . . . . . . . . . . . . . .h .3. . . . . . . . . . . . . . . . .1. . . . . . . . . . .9. . . . Ejercicios . . .5. . . . . . . . . . . . . . .h> 20. . . . . . . . . . .1. . . . Uso de macros con Make . . . . . . . . . . o 18. . . . Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . Funciones para el manejo de directorios <unistd. Prueba y conversi´n de caracteres <ctype. . . . . . . . . . . . . .6. . . . . . . Creaci´n de un Archivo Make (Makefile) . . . . . . . . . . . . . .´ INDICE GENERAL iv 16. . . . Operaciones con la memoria <memory. . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . 98 98 99 101 101 102 103 103 104 106 106 106 108 108 108 . . 17. Ejercicios . . . . . . . 18. . . . . . .h. . . 110 . . . . . . . . 21. . . wait() . . . . . . . . . .Control de procesos: <stdlib. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .sys/dir. . . . . . . u a 19. . . . . . . . . . . . . . . . . . sys/types. . . .7. . . . . 18. . . . . fork() . .h> . . . . .2. . . . . . . . . . . . . . 109 . . . . . . .2. . . . . . .2. . . . . . . . . . . . . 20. . . . . .3. . . . . . . .6. .

. . . . 127 . . v 127 . . . . 127 . . . . . . . . . . . . 134 o 23.2. . . .Comunicaci´n entre procesos (IPC Interprocess o 22. . .1. . . .2. . . . . . . 134 23. . . .2.Sockets 133 23. . . . . ıa Communication). . . . . . . . . . . . Transferencia de datos en un flujo y cerrado . . .h> . . . . . . . . . . . . PIPES . . . . . . . . . . . . . 135 . Conectando sockets de flujo . . . . . . . . . . . . Creaci´n y nombrado de sockets . .´ INDICE GENERAL 22. pipe() Tuber´ de bajo nivel . . .1. . . . . . . . . . . . . . . . . . ıa 22. . . . . . . . . . . . . . . . . . . . Entubando en un programa de C <stdio. . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . .1. . . 22.1. 128 23. . . . . . . popen() Tuber´ formateada . . . . . . . . . . . . . .

En el caso que esta fuera la situaci´n ıa o a o se le estar´ indicando a la computadora que haga las operaciones incorrectas.c es el nombre del archivo. Sin embargo. El comando deber´ ser seguido por el nombre del programa a en C que se quiere compilar. la mejor referencia de cada compilador es a trav´s de las p´ginas en l´ e a ınea. Se describe tambi´n a e el modelo de compilaci´n de C y tambi´n como C soporta bibliotecas adicionales. y en Linux se puede usar pico. Un determinado n´mero de opciones del compilador pueden ser indicadas tambi´n. el compilador se detendr´ y los reportar´. ıa.1.1. a a Podr´ haber desde luego errores l´gicos que el compilador no podr´ detectar. Por convenci´n el nombre del archivo debe terminar con “.1. caracteres no tecleados u omisiones de punto y coma). Existen tambi´n compiladores equivalentes de C++ los cuales usualmente son nombrados como CC. Por ejemplo. El compilador GNU de C es gcc. el a cual es bastante popular y esta disponible en varias plataformas. M´s adelante se a listan y se dan ejemplos de opciones comunes de los compiladores.1. 1.c” por ejemplo: miprograma. del manual del sistema. el comando b´sico de compilaci´n es: a o gcc programa. e Existen otros compiladores menos comunes de C y C++. El compilador de GNU es tambi´n denotado como g++. Compilaci´n o Existen muchos compiladores de C. a 1 . Un editor disponible en la mayor´ ıa de los sistemas UNIX es vi. o e 1. como en los ejemplos que se tienen m´s adelante. compilaci´n y ejecuci´n de un programa o o o Creaci´n del programa o Se puede crear un archivo que contenga el programa completo. Para compilar el programa usaremos el comando gcc. Por ejemplo: man gcc.2. Si hay errores obvios en el programa (tales como palabras mal escritas.c progprueba. El cc es el compilador est´ndar de Sun.c. a Se puede usar cualquier editor de textos ordinario con el que se este familiarizado. e Sun provee CC y GNU GCC.Cap´ ıtulo 1 Compilaci´n de un programa en C/C++ o En esta cap´ ıtulo se dan las procesos b´sicos que se requieren para compilar un programa de C. El conteo nido del archivo deber´ obedecer la sintaxis de C. a a Por lo tanto. En general todos los compiladores mencionados operan esencialmente de la misma forma y comparten muchas opciones comunes en la l´ ınea de opciones. a 1.c donde programa. Creaci´n. Por u e el momento no haremos uso de estas opciones todav´ se ir´n comentando algunas m´s esenciales.

la versi´n compilada. Sin embargo.out.3. o 1. entonces se debe regresar a editar el archivo del programa.out.1. El preprocesador Esta parte del proceso de compilaci´n ser´ cubierta con m´s detalle en el cap´ o a a ıtulo 12 referente al preprocesador.1 se muestran las distintas ´tapas que cubre el compilador para obtener el c´digo ejecutable. podr´ haber u e ıa errores en tiempo de ejecuci´n (run-time errors). recompilarlo. en este caso programa (o a. 1. Con lo anterior. Se recomienda y es m´s conveniente usar la opci´n -o con el nombre del archivo ejecutable como se muestra a a o continuaci´n: o gcc -o programa programa. mostrando alg´n resultado en la pantalla. 1. o si la opci´n -o es usada con el compilador.out). en vez del archivo n e a. y ejecutarlo nuevamente. El modelo de compilaci´n de C o En la figura 1. se ejecuta el programa. Para correr un ejecutable en UNIX. e o CODIGO FUENTE PREPROCESADOR COMPILADOR CODIGO EN ENSAMBLADOR ENSAMBLADOR LIBRERIAS CODIGO OBJETO LIGADOR CODIGO EJECUTABLE Figura 1. Si lo anterior sucede. COMPILACION DE UN PROGRAMA EN C/C++ 2 Cuando el compilador ha terminado con ´xito. o el ejecutable. Ejecuci´n del programa o El siguiente estado es correr el programa ejecutable.2. En ´ste estado.1: Modelo de compilaci´n de C. o a El preprocesador acepta el c´digo fuente como entrada y es responsable de: o . el nombre despu´s de -o es el nombre del programa o e compilado. o bien.c el cual pone el programa compilado en el archivo del programa se˜alado. es dejado en un archivo e o llamado a. tales como divisi´n por cero. se da alguna informaci´n b´sica para algunos programas de C. podr´ hacerse evidentes al o o ıan ver que el programa no produce la salida correcta.´ CAP´ ITULO 1.3. en ´ste caso en programa. simplemente se escribe el nombre del archivo que lo contiene.

el ligador combina estas funciones (con main()) para crear un archivo ejecutable. El c´digo fuente es recibido del preproo o o cesador.5.h> — Archivo de la biblioteca est´ndar de Entrada/Salida. Algunas opciones utiles del compilador ´ Descrito el modelo b´sico de compilaci´n. por ejemplo: gcc arch1. se recomienda revisar las p´ginas de man para mayor informaci´n y opciones adicionales. Sustituci´n de macros.o para cada archivo fuente listado. o o • #define TAM_MAX_ARREGLO 100 1. a o -E Se compilador se detiene en la ´tapa de preprocesamiento y el resultado se muestra en la salida e est´ndar..o arch2. e 1. Por ejemplo: 3 #include — incluye el contenido del archivo nombrado. En los sistemas con UNIX se podr´n ver los archivos o a con el sufijo .6.7.. De nueva a o a ´ cuenta. Por ejemplo: • #include <math. 1. Ensamblador El ensamblador crea el c´digo fuentei o los archivos objeto. se dar´n algunas opciones utiles y algunas veces esenciales. Despu´s los e archivos objeto pueden ser ligados por el comando gcc.h> — Archivo de la biblioteca est´ndar de matem´ticas. a a • #include <stdio. COMPILACION DE UN PROGRAMA EN C/C++ quitar los comentarios interpretar las directivas del preprocesador las cuales inician con #.o . a gcc -E arch1. Compilador de C El compilador de C traduce el c´digo fuente en c´digo de ensamblador. Ligador Si alg´n archivo fuente hace referencia a funciones de una biblioteca o de funciones que est´n definidas en otros u a archivos fuentes. Estos son usualmente llamados archivos de cabecera (header). -o ejecutable -lbiblioteca . 1. Las referencias a variables externas en esta ´tapa son resueltas.o. a #define — define un nombre simb´lico o constante.c -c Suprime el proceso de ligado y produce un archivo .4.´ CAP´ ITULO 1.

c -I/home/minombr/miscabeceras Nota: Las cabeceras de las bibliotecas del sistema son guardados en un lugar especial (/usr/include) y no son afectadas por la opci´n -I. Probablemente la biblioteca m´s com´nmente usada es la biblioteca matem´tica (math. a por ejemplo: gcc prog. El ligador siempre busca las bibliotecas est´ndares y del sistema en /lib y /usr/lib. Las o a bibliotecas objeto son guardadas y pueden estar estandarizadas.c -D Define s´ ımbolos como identificadores (-Didentificador) o como valores (-Ds´ ımbolo=valor) en una forma similar a la directiva del preprocesador #define). -v Muestra en la salida estandar de errores los comandos ejecutados en las ´tapas de compilaci´n. Uso de las bibliotecas C es un lenguaje extremadamente peque˜o.´ CAP´ ITULO 1. e .8. Esta biblioteca a u a deber´ ligarse expl´ a ıcitamente si se desea usar las funciones matem´ticas (y por supuesto no olvidar el a archivo cabecera #include <math. y finalmente. -Ldirectorio Agrega directorios a la lista de directorios que contienen las rutinas de la biblioteca de objetos. no hay funciones para E/S. Por ıas o ejemplo. un tercero o usuario las crea. COMPILACION DE UN PROGRAMA EN C/C++ Liga con las bibliotecas objeto. Esta opci´n deber´ seguir los argumentos de los archivos fuente. en /usr/include. el programa deber´ compilarse de la siguiente forma para a generar extensiones de GDB: gcc -ggdb -o prog prog. por ejemplo: gcc calc. e o Por lo tanto. Muchas de las funciones que tienen otros lenguajes no est´n en C. e o 4 1. n a por ejemplo.h).c -o calc -lm Muchas otras bibliotecas son ligadas de esta forma. se tendr´ que especificar donde estan guardados los archivos. los que no empiezan con diagonal /). y despu´s en los directorios nombrados con la opci´n -I si hubiera. Los archivos cabecera del sistema y del usuario son inclu´ o ıdos en una manera un poco diferente. -g Opci´n para llamar las opciones de depuraci´n (debug). manejo de cadenas o funciones matem´ticas. en el programa que llama a las funciones). a La funcionalidad de C se obtiene a trav´s de un rico conjunto de bibliotecas de funciones. si se emplea el depurador de GNU. primero busca los archivos #include en el directorio que contiene el archivo fuente.a -Itrayectoria Agrega una trayectoria o ruta a la lista de directorios en los cuales se buscar´n los archivos cabecera a #include con nombres relativos (es decir.h>. Instruye al compilador para producir inforo o maci´n adicional en la tabla de s´ o ımbolos que es usado por una variedad de utiler´ de depuraci´n. El procesador por default.c -L/home/minombr/mislibs milib. si se quiere incluir archivos de cabecera guardados en /home/minombr/miscabeceras se tendr´ que hacer: a gcc prog. Si se quieren ligar a bibliotecas personales o instaladas por usted.

c int factorial(int n) { int i. COMPILACION DE UN PROGRAMA EN C/C++ 5 Como resultado. Para muchos prop´sitos b´sicos estas podr´ ser consideradas como parte de C.´ CAP´ ITULO 1. Todas las bibliotecas (excepto E/S est´ndar) requieren ser expl´ a ıcitamente ligadas con la opci´n -l y. 1.h" #define VALOR 4 main() .9. se podr´ desear agruparlas en un conjunto de ıa archivos fuente. for(i=1. y entonces crear una biblioteca con los archivos objeto. Supongamos que se tiene un conjunto de archivos que contengan rutinas que son usadas frecuentemente.9. por ejemplo un archivo cubo. i++) res*=i. a Un programador puede tambi´n desarrollar sus propias funciones de biblioteca e incluso bibliotecas especiales de e terceros. como se se˜alo previamente. Con lo anterior se puede ahorrar tiempo al compilar en aquellos programas donde sean usadas. NAG o PHIGS. posiblemente o con L. } Para los archivos de nuestras funciones tambi´n se debe tener un archivo de cabezera. n 1.h con el siguiente contenido: extern float cubo(float).1. i<=n.c */ #include "libmm. El c´digo que use la biblioteca que se esta creando podr´ ser: o ıa /* Programa prueba. Pero pueden variar de m´quina o a ıan a a m´quina.c: float cubo(float x) { return (x*x*x). por ejemplo. return (res). e suponiendo que se tiene el siguiente archivo libmm. muchas implementaciones de C incluyen bibliotecas est´ndar de funciones para varias finalidaa des. Ejemplos Creaci´n de una biblioteca est´tica o a Si se tiene un conjunto de rutinas que se usen en forma frecuente. para que puedan ser usadas. extern int factorial(int). } y otro archivo factorial. compilar cada archivo fuente en un archivo objeto. res=1.

o Cuando se actualiza una biblioteca. debemos u hacer lo siguiente: $ mkdir . El ultimo paso es crear un ´ ´ ındice para la biblioteca. entonces hubieramos podido teclear: ´ o $ar rs libmm.o factorial./lib -o prueba prueba... que lo podemos hacer de la siguiente forma: $ gcc -c cubo.o . es la reducci´n en el consumo de memoria. para hacer posible que sea usado o o el c´digo por varios programas.9.o Ahora que ya tenemos la biblioteca.VALOR. es conveniente que coloquemos nuestra biblioteca y el archivo cabezera en alg´n lugar apropiado. lo que permite que el ligador pueda encontrar las rutinas. adem´s de la reducci´n del tama˜o del c´digo ejecutable.a. Despu´s se debe crear la biblioteca con los archivos fuentes. por lo que teclearemos ahora: $ ranlib libmm. n El c´digo de la biblioteca compartida necesita ser independiente de la posici´n. } Para crear la biblioteca se deben compilar los archivos fuente. a e suponiendo que nuestra biblioteca se llame libmm. cubo(VALOR) ).o factorial.a cubo.o factorial.a).h .o.c factorial./lib $ mv libmm./lib Si llegar´s a modificar la biblioteca. COMPILACION DE UN PROGRAMA EN C/C++ { printf("El cubo de %d es %f\n".a cubo. Se requiere lo anterior s´lo si se modifico el n´mero de argumentos con los que se llama una funci´n o se cambio o u o el tama˜o de alguna estructura. Para crear la biblioteca hacerlo de la siguiente forma: o $ gcc -c -fPIC cubo.a Los ultimos dos pasos pudieron ser combinados en uno s´lo.factorial(VALOR) )./include -L.. si son usadas o por m´s de un proceso. tendr´s que hacerlo con el comando ar as´ a ı: $ ar r libmm. printf("\t y su factorial es %d\n".c 6 Lo cual nos dejar´ los archivos cubo./include $ mkdir ../include $ mv libmm.c Para generar la biblioteca din´mica hacer lo siguiente: a $ gcc -shared -o libmm. ya que cuando se hace alg´n cambio en la biblioteca. a ıas ´ o Se debe ahora compilar el archivo con la biblioteca. tendr´ que repetir la ultima instrucci´n. Lo anterior. Supongamos que dejamos la biblioteca en ~/lib y el fichero cabezera en ~/include. lo hacemos con el comando ranlib.o y factorial.´ CAP´ ITULO 1. Creaci´n de una biblioteca compartida o Las ventajas que presentan las bibliotecas compartidas..so cubo.c -lmm 1.a . de la siguiente forma: gcc -I. se necesita borrar el archivo anterior (libmm. no se necesita recompilar y reenlazar la aplicaci´n cada a u o vez.. Tambi´n se hace el desarrollo m´s a a o n o e a f´cil.2.c factorial.

. o a Despu´s habr´ que mover la biblioteca din´mica a su directorio correspondiente (.2 => /lib/ld-linux.c -lmm Nos preguntamos que sucede si hay una biblioteca compartida (libmm. COMPILACION DE UN PROGRAMA EN C/C++ 7 No existe un paso para la indexaci´n como ocurre en las bibliotecas est´ticas. puede leer la p´gina tecleando: o a man 3 sqrt Si no sabe el nombre de la funci´n./lib -o prueba prueba.so. El n´mero tiende a incrementarse con cada actualizaci´n al sistema. Algunas implementan operaciones de uso frecuente.2 (0x40000000) Como se ve en cada l´ ınea aparece el nombre de la biblioteca. Si ldd muestra como salida not found para alguna biblioteca.so.so) y una est´tica (libmm.. Por ejemplo: a o o o .so => libstuff. Con lo anterior se reduce el tiempo de desarrollo de un programa.so.so.a Cuando se usan bibliotecas compartidas un comando util es ldd. y hay muchas otras utiles que hacen llamadas al sistema en la secci´n 2.10.6 => /lib/i686/libc.6 (0x4002f000) /lib/ld-linux. Funciones de la biblioteca de UNIX El sistema UNIX da un gran n´mero de funciones de C. a continuaci´n un ejemplo: o $ ldd prueba libstuff./include -L.. el cual nos informa que bibliotecas compartidas ´ un programa ejecutable usa. Para leerlo. o El manual de UNIX tiene una entrada para todas las funciones disponibles. La documentaci´n de funciones esta o guardada en la secci´n 3 del manual. u o En cualquier p´gina del manual. que siempre busca por default en lib y /usr/lib. Si se tienen bibliotecas en otro directorio. a En este caso. Ya que las funciones o de la biblioteca han sido probadas. Lo anterior reducir´ el tiempo de depuraci´n del programa. una lista completa esta incluida en la p´gina introductoria de la secci´n 3 del o a o manual.1. a diferencia de cualquiera que un programador a pueda escribir.10. Se recomienda revisar si una funci´n en alguna biblioteca existe. Encontrando informaci´n acerca de las bibliotecas. por lo que estar´n corregidas. teclee: man 3 intro Hay aproximadamente 700 funciones. se van a tener problemas y el programa no podra ser ejecutado. la secci´n de SYNOPSIS incluye informaci´n del uso de la funci´n.´ CAP´ ITULO 1. y donde en el espacio de direcciones virtuales la biblioteca esta mapeada./include -L.c libmm. Si o ´ o ya sabe el nombre de la funci´n que se quiere revisar.. Si se desea hacer uso de la est´tica. u mientras otras est´n muy especializadas en relaci´n a su aplicaci´n.. o $ gcc -I.a) disponibles. en vez de hacer la tarea o de escribir su propia versi´n. a o 1. el ligador siempre toma la compartida. se tendr´ que nombrar a a expl´ ıcitamente en la l´ ınea de comandos: $ gcc -I. crear una variable de ambiente LD_LIBRARY_PATH y poner los directorios separados por ../lib -o prueba prueba.so (0x40018000) libc. 1. el camino completo a la biblioteca que es usada. Una forma de arreglarlo es buscar la biblioteca y colocarla en el lugar correcto para que el programa loader la encuentre./lib) y proceder a compilar e a a para que nuestro c´digo use la biblioteca. a o o No reinvente la rueda.

i. Incluye con comentarios a e dentro del programa tu nombre y la forma como hicistes lo anterior. a .0)). o n o o 1. En la secci´n DESCRIPTION se da una peque˜a descripci´n de lo que hace la funci´n. COMPILACION DE UN PROGRAMA EN C/C++ #include <time.h> main() { int i.h> 8 en el archivo del programa que hace el llamado a ctime.i*i*i ). Para iniciar un comentario usa /* y para terminarlo */: main() { int i. Lo que significa que se debe tener #include <time.h> char *ctime(const time_t *timep). y regresa una cadena char *. Escribe. Use el comando man para obtener detalles de las funciones de la biblioteca. } 3. puedes teclear: ar tv biblioteca o 4.i. Ejercicios 1. compila y corre el siguiente programa. } 2. Selecciona alguna biblioteca y ve los c´digos objetos que contiene. Busca en los directorios /lib y /usr/lib las bibliotecas que est´n disponibles. printf("\tAngulo \t\t Seno\n\n"). for( i=0. for( i=0. ++i) printf("\t %d \t\t\t %d \n". El siguiente programa usa la biblioteca matem´tica. Modifica el programa para que incluyas tu nombre y la forma como hicistes lo anterior. printf("\t Numero \t\t Cubo\n\n").11. i<=20. Tecl´alo. compilalo y ejecutalo. Y que la funci´n ctime toma un apuntador del tipo o time_t como un argumento. i<=360.´ CAP´ ITULO 1.sin((double) i*M_PI/180. Ve en /usr/include que archivos de cabecera est´n disponibles. Manda 3 nombres de est´ticas a a y 3 de compartidas. i+=15) printf("\t %d \t\t\t %f \n". #include <math.

9 Explora los archivos cabecera para ver el contenido. define.c y que se tienen otras o funciones en los archivos input. 5. indicando de que archivo de cabezera las obtuvistes. observando las directivas de preprocesamiento include. ¿C´mo se modificar´ los comandos de compilaci´n para e o ıan o hacer lo se˜alado? n . Envia 10 casos donde se hayan usado las directivas anteriores.c: ¿De qu´ forma habr´ que compilar y ligar este programa? e ıa ¿C´mo se deber´n modificar los comandos anteriores para ligar una biblioteca llamada proceso1 guardada o a en el directorio est´ndar de las bibliotecas del sistema? a ¿C´mo modificar´ los comandos anteriores para ligar una biblioteca llamada proceso2 guardada en tu o ıas directorio casa? Algunos archivos cabecera necesitan ser le´ ıdos y encontrados en el subdirectorio header de su directorio casa y tambi´n en directorio de trabajo actual. COMPILACION DE UN PROGRAMA EN C/C++ Usa los comandos more o cat para ver los archivos de texto.´ CAP´ ITULO 1. las definiciones de los tipos y los prototipos de las funciones. Supongamos que se tiene un programa en C el cual tiene la funci´n principal en main.c y output.

el cual empez´ a o e o trabajar en un est´ndar ANSI C. Durante muchos a˜os el est´ndar para C fue la versi´n 5 del sistema operativo UNIX. 2. Kernighan and Dennis M. Para o o o 1973 el sistema operativo UNIX estaba casi totalmente escrito en C. El BCPL tuvo influencia en un lenguaje llamado B. Ritchie in 1978 com´nmente referido como u K&R. En el resto del cap´ ıtulo se ven los aspectos b´sicos de los programas de C. el cual se us´ en 1970 y fue inventado o por Ken Thompson y que permiti´ el desarrollo de C en 1971.1.2. que fue desarroo llado por Martin Richards. 10 . la declaraci´n a o de variables. Or´ ıgenes del C El proceso de desarrollo del lenguaje C se origina con la creaci´n de un lenguaje llamado BCPL. Caracter´ ısticas de C Algunas de las caracter´ ısticas m´s importantes que definen el lenguaje y que han permitido que sea tan popular. documentada en “The C n a o Programming Language” escrito por Brian W.Cap´ ıtulo 2 Principios de C En este cap´ ıtulo se ofrece una breve historia del desarrollo del lenguaje C y se consideran tambi´n sus carace ter´ ısticas. a 2. n n Uso extensivo de llamadas a funciones. el cual lo invent´ e implement´ Dennis Ritchie. tipos de datos y operadores. a como lenguaje de programaci´n son: o Tama˜o peque˜o. tales como su estructura. Posteriormente se hicieron varias implementaciones las cuales mostraban las siguientes tendencias: Nuevas caracter´ ısticas Diferencias de m´quinas a Diferencias de productos Errores en los compiladores Malas implementaciones Esto origin´ que en el verano de 1983 se estableciera un comit´ para resolver estas discrepancias. la cual fue completada en 1988.

lo cual en ocasiones es problem´tico para los o a principiantes. El generar programas eficientes. estructuras y funo ciones Las diversas razones por la cual se ha convertido en un lenguaje de uso profesional son: El uso de constructores de alto nivel. A continuaci´n se muestra un primer programa: o . La posibilidad de poder ser compilado en una variedad de computadoras. Programaci´n de bajo nivel (nivel bit) o 11 Implementaci´n de apuntadores – uso extensivo de apuntadores para la memoria. 2. Lenguaje estructurado.CAP´ ITULO 2. o Una funci´n tiene la forma: o tipo nombre_de_la_funcion (par´metros) a { variables locales sentencias de C } Si la definici´n del tipo es omitida. Un punto en contra es que tiene una detecci´n pobre de errores. Nota: Lo anterior puede ser o o una fuente de problemas en un programa. El poder manejar actividades de bajo-nivel. C asume que la funci´n regresa un tipo entero. PRINCIPIOS DE C Comandos breves (poco tecleo).3. o Variables Funciones Para un programa se debe tener una funci´n main(). Definiciones de tipos. arreglos. con pocos cambios (portabilidad). Prototipos de funciones – declara el tipo de funci´n y las variables pasadas a la misma. Estructura de un programa en C Un programa de C tiene b´sicamente la siguiente forma: a Comandos del preprocesador.

exit (0). Se usa un modificador para alterar el a significado de un tipo base para que encaje con las diversas necesidades o situaciones.CAP´ ITULO 2. printf es una funci´n est´ndar de C. En el sentido estricto no es necesario e o a ya que es la ultima l´ ´ ınea de main() y de cualquier forma terminar´ el programa. La salida tendr´ la siguiente forma: ıa .. long y short.. la cual es llamada en la funci´n main(). Salida formateada...7 × 10±308 Cuadro 2. exit() es tambi´n una funci´n est´ndar que hace que el programa termine. PRINCIPIOS DE C 12 /* Programa ejemplo */ main() { printf( "Me gusta C\n" ). Variables C tiene los siguientes tipos de datos simples: Los tipos de datos b´sicos tiene varios modificadores que les preceden.1: Tipos de C .2 .\n.3\n").2\n. a En caso de que se hubiera llamado a la funci´n printf de la siguiente forma: o printf("..2 × 10±38 −1. Los modificadores son: signed.1\n. unsigned. o a o \n significa salto de l´ ınea. Tipo char unsigned char short int unsigned short int (long) int float double Tama˜o (bytes) n 1 1 2 2 4 4 8 L´ ımite inferior — 0 −32768 0 −231 −3.1 . } NOTAS: C requiere un punto y coma al final de cada sentencia.4.3 2..2 × 10±38 +1.7 × 10±308 L´ ımite superior — 255 +32767 65536 +231 − 1 +3.

char letra. char ch..y. main() { . 2. j. int sumagr= 0. sumagr. Una variable global o puede ser utilizada en cualquier parte del programa. int numerogr. es redundante porque la declaraci´n de entero por defecto asume un n´mero con signo.z. o u Para declarar una variable en C. char letra= ’A’. float x. a tipo es un tipo v´lido de C y lista variables puede consistir en uno o m´s indentificadores separados por una a coma. PRINCIPIOS DE C 13 En los sistemas UNIX todos los tipos int son long int. o Ejemplo: int i. a u signed. incluyendo a la funci´n main().. por ejemplo: e o float suma= 0. int sumagr.4. suma. unsigned. Por ejemplo: short numero. a menos que se especifique expl´ ıcitamente short int. main() { .1. long y short pueden ser usados con los tipos char e int. k. Un identificador debe comenzar con una letra o un gui´n bajo.0.. Nota: no hay un tipo booleano en C — se deber´ usar char. } Que es lo mismo que: float suma.. . se debe seguir el siguiente formato: tipo lista_variables. int o a´n mejor unsigned char. Aunque es permitido el uso de signed en enteros.CAP´ ITULO 2. Definici´n de variables globales o Una varible global se declara fuera de todas las funciones. char letra. } Es tambi´n posible preinicializar variables globales usando el operador de asignaci´n =.

letra = ’A’. .i. Asegurarse que el orden de formateo y los tipos de datos de las variables coincidan. Su formato es similar a printf. PRINCIPIOS DE C 14 main() { suma = 0. letra sig_letra. si todos los tipos de las variables son iguales. La sentencia de formato se encierra entre " ". %c caracteres %s cadena de aracteres %d enteros %f flotantes Por ejemplo: printf("%c %d %f"..2. 3. typedef float real. /* Declaracion de variables usando el nuevo tipo */ real suma=0. 3. o u Se pueden redefinir los tipos de C usando typedef. por ejemplo: e o u a = b = c = d = 3. y enseguida las variables. La funci´n printf tiene un caracter especial para formatear ( %) — un o caracter enseguida define un cierto tipo de formato para una variable. . Estos nuevos tipos pueden ser usados de igual forma como los tipos predefinidos de C.x). sumagr= 0. Por ejemplo: o . que es lo mismo.0. } Dentro de C tambi´n se permite la asignaci´n m´ltiple usando el operador =.CAP´ ITULO 2. . scanf() es la funci´n para entrar valores a variables. 3.ch. pero m´s eficiente que: a a b c d = = = = 3.4. 2.. Lectura y escritura de variables El lenguaje C usa salida formateada. Como un ejemplo de un simple uso se considera como se crean dos nuevos tipos real y letra. typedef char letra. La asignaci´n m´ltiple se puede llevar a cabo. .0.

Lo anterior simplemente indica que o a o la funci´n no cambiara el valor del par´metro. } Que es equivalente a: . PRINCIPIOS DE C scanf("%c %d %f %s". * multiplicaci´n.resta. por ejemplo: i=4.6. e Es usual inicializar una constante con un valor.5. ++z es prefijo y -. y cuando es postfijo el valor es calculado despu´s que la expresi´n es evaluada. Cuando son prefijos.z. / divisi´n y % m´dulo). a a Los operadores ++ y -. excepto a la cadena de caracteres. Notas: Se puede usar const antes o despu´s del tipo. La palabra clave const se usa para declarar una constante. La directiva del preprocesador #define es un m´todo m´s flexible para definir constantes en un programa.) ) % 100. En el cap´ ıtulo 8 que trata sobre apuntadores se revisar´ m´s a fondo el uso de este operador.es postfijo: int x. excepto que el valor no puede ser cambiado. 15 Observar que se antepone & a los nombres de las varibles.y. la siguiente funci´n usa este concepto: o a o char *strcpy(char *dest. const char *orig). o e a . Operadores Aritm´ticos e Lo mismo que en otros lenguajes de programaci´n. &i. Cuando se declara una constante es un poco parecido a declarar una variable. o Incremento ++ y decremento -. cuando se use la funci´n de la biblioteca a o para copiar cadenas. ya que no puede ser cambiada de alguna otra forma. como se muestra a continuaci´n: o const a = 1. Constantes ANSI C permite declarar constantes. Por ejemplo: a x++ es m´s r´pido que x=x+1. ch=’y’.CAP´ ITULO 2. Por ejemplo. &x. Los cuales son m´s eficientes que las respectivas asignaciones.unario.( y-. o e o En el siguiente ejemplo. 2. en C se tienen los operadores aritm´ticos m´s usuales (+ suma.&ch. o o o El operador de asignaci´n es =. El segundo argumento orig es una cadena de C que no ser´ alterada.pueden ser prefijos o postfijos. main() { x=( ( ++z ) . a a 2. e a Frecuentemente se ve la declaraci´n const en los par´metros de la funci´n. cad). el valor es calculado antes de que la expresi´n sea evaluada. int a = 2.

Si se desea obtener la divisi´n con la fracci´n.. <= menor que o igual a y >= (mayor que o igual a). 2. otros operadores son: < menor que. Es una sentencia legal de C (sint´cticamente hablando aunque el compilador avisa cuando se emplea).0 y a´n mejor x = 3. > mayor que. x = ( z-y ) % 100. } 16 El operador % (m´dulo o residuo) solamente trabaja con enteros. Operadores de Comparaci´n o El operador para probar la igualdad es ==.0. si j es diferente de cero. o x = x * (y + 2). u Por otra parte. o o o x = 3 / 2. a Diferente es !=. u o entoces el resultado es entero. a El operador divisi´n / es para divisi´n entera y flotantes.0 / 2.8. lo cual ser´ interpretado como VERDADERO. es uno. || O l´gico y ! negaci´n. existe una forma m´s corta para expresar c´lculos en C. dependiendo de lo que se desee hacer (a + b) * c .7. pero menos eficiente que: expr1 = expr1 oper expr2 Por lo que podemos reescribir las expresiones anteriores como: i += 3. Por ejemplo. El resultado de o o x = 3 / 2. entonces escribirlo como: x = 3. los operadores b´scios o a l´gicos son: o && Y l´gico. . PRINCIPIOS DE C int x. si se tienen expresiones a a como: i = i + 3. Por lo tanto hay que tener cuidado. Orden de precedencia Es necesario ser cuidadosos con el significado de expresiones tales como a + b * c. Operadores l´gicos o Los operadores l´gicos son usualmente usados con sentencias condicionales o relacionales. por lo que se deber´ tener cuidado de no escribir accidentalmente s´lo a o =.0 / 2.CAP´ ITULO 2.1 o o fmod() ) de la biblioteca matem´tica.y. ya que: if ( i = j ) . 2. pueden ser reescritas como: expr1 oper = expr2 Lo cual es equivalente. y x *= y + 2.z.9. La regla es: si ambos argumentos en una divisi´n son enteros. respectivamente. y--. a´n si x es declarado como float. la cual a copia el valor de “j” en “i”. o o o 2.. aunque existe una funci´n para flotantes (15. main() { z++.

PRINCIPIOS DE C o a + (b * c) 17 Todos los operadores tienen una prioridad. Los operadores que tienen la misma prioridad son evaluados de izquierda a derecha..(tipo) * & sizeof */% +<< >> < <= > >= == != & ^ | && || ? = += -= *= /= .b) . M´s baja a De acuerdo a lo anterior. como a = ( b = ( 10 / 5 ) + 2 ).c Prioridad M´s alta a Operador(es) ( ) [ ] -> ! ~ ++ -.b .c es evaluado como (a . los operadores de mayor prioridad son evaluados antes que los que tienen menor prioridad. la siguiente expresi´n: o a < 10 && 2 * b < c Es interpretada como: (a < 10) && ( (2 * b) < c ) y a = b = 10 / 5 + 2. por lo que: a .CAP´ ITULO 2. .

Leer la entrada de dos n´meros y mostrar el doble producto del primero menos la mitad del segundo.2 pulgadas. con un caracter de nueva l´ ınea en cada una. Si la entrada es 333. 5. e imprime como u un “flotante” la temperatura equivalente en grados Fahrenheit.0 grados Celsius son 212. Escribir un programa que lea el radio de un c´ ırculo como un n´mero flotante y muestre el area y el per´ u ´ ımetro del c´ ırculo. 4. Lea y escriba su nombre. 6. Escribir un programa para imprimir varias veces el ejercicio 2. imprimir su equivalencia a pies (enteros) y pulgadas (flotante. Puede usar varias instrucciones printf.54 cent´ o ımetros por pulgada.CAP´ ITULO 2. 3.3. 1 decimal).10. dando las pulgadas con una precisi´n de un lugar decimal Suponer 2. u 2. La salida puede ser de la siguiente forma: 100. Dados ciertos cent´ ımetros como entrada de tipo flotante.0 grados Fahrenheit. apellido materno y matricula en un formato adecuado. el formato de la salida deber´ ser: a 333. PRINCIPIOS DE C 18 2. .3 cent´ ımetros son 10 pies 11. apellido paterno. y 12 pulgadas por pie. o una instrucci´n con varios caracteres nueva l´ o ınea en la cadena de formateo. Escribir un programa para leer un “flotante” que representa un n´mero de grados Celsius. Ejercicios Escribir programas en C para hacer las siguientes tareas: 1.

1.o if (condicion1 ) 19 . <=. que s´lo toma un argumento. e o Como se revis´ en el cap´ o ıtulo anterior..Cap´ ıtulo 3 Estructuras Condicionales En este cap´ ıtulo se revisan los distintos m´todos con los que C controla el flujo l´gico de un programa. . los operadores relaciones binarios que se usan son: ==.. && y el operador l´gico unario de negaci´n !. La sentencia if Las tres formas como se puede emplear la sentencia if son: if (condicion) sentencia. else sentencia2 . 3.. o o o Los operadores anterior son usados con las siguientes estructuras que se muestran. !=. > y >= adem´s los operadores l´gicos binarios: a o ||.o if (condicion) sentencia1 . <.. .

2. 20 El flujo l´gico de esta estructura es de arriba hacia abajo.. . Por ejemplo.. El operador ? tiene el siguiente formato: a expresion1 ? expresion 2 : expresion3 .. . Si existen m´s condiciones dentro u o a de la estructura if.. if (x>0) { z=w... siempre y cuando las condiciones que le precedan sean falsas.. para asignar el m´ximo de a y b a la variable z. entonces se ejecuta la sentencia asociada. Por ejemplo: main() { int x.CAP´ ITULO 3. usando ?. y si la condici´n es verdadera... Que es equivalente a la siguiente expresi´n: o if (expresion1 ) then expresion2 else expresion3 .. w. } else { z=y.. else if (condicion2 ) sentencia2 .. e La sentencia que esta asociada a la palabra reservada else. . } } 3. Si la primera condici´n fue falsa.. El operador ? El operador ternario condicional ? es m´s eficiente que la sentencia if. y... La primera sentencia se ejecutar´ y se saldr´ de la o a a estructura if si la primera condici´n es verdadera. y existe otra condici´n. se van evaluando ´stas.. se o o o eval´a... else sentencian.. ESTRUCTURAS CONDICIONALES sentencia1 .. se ejecuta si todas las condiciones de la estructura if fueron falsas. .. . tendr´ a ıamos: z = (a>b) ? a : b.

Con esta sentencia. como en el ejemplo siguiente: f1(int n) { printf("%d ". 21 El uso del operador ? para reemplazar las sentencias if . la o u computadora comprueba una variable sucesivamente frente a una lista de constantes enteras o de caracter.. . La forma general de la sentencia switch es: switch (variable) { case constante1: secuencia de sentencias break..&t).. scanf("%d".3.n). Se pueden ejecutar una o m´s llamadas de funci´n usando el operador ? poni´ndolas en las a o e expresiones que forman los operandos. La sentencia switch Aunque con la estructura if . C tiene incorporada una sentencia de bifurcaci´n m´ltiple llamada switch. Despu´s e de encontrar una coincidencia. } f2() { printf("introducido\n"). /* imprime mensaje apropiado */ t ? f1(t) + f2() : printf("Se di´ un cero\n"). else no se restringe s´lo a asignaciones. o } 3. la computadora ejecuta la sentencia o bloque de sentencias que se asocian con la constante. } main() { int t. else if se pueden realizar comprobaciones m´ltiples. o ıcil Por lo anterior.CAP´ ITULO 3. else z = b. printf(": "). en ocasiones no es muy u elegante.. ya que el c´digo puede ser dif´ de seguir y puede confundir incluso al autor transcurrido un tiempo. como en o el ejemplo anterior. ESTRUCTURAS CONDICIONALES que es lo mismo que: if (a > b) z = a.

No se puede probar m´s de una constante por case. a La forma como se puede simular el ultimo punto. break. Cuando se encuentra una coincidencia.... esta ultima ´ es opcional. la computadora ejecuta las sentencias asociadas con el case hasta encontrar la sentencia break con lo que sale de la estructura switch. es decir. con lo que se permite que el flujo del programa caiga al omitir las o sentencias. o Con switch s´lo se puede comprobar por igualdad. break. case ’ ’: numesp++.. Las limitaciones que tiene la sentencia switch . es no teniendo sentencias asociados a un case. break. mientras que con if puede ser con cualquier operador o relacional. como se muestra a continuaci´n: o switch (letra) { case ’a’: case ’e’: case ’i’: case ’o’: case ’u’: numvocales++. case constante3: secuencia de sentencias break. } . ESTRUCTURAS CONDICIONALES case constante2: secuencia de sentencias break. default: numotras++. 22 default: secuencia de sentencias } donde la computadora ejecuta la sentencia default si no coincide ninguna constante con la variable.CAP´ ITULO 3. case respecto a la estructura if son: S´lo se tiene posibilidad de revisar una sola variable. . teniendo ´ una sentencia nula donde s´lo se pone el caso.

CAP´ ITULO 3. ESTRUCTURAS CONDICIONALES

23

3.4.

Ejercicios

1. Escribir un programa que lea dos caracteres, e imprima su valor cuando se pueda interpretar como un n´mero u hexadecimal. Aceptar letras may´sculas y min´sculas para los valores del 10 al 15. u u 2. Leer un valor entero. Suponer que el n´mero es un d´ de la semana. Suponer que 0 corresponde a Domingo y u ıa as´ sucesivamente. Imprimir el nombre del d´ ı ıa. 3. Dados como entrada 3 enteros representando la fecha como d´ mes, a˜o, imprimir la fecha del d´ anterior. ıa, n ıa Por ejemplo para una entrada como: 1 3 1992 La salida ser´: Fecha anterior a 1-3-1992 es 29-02-1992 a 4. Escribir un programa el cual lea dos valores enteros. Si el primero es menor que el segundo, que imprima el mensaje “Arriba”. Si el segundo es menor que el primero, que imprima el mensaje “Abajo”. Si los n´meros son u iguales, que imprima el mensaje “igual”. Si hay un error en la lectura de los datos, que imprima un mensaje conteniendo la palabra “Error” y haga exit( 0 );

Cap´ ıtulo 4

Iteraci´n o
En este cap´ ıtulo se revisan los mecanismos de C para repetir un cojunto de instrucciones hasta que se cumple cierta condici´n. o

4.1.

La sentencia for

La sentencia for tiene el siguiente formato:

for ( expresion1 ; expresion2 ; expresion3 ) sentencia; o { bloque de sentencias } En donde expresion1 se usa para realizar la inicializaci´n de variables, usando una o varias sentencias, si se o usan varias sentencias deber´ usarse el operador , para separarlas. Por lo general, establece el valor de la variable de a control del ciclo. expresion2 se usa para la condici´n de terminaci´n del ciclo y expresion3 es el modificador a la o o variable de control del ciclo cada vez que la computadora lo repite, pero tambi´n puede ser m´s que un incremento. e a Por ejemplo: int X; main() { for( X=3; X>0; X--) { printf("X=%d\n",X); } } genera la siguiente salida a pantalla . . . X=3 X=2 X=1 Todos las siguientes sentencias for son v´lidas en C. Las aplicaciones pr´cticas de tales sentencias no son impora a tantes aqu´ ya que tan s´lo se intenta ilustrar alguanas caracter´ ı, o ısticas que pueden ser de utilidad: 24

´ CAP´ ITULO 4. ITERACION for ( x=0; ( (x>3) && (x<9) ); x++ ) for ( x=0, y=4; ( (x>3) && (x<9) ); x++, y+=2) for ( x=0, y=4, z=4000; z; z/=10)

25

En el segundo ejemplo se muestra la forma como m´ltiples expresiones pueden aparecer, siempre y cuando est´n u e separadas por una coma , En el tercer ejemplo, el ciclo continuar´ iterando hasta que z se convierta en 0. a

4.2.

La sentencia while

La sentencia while es otro ciclo o bucle disponible en C. Su formato es:

while ( expresion) sentencia;

donde sentencia puede ser una sentencia vac´ una sentencia unica o un bloque de sentencias que se repetir´n. ıa, ´ a Cuando el flujo del programa llega a esta instrucci´n, primero se revisa si la condici´n es verdad para ejecutar la(s) o o sentencia(s), y despu´s el ciclo while se repetir´ mientras la condici´n sea verdadera. Cuando llega a ser falsa, el e a o control del programa pasa a la l´ ınea que sigue al ciclo. En el siguiente ejemplo se muetra una rutina de entrada desde el teclado, la cual se cicla mientras no se pulse A: main() { char carac; carac = ’\0’; while( carac != ’A’) carac = getchar(); } Antes de entrar al ciclo se inicializa la variable carac a nulo. Despu´s pasa a la sentencia while donde se comprueba e si carac no es igual a ’A’, como sea verdad entonces se ejecuta la sentencia del bucle (carac = getchar();). La funci´n getchar() lee el siguiente car´cter del flujo est´ndar (teclado) y lo devuelve, que en nuestro ejemplo es el o a a caracter que haya sido tecleado. Una vez que se ha pulsado una tecla, se asigna a carac y se comprueba la condici´n o nuevamente. Despu´s de pulsar A, la condici´n llega a ser falsa porque carac es igual a A, con lo que el ciclo termina. e o De lo anterior, se tiene que tanto el ciclo for, como el ciclo while comprueban la condici´n en lo alto del ciclo, o por lo que el c´digo dentro del ciclo no se ejecuta siempre. o A continuaci´n mostramos otro ejemplo: o main() { int x=3; while( x>0 ) { printf("x = %d\n", x); x--;

podemos realizar una operaci´n completa dentro de la expresi´n. while ( x += 5 ). carac = ’\0’. la condici´n o fallar´ y se podr´ salir del ciclo. 4. o . while ( x = x + 1 ). dentro del ciclo tenemos m´s de una sentencia.. por lo que se requiere usar la llave abierta y la a llave cerrada { .. x=x+1 o x+=5 sea cero.. } para que el grupo de sentencias sean tratadas como una unidad. y no solamente condiciones lo siguiente es v´lido: e a while ( x-. a a De acuerdo a lo anterior. Como el ciclo while pueda aceptar tambi´n expresiones. ITERACION } } que genera la siguiente salida en pantalla: x = 3 x = 2 x = 1 26 Como se observa. solamente cuando el resultado de x--. } while (condici´n).. El ciclo while proceder´ a leer del teclado y lo mostrar´ hasta que a a el caracter A sea le´ ıdo. La forma general del ciclo es: do { sentencia. Esta caracter´ ıstica provoca que un ciclo do .. Por ejemplo: o o main() { char carac. Si se usan este tipo de expresiones. while ( (carac = getchar()) != ’A’ ) putchar(carac). el bucle do .3. } En este ejemplo se usan las funciones de la biblioteca est´ndar getchar() — lee un caracter del teclado y a putchar() escribe un caracter dado en pantalla.. La sentencia do-while Al contrario de los ciclos for y while que comprueban la condici´n en lo alto del bucle. while o la examina en la parte baja del mismo. while siempre se ejecute al menos una vez.´ CAP´ ITULO 4.).

break. y no del compilador) con la sentencia while. case 3: printf("\tOpcion 3 seleccionada\n\n"). default: printf("\tOpcion no disponible\n\n"). } . } Otro uso com´n de la estructura do . case 2: printf("\tOpcion 2 seleccionada\n\n"). while es una rutina de selecci´n en un men´. Limites\n"). break.. ya que siempre se requiere u o u que se ejecute al menos una vez. Integrales\n"). switch(opc) { case 1: printf("\tOpcion 1 seleccionada\n\n"). printf("1. &opc). printf("2. } } while( opc != 1 && opc != 2 && opc != 3). "). } while ( num>100 ).. se usan normalmente por legibilidad o a y para evitar confusi´n (respecto al lector. while para leer n´meros desde el teclado hasta que uno de ellos u es menor que o igual a 100: main() { int num.. break. printf("3. o En el siguiente programa se usa un ciclo do . Derivadas\n"). &num). main() { int opc. do { scanf("%d". do { printf(" Teclear una opcion: scanf("%d".´ CAP´ ITULO 4. break.. ITERACION 27 Aunque no son necesarias las llaves cuando s´lo est´ presente una sentencia.

y si el valor es cero. do { printf("x = %d\n". while uno de los ejemplos ya mostrados. se ignora y se continua leyendo. x--). en vez de forzar la salida.´ CAP´ ITULO 4. Uso de break y continue Como se comento uno de los usos de la sentencia break es terminar un case en la sentencia switch. x++) { if (x%2) continue. se desea terminar el ciclo.4. t). La sentencia continue funciona de manera similar a la sentencia break. cuando alcanza el valor 10 se cumple la condici´n de u o la sentencia if. for( x=0. for(t=0.. continue fuerza la siguiente iteraci´n. se desea imprimir un mensaje de error y se abandona el ciclo. x<100. printf("%d ". ITERACION Se muestra un ejemplo donde se reescribe usando do . saltando la prueba condicional del ciclo. o Cuando se encuentra la sentencia break en un bucle. } } Este programa muestra en pantalla los n´meros del 0 al 10.x). la computadora termina inmediatamente el ciclo y el control del programa pasa a la siguiente sentecia del ciclo. .. } while( x>0 ) . if (t==10) break. el siguiente programa visualizar´ s´lo los n´meros pares: a o u main() { int x. } } Finalmente se considera el siguiente ejemplo donde se leen valores enteros y se procesan de acuerdo a las siguientes condiciones. Sin embargo. Por o o o ejemplo. Por ejemplo: main() { int t. Si el valor que sea le´ es negativo. main() { int x=3. t<100. Si el ıdo valor es mayor que 100. Otro uso es forzar la terminaci´n inmediate de un ciclo. se ejecuta la sentencia break y sale del ciclo. } 28 4. por lo que salta el c´digo que falta para llegar a probar la condici´n. t++) { printf("%d ".

El programa podr´ tener la siguiente u a ıa salida: Entrada Base Numero ============== 10 1234 8 77 2 1111 Salida ========= 1234 63 15 4. Escribir un programa que lea un n´mero en base 10 y lo convierta a base 2. base 8 y base hexadecimal.´ CAP´ ITULO 4. u u a luego el tercero se restar´. } } 29 4. octal o a e decimal).5. ITERACION main() { int valor. Escribir un programa que lea un valor entero que ser´ la base para un sistema num´rico (binario. while( scanf("%d". break. La base ser´ menor que o igual a 10. y as´ se deber´ seguir alternado hasta que se llegue al cero. /* Pasar al principio del ciclo nuevamente */ } printf("Se garantiza que el valor leido esta entre 1 y 100"). el m´ximo y el m´ u a ınimo de esos valores. despu´s que lea un entero positivo en esa base y que imprima su valor en base 10. el total de operandos de la operaci´n (sin a o incluir el cero). a a ı a Cuando se llegue a esta condicion deber´ imprimir el resultado. /* Salir del ciclo */ } if ( valor>100) { printf("Valor no valido\n"). continue. &valor) == 1 && valor != 0) { if ( valor<0 ) { printf("Valor no valido\n"). Se debe validar que e el n´mero pertenezca a esa base. Leer tres valores representando lo siguiente: El capital (n´mero entero de pesos) u . 3. Escribir un programa que lea 5 n´meros y encuentre el promedio. u 5. y la suma de los operandos que se restaron. el cuarto se sumar´. El segundo n´mero se sumar´ al primero. Escribir un programa que lea n´meros hasta que se encuentre el cero. 2. Ejercicios 1.

05 89802.98 4 6229.38 79824.14 6 7883.47 113656. si es non.41 8 9978. Para cada a˜o n n el interes es calculado como: interes = capital * tasa_interes / 100. el cual se suma al capital capital += interes. y hacer la siguiente secuencia: si el n´mero es par.11 49833.´ CAP´ ITULO 4.50 en 10 a~os n A~o n Interes Suma -----+-----------+--------1 4375. Leer un valor positivo. Imprimir los valores del inter´s compuesto o e para cada a˜o al final del per´ n ıodo.76 10 12628.03 7 8869.00 39375. Repetir lo anterior hasta que el valor sea 1. u n 30 Calcular los valores de la suma del capital y el interes compuesto para un per´ ıodo dado de a˜os.31 101027.45 9 11225. a Una salida podr´ ser la siguiente: ıa El valor inicial es 9 El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es El siguiente valor es Valor fina1 1. multiu plicarlo por 3 y sumarle 1.23 6.90 63071.00 2 4921.89 70955.88 3 5537.88 44296. tambi´n se e deber´ imprimir cuantas operaciones de estas son hechas. imprimiendo cada valor. Imprimir los valores de moneda con una precisi´n de dos decimales. ITERACION Una tasa de interes en porciento (flotante) y un n´mero de a˜os (entero). . La salida puede ser como la siguiente: Capital inicial 35000.25 56063.23 5 7007. numero 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 de pasos 19.00 con tasa del 12. dividirlo entre 2.

imprimir un mensaje que contenga la palabra Error y haga exit(0) 31 . ITERACION Si el valor ingresado es menor que 1.´ CAP´ ITULO 4.

Por ejemplo un arreglo de enteros bidimensionales se escribir´ como: a int tabladenums[50][50]. Un arreglo puede tener una o varias dimensiones. es de la siguiente forma: listanum[2] = 15. El formato para declarar un arreglo unidimensional es: tipo nombre arr [ tama˜ o ] n Por ejemplo. Las cadenas se consideran como un arreglo de tipo char. /* Asigna el contenido del 3er elemento a la variable num */ El lenguaje C no realiza comprobaci´n de contornos en los arreglos. La direcci´n m´s baja corresponde al primer elemento y la m´s o a a alta al ultimo. el ejemplo anterior declara un arreglo de enteros con diez elementos desde listanum[0] hasta listanum[9].Cap´ ıtulo 5 Arreglos y cadenas En el siguiente cap´ ıtulo se presentan los arreglos y las cadenas. el formato general es: a o tipo nombre arr [ tam1 ][ tam2 ] . /* Asigna 15 al 3er elemento del arreglo listanum*/ num = listanum[2]. esto es. En C. Para acceder a un elemento en particular de un ´ arreglo se usa un ´ ındice. se puede referenciar el arreglo por encima de N sin provocar ning´n mensaje de n u error en tiempo de compilaci´n o ejecuci´n. Arreglos unidimensionales y multidimensionales Los arreglos son una colecci´n de variables del mismo tipo que se referencian utilizando un nombre com´n. a C permite arreglos con m´s de una dimensi´n . La forma como pueden ser accesados los elementos de un arreglo.. En el caso de que sobrepase el final durante o una operaci´n de asignaci´n.. incluso aunque probablemente se provoque el fallo del programa. [ tamN]. para declarar un arreglo de enteros llamado listanum con diez elementos se hace de la siguiente forma: int listanum[10]. 32 . todos los arreglos usan cero como ´ ındice para el primer elemento.1. Un o u arreglo consta de posiciones de memoria contigua. si se o o a o dimensiona un arreglo de tama˜o N. Por tanto. Como o o programador se es responsable de asegurar que todos los arreglos sean lo suficientemente grandes para guardar lo que pondr´ en ellos el programa. 5. entonces se asignar´n valores a otra variable o a un trozo del c´digo.

ı main() { int t.8. i<4.3.num[t][i]). [ tamN] = {lista-valores}.8. en donde la terminaci´n de la o cadena se debe indicar con nulo.6. } } En C se permite la inicializaci´n de arreglos.10. t. c Por ejemplo.3.2.CAP´ ITULO 5. debiendo seguir el siguiente formato: o tipo nombre arr[ tam1 ][ tam2 ] . en C. Por ejemplo: int i[10] = {1. ++i) num[t][i]=(t*4)+i*1. t<3.7.7. si se a a quiere declarar un arreglo cadena que guarde una cadena de diez caracteres.. de la siguiente forma: char nombre arr[ tam ]=¸adena”. tabladenums[2][3] = 15.5. Un nulo se especifica como ’\0’.6. i<4. . cuando se declare un arreglo de caracteres se debe considerar un car´cter adicional a la cadena m´s larga que se vaya a guardar. Se pueden hacer tambi´n inicializaciones de arreglos de caracteres en donde autom´ticamente C asigna el caracter e a nulo al final de la cadena. 5. for(t=0.10}.i. Por lo anterior.i.4. o e Para acceder los elementos se procede de forma similar al ejemplo del arreglo unidimensional. Cadenas A diferencia de otros lenguajes de programaci´n que emplean un tipo denominado cadena string para manipular o un conjunto de simbolos.1.11}. printf("\n"). /* Asigna 15 al elemento de la 3 a fila y la 4a columna*/ num = tabladenums[25][16]. 33 A continuaci´n se muestra un ejemplo que asigna al primer elemento de un arreglo bidimensional cero.9. al siguiente o 1.2. ARREGLOS Y CADENAS Observar que para declarar cada dimensi´n lleva sus propios par´ntesis cuadrados.5. int num[3][4]={0. y as´ sucesivamente. Por ejemplo. ++t) for(i=0. esto es. se har´ como: a char cadena[11].2.num[3][4].4. t<3. ++i) printf("num[%d][%d]=%d ". el siguiente fragmento inicializa cadena con “hola”: char cadena[5]="hola".9. ++t) { for(i=0. for(t=0.. se debe simular mediante un arreglo de caracteres.

printf("Introduce tus apellidos: "). completo[80]. por lo que lo siguiente no es v´lido: a main() { char nombre[40]. y tambi´n escribir un programa que revise el arreglo para encontrar un valor en e particular.CAP´ ITULO 5. ch>=0 ) /* ch < 0 indica fin-de-datos */ o char ch.nombre). como se hace con enteros o flotantes. El programa podr´ probarse tecleando progrev | progrev para ver si una copia exacta de la entrada original ıa es recreada. Leer un texto. while( ch=getchar().’l’. de igual forma para mostrarlo en la salida est´ndar. char). apellidos[30].’o’. /* Ilegal */ } 5.apellidos). se puede usar un ciclo como el siguiente char ch.nombre.3. flotante.apellidos)."+nombre+appellidos. 2. 34 Para asignar la entrada est´ndar a una cadena se puede usar la funci´n scanf con la opci´n %s (observar que no a o o se requiere usar el operador &). } El lenguaje C no maneja cadenas de caracteres. printf("Introduce tu nombre: "). Leer hasta que se encuentre un final-de-datos (teclar CONTROL-D para generarlo). ARREGLOS Y CADENAS El c´digo anterior es equivalente a: o char cadena[5]={’h’. Ejercicios 1. scanf("%s". Para leer caracteres hasta el final de datos. &ch ) == 1 ) /* se lee un caracter */ . un caracter a la vez desde la entrada est´ndar (que es el teclado). a Por ejemplo: main() { char nombre[15]. e ı /* Ilegal */ apellidos="Morelos y Pav´n". nombre="Jos´ Mar´a". while( scanf( "%c". Escribir un programa que lea un arreglo de cualquier tipo (entero. apellidos[40]. scanf("%s". o /* Ilegal */ completo="Gral. printf("Usted es %s %s\n".’a’. se podr´ pedir al usuario ıa que indique el tipo de arreglo. e imprimir cada l´ a ınea en forma invertida.’\0’}.

.. esto es.. el total de longitud 2 y u as´ sucesivamente.. .CAP´ ITULO 5.. Una salida t´ ıpica podr´ ser como esta: ıa longitud longitud longitud longitud longitud 1 : 16 ocurrencias 2 : 20 ocurrencias 3 : 5 ocurrencias 4 : 2 ocurrencias 5 : 0 ocurrencias .. y mostrar una estad´ ıstica de las longitudes de las palabras.. Se deber´n permitir palabras hasta de una e a longitud de 25 letras. ARREGLOS Y CADENAS 35 3. Escribir un programa para leer un texto hasta el fin-de-datos. el n´mero total de palabras de longitud 1 que hayan ocurrido. ı Define una palabra como una secuencia de caracteres alfab´ticos.

El formato general de una funci´n en C es o especificador de tipo nombre de funci´n( lista de par´metros ) o a { variables locales c´digo de la funci´n o o } o a El especificador de tipo indica el tipo del valor que la funci´n devolver´ mediante el uso de return. expresiones y sentencias que realizan una tarea eso pec´ ıfica. la lista de par´metros a a o a puede estar vac´ ıa. Si no se espec´ a ıfica un valor. definiciones. b=10. Las funciones terminan y regresan autom´ticamente al procedimiento que las llam´ cuando se encuentra la ultima a o ´ llave }. se puede forzar el regreso antes usando la sentencia return. return(promedio). promedio = (num1 + num2) / 2. b). Ademas del uso se˜alado la funci´n return n o se usa para devolver un valor. Se examina a continuaci´n un ejemplo que encuentra el promedio de dos enteros: o float encontprom(int num1. int num2) { float promedio. float resultado. o bien. resultado = encontprom(a.Cap´ ıtulo 6 Funciones Una funci´n es un conjunto de declaraciones. El valor puede ser de cualquier tipo v´lido. } main() { int a=7. No se tienen siempre que incluir par´metros en una funci´n. 36 . entonces la computadora asume por defecto que la funci´n o devolver´ un resultado entero.0.

contador<10. y por otra parte tampoco se emplea la sentencia o u a return para regresar de la funci´n. } . } 37 6. FUNCIONES printf("Promedio=%f\n".2. lo que en otros lenguajes se conocen como procedimientos (por ejemplo.1.s[t]). u void cuadrados() { int contador. contador++) printf("%d\n". Funciones y arreglos Cuando se usan un arreglo como un argumento a la funci´n.contador*contador). Se muestra un ejemplo que imprime los cuadrados de ciertos n´meros. for( contador=1. o a o void imp_rev(char s[]) { int t. for( t=strlen(s)-1. Para fines pr´cticos podemos considerar el nombre del arreglo sin ning´n ´ a u ındice como la direcci´n del o arreglo.CAP´ ITULO 6. } main() { cuadrados(). } main() { char nombre[]="Facultad".resultado). se pasa s´lo la direcci´n del arreglo y no la copia del o o o arreglo entero. Funciones void Las funciones void dan una forma de emular. Se usan cuando no requiere regresar un valor. t>=0. o 6. } En la funci´n cuadrados no esta definido ning´n par´metro. en PASCAL). t--) printf("%c". imp_rev(nombre). Considerar el siguiente ejemplo en donde se pasa un arreglo a la funci´n imp_rev. observar que no es necesario o especificar la dimensi´n del arreglo cuando es un par´metro de la funci´n.

for ( i=0. o o ni para regresar alg´n valor.y.tabla[x][y]). B´sicamente si una funci´n ha sido definida antes o a o de que sea usada (o llamada).0. a o El est´ndar ANSI de C introduj´ una nueva (mejor) forma de hacer lo anterior respecto a las versiones previas a o de C. float tabla[][5]) { int x. u Se muestra otro ejemplo. y<tamy. 15. printf("El promedio de la lista es %f\n". return(suma/tam). Lo anterior es hecho. Por otra parte.numeros) ).x. el resto de las dimensiones deben se˜alarse. dependiendo del alcance de la funci´n. Prototipos de funciones Antes de usar una funci´n C debe tener conocimiento acerca del tipo de dato que regresar´ y el tipo de los o a par´metros que la funci´n espera. La importancia de usar prototipos de funciones es la siguiente: Se hace el c´digo m´s estructurado y por lo tanto. 8.0. for ( x=0. FUNCIONES 38 Observar que en la funci´n imp_rev se usa la funci´n strlen para calcular la longitud de la cadena sin incluir el o o terminador nulo.9}.y. } Para el caso de que se tenga que pasar un arreglo con m´s de una dimensi´n.int tamy.3. enconprom(7. float enconprom(int tam.2. o . m´s f´cil de leer.0. y++ ) printf("t[%d][%d]=%f".0. i<tam. i++) suma += lista[i]. o a a a Se permite al compilador de C revisar la sintaxis de las funciones llamadas.01.3. float suma = 0. float lista[]) { int i. printf("\n"). no se indica la primera dimensi´n a o o pero. x++ ) { for ( y=0. 44. la funci´n imp_rev no usa la sentencia return ni para terminar de usar la funci´n. } } 6. -2. entonces se puede usar la funci´n sin problemas. -3. x<tamx. Se muestra a continuaci´n un ejemplo: n o void imprtabla(int tamx.CAP´ ITULO 6. } main() { float numeros[]={2. 20.

Por ejemplo: o u char cadena[] = "El gato negro".CAP´ ITULO 6. . Escribir un programa que lea una l´ ınea de texto en un buffer (una cadena de caracteres) usando la funci´n o gets y calcule la longitud de la l´ ınea (NO usar la funci´n strlen). Escribir una funci´n “reemplaza”. Lo anterior declara una funci´n llamada longcad que regresa un valor entero y acepta una cadena como par´metro. o ´ o Es una pr´ctica usual y conveniente escribir el prototipo de todas las funciones al principio del programa. le reemplaza todos los espacios o a de la cadena por un gui´n bajo. Por ejemplo: o int longcad(char []). el nombre de la o a a o funci´n y entre par´ntesis la lista del tipo de los par´metros de acuerdo al orden que aparecen en la definici´n de la o e a o funci´n. La declaraci´n simplemente maneja el tipo de dato que la ı. o 3. deber´ devolver: a cadena convertida "El_gato_negro" n = 2 2. o a 6. FUNCIONES 39 Si no es as´ entonces la funci´n se debe declarar. Ejercicios 1. Modificar el programa anterior para que lea un archivo de texto. Para declarar un prototipo de una funci´n se indicar´ el tipo de dato que regresar´ la funci´n. El archivo deber´ redireccionarse al programa. o o o funci´n regresa y el tipo de par ametros usados por la funci´n. En caso de que se lea una l´ ınea con longitud 0 deber´ terminar el a programa.4. sin a embargo esto no es estrictamente necesario. a debiendo mostrar el contenido del mismo. n = reemplaza( cadena ). y devuelve el n´mero de espacios reemplazados. la cual toma una cadena como par´metro.

char estado[3]. Con el trozo de c´digo anterior no ha sido declarada ninguna variable. Las variables que forman la estructura son llamados elementos estructurados. char ciudad[20]. char calle[40]. unsigned int codigo. Mediante la palabra clave struct se le indica al compilador que defina una plantilla de estructura. char estado[3]. 40 . a o se puede representar una lista de nombres de correo en una estructura.Cap´ ıtulo 7 M´s tipos de datos a En este cap´ ıtulo se revisa la forma como pueden ser creados y usados en C tipos de datos m´s complejos y a estructuras. o o Para declarar una variable. Generalmente. char calle[40]. unsigned int codigo. todos los elementos en la estructura est´n relacionados l´gicamente unos con otros. char ciudad[20]. 7. Una estructura o proporciona un medio conveniente para mantener junta informaci´n que se relaciona.1. Se pueden declarar una o m´s variables cuando se define una estructura entre ) y . tan s´lo se ha definido el formato. Estructuras En C una estructura es una colecci´n de variables que se referencian bajo el mismo nombre. Una definici´n de estructura o o forma una plantilla que se puede usar para crear variables de estructura. Por ejemplo. Por ejemplo: a struct direc { char nombre[30].. struct direc { char nombre[30]. }. se har´ como sigue: a struct direc info_direc.

cinfo. C proporciona el operador punto . usando algunos de los tipos de datos de C. 7. 41 Para referenciar o accesar un miembro (o campo) de una estructura. } info_direc. unsigned int codigo.."MMX". por ejemplo.1."Dorado". Se puede usar typedef para crear nombres para tipos m´s complejos.12345}. Como en este ultima declaraci´n se indican las variables con esta estructura. donde su formato es: typedef <tipo><nombre>. en este caso direc sirve como una etiqueta a la estructura y es opcional. Con C tambi´n se pueden tener arreglos de estructuras: e typedef struct direc { char nombre[30].1) que typedef se puede usar para definir nuevos nombres de datos explic´ n o ıtamente.codigo=54321. por ejemplo: a typedef struct direc { char nombre[30]. unsigned int codigo.4."Dorado". como una estructura. la cual es una estructura."Fantasia 2000". en donde sdirec es el nuevo tipo de datos e info_direc es una variable del tipo sdirec. Las estructuras pueden ser tambi´n preinicializadas en la declaraci´n: e o struct direc info_direc={"Vicente Fernandez". por lo anterior. sdirec info_direc={"Vicente Fernandez".12345}. observar que direc es una etiqueta para la estructura que sirve como una forma breve para futuras declaraciones. se puede ´ o omitir el nombre de la estructura tipo."MMX". char estado[3]."Fantasia 2000". para asignar a info_direc otro c´digo. Definici´n de nuevos tipos de datos o Se se˜alo previamente (secci´n 2. Lo anterior podr´ ser accesado de ıa la siguiente forma: . char ciudad[20]. char calle[40]. MAS TIPOS DE DATOS } info_direc. artistas tiene 1000 elementos del tipo info_direc. por lo que la etiqueta no tiene mucho uso. } sdirec. info_direc artistas[1000]. binfo. char ciudad[20].1.´ CAP´ ITULO 7. lo hacemos como: o info_direc. ya que ha sido definido un nuevo tipo de dato. char estado[3]. char calle[40].

typedef union { jet jetu. typedef struct { int maxcarga. helicoptero helicopterou. con lo anterior se define una uni´n llamada numero y una instancia de esta llamada unumero. o Cuando el compilador de C esta reservando memoria para las uniones. } jet. 42 7.´ CAP´ ITULO 7. double floatnumero. } helicoptero. es com´n tener una estructura (con una uni´n anidada) y una variable que indica el tipo de uni´n. MAS TIPOS DE DATOS artistas[50]. numero es o la etiqueta de la uni´n y tiene el mismo comportamiento que la etiqueta en la estructura. typedef struct { int capac_elev. ıa n C emplea la sentencia union para crear uniones por ejemplo: union numero { short shortnumero. . } transporteaereo. } unumero. long longnumero. } avioncarga. Uniones Una union es una variable la cual podr´ guardar (en momentos diferentes) objetos de diferentes tama˜os y tipos. con la llamada a la funci´n se muestra el valor de longnumero. siempre crear´ una variable lo suficientea mente grande para que quepa el tipo de variable m´s largo de la uni´n. avioncarga avioncargau. o Los miembros pueden ser accesados de la siguiente forma: printf("%ld\n".codigo=22222. a o Con la finalidad de que el programa pueda llevar el registro del tipo de la variable uni´n usada en un momento o dado. u o o Se muestra un ejemplo de lo anterior: typedef struct { int maxpasajeros.longnumero).unumero.2.

char letra = ’A’. un helic´ptero o un o e o avion de carga. Como se observa C convierte el valor del lado derecho de la asignaci´n o al tipo del lado izquierdo. conversi´n de tipos. transporteaereo descripcion. esto es. numeroentero = (int) numeroflotante.´ CAP´ ITULO 7. 43 En la estructura un_transporeaereo hay un miembro para el tipo. int velocidad. Para llevar a cabo lo anterior se usa el operador de conversi´n (cast) (). Conversi´n de tipos (casts) o C es uno de los pocos lenguajes que permiten la conversi´n de tipos. e 7. o Otro uso es asegurarse que la divisi´n de n´meros se comporta como se requiere. con lo cual se asigna 9 a la variable numeroentero y la fracci´n es desechada. Lo anterior se presenta cuando variables de un tipo se mezclan con las de otro tipo. o a u . ´ asigna 65 (que es el c´digo ASCII de A’) a numeroentero. } un_transporteaereo en el ejemplo se define una uni´n base de transporte a´reo el cual puede ser un jet.0 a numeroflotante. por lo tanto: o e int numeroentero.87. numeroentero = (int) letra. forzar una variable de un tipo a ser o de otro tipo. Por ejemplo: o int numeroentero. ya que si se tienen dos enteros o u la forma de forzar el resultado a un n´mero flotante es: u numeroflotante = (float) numerent / (float) denoment. MAS TIPOS DE DATOS typedef struct { int tipo. float numeroflotante = 9.3. La conversi´n de tipos puede ser tambi´n usada con cualquier tipo simple de datos incluyendo char. float numeroflotante. Una buena regla es la siguiente: En caso de duda. asigna 10. numeroflotante = (float) numeroentero. que indica cual es la estructura manejada en ´se momento. o Algunas conversiones de tipos son hechas autom´ticamente – esto es principalmente por la caracter´ a ıstica de compatibilidad de tipos. con lo que se asegura que la divisi´n devolver´ un n´mero flotante. o El siguiente c´digo: o int numeroentero = 10.

lo siguiente asigna el valor 250 a cd enum disco { diskette. Variables est´ticas a auto extern static register C soporta cuatro especificadores de clase de almacenamiento. cinta) muestra 1 4 en la pantalla. o enum almacenamiento { diskette. usando la o palabra clave enum para el comienzo de un tipo de enumeraci´n. La primera se usa para declarar las variables de su tipo. Enumeraciones Una enumeraci´n es un conjunto de constantes enteras con nombre y especifica todos los valores legales que puede o tener una variable del tipo enum. Con la siguiente definici´n y declaraci´n son v´lidas las siguientes sentencias: o o a disco = cd. cd=250. Son Estos especifcadores le dicen al compilador como almacenar la variable que sigue. cinta }. el valor del segundo s´ o ımbolo a 1 y as´ sucesivamente. MAS TIPOS DE DATOS 44 7. El especificador de almacenamiento precede a la declaraci´n de variable que tiene el formato general: o .5. a ı menos que se inicialice de otra manera. Por lo tanto los valores de los s´ ımbolos son los siguientes: diskette duro cd dvd cinta 0 1 250 251 252 7. Para hacerlo. if ( disco == diskette ) printf("Es de 1440 Kb\n"). dvd. Por tanto. Es opcional nombre enum y lista de variables. dd.´ CAP´ ITULO 7. Su formato es: o o enum nombre enum { lista de enumeraci´n } lista de variables. El siguiente fragmento define una enumeraci´n llamada disco que declara almacenamiento para ser de ese tipo.4. cinta }. Se puede especificar el valor de uno o m´s s´ a ımbolos usando un inicializador. La forma como se define una enumeraci´n es de forma parecida a como se hace con las estructuras. dd. poner un signo igual y un valor entero despu´s del s´ e ımbolo. Por ejemplo. enum almacenamiento disco. cd. dvd. duro. Se inicializa el primer s´ ımbolo de enumeraci´n a cero. printf("%d %d\n".

for (i=0. Si lo anterior no se pudiera hacer. Las variables static son variables permanentes en su propia funci´n o archivo. ya que C les crea un almacenamiento permanente. La variable s_var es o creada en la primera llamada y despu´s recuerda su valor. static = %d \n". es decir. ++s_var. . i<5. Una variable est´tica local es una variable local que retienen su valor entre llamadas de a funci´n. pero mantienen sus valores entre llamadas. a u 2. static static static static static = = = = = 0 1 2 3 4 Como se puede observar la variable a_var es creada cada vez que se llama a la funci´n. El mes se ingresar´ desde el teclado dando un n´mero entre 1 y 12. s_var). entonces se tendr´ que o ıan usar variables globales –que abrir´ la puerta a posibles efectos laterales–. ++a_var. raramente se usa porque las variables locales son auto por defecto. printf("auto = %d. no se destruye cuando termina la funci´n. su predecesor y su sucesor. static int s_var = 0. Escribir un programa que use el tipo enumerate para mostrar el nombre de un mes. 45 Se usa el especificador auto para declarar variables locales. } La salida del c´digo anterior es: o auto auto auto auto auto = = = = = 0. e o 7. Escribir un programa que contenga una funci´n que pueda recibir dos estructuras que contienen las coordenadas o en el plano de dos puntos dados. 0. ´ Variables static locales. Sin embargo. de los cuales se desea conocer su punto medio. /* Prototipo de la funcion */ main() { int i.6. a_var. } void stat() { auto int a_var = 0. Se diferencian de las variables o globales porque son desconocidas fuera de sus funciones o archivo. Un ejemplo de una funci´n que requerir´ ıa o ıa tales variables es un generador de series de n´meros que produce un n´mero nuevo basado en el ultimo. u u ´ A continuaci´n se muestra un ejemplo en donde se analiza el comportamiento de una variable auto y una variable o static void stat(). Ejercicios 1. 0. Esta caracter´ ıstica las hace utiles cuando se escriben funciones generales y bibliotecas de funciones. MAS TIPOS DE DATOS especif almac especif tipo lista variables. 0. 0.´ CAP´ ITULO 7. ++i) stat().

Para tener una mejor idea. a C usa los apuntadores en forma extensiva. Se pueden tener apuntadores o a cualquier tipo de variable.1. a o El operador de indirecci´n o dereferencia * devuelve el “contenido de un objeto apuntado por un apuntador”. o Es una herramienta muy poderosa. Estructuras y Funciones 8. o Para declarar un apuntador para una variable entera hacer: int *apuntador. ¿Porqu´? e Es la unica forma de expresar algunos c´lculos. considerar el siguiente c´digo: o 46 . Se debe asociar a cada apuntador un tipo particular.Cap´ ıtulo 8 Apuntadores Los apuntadores son una parte fundamental de C. no se puede asignar la direcci´n de un short int o a un long int. C usa apuntadores expl´ ıcitamente con: Arreglos. ´ a Se genera c´digo compacto y eficiente. ´ a Se genera c´digo compacto y eficiente. El operador unario o mon´dico & devuelve la direcci´n de memoria de una variable. C usa apuntadores expl´ ıcitamente con: Es la unica forma de expresar algunos c´lculos. Definici´n de un apuntador o Un apuntador es una variable que contiene la direcci´n en memoria de otra variable. o Es una herramienta muy poderosa. Por ejemplo. Si usted no puede usar los apuntadores apropiadamente entonces esta perdiendo la potencia y la flexibilidad que C ofrece b´sicamente. El secreto para C esta en el uso de apuntadores.

CAP´ ITULO 8. APUNTADORES main() { int x = 1, y = 2; int *ap; ap = &x; y = *ap; x = ap; *ap = 3; }

47

Cuando se compile el c´digo se mostrar´ el siguiente mensaje: o a warning: assignment makes integer from pointer without a cast. Con el objetivo de entender el comportamiento del c´digo supongamos que la variable x esta en la localidad de o la memoria 100, y en 200 y ap en 1000. Nota: un apuntador es una variable, por lo tanto, sus valores necesitan ser guardados en alg´n lado. u int x = 1, y = 2; int *ap; ap = &x; 100 1 200 2 1000 ap 100

x

y

Las variables x e y son declaradas e inicializadas con 1 y 2 respectivamente, ap es declarado como un apuntador a entero y se le asigna la direcci´n de x (&x). Por lo que ap se carga con el valor 100. o

y = *ap;

x

100 1

y

200 1

1000 ap 100

Despu´s y obtiene el contenido de ap. En el ejemplo ap apunta a la localidad de memoria 100 — la localidad de e x. Por lo tanto, y obtiene el valor de x — el cual es 1.

x = ap;

x

100 100

y

200 1

1000 ap 100

Como se ha visto C no es muy estricto en la asignaci´n de valores de diferente tipo (apuntador a entero). As´ que o ı es perfectamente legal (aunque el compilador genera un aviso de cuidado) asigna el valor actual de ap a la variable x. El valor de ap en ese momento es 100.

CAP´ ITULO 8. APUNTADORES

48

*ap = 3; 100 3 200 1 1000 ap 100

x

y

Finalmente se asigna un valor al contenido de un apuntador (*ap). Importante: Cuando un apuntador es declarado apunta a alg´n lado. Se debe inicializar el apuntador antes de u usarlo. Por lo que: main() { int *ap; *ap = 100; } puede generar un error en tiempo de ejecuci´n o presentar un comportamiento err´tico. o a El uso correcto ser´: a main() { int *ap; int x; ap = &x; *ap = 100; } Con los apuntadores se puede realizar tambi´n aritm´tica entera, por ejemplo: e e main() { float *flp, *flq; *flp = *flp + 10; ++*flp; (*flp)++; flq = flp; } NOTA: Un apuntador a cualquier tipo de variables es una direcci´n en memoria — la cual es una direcci´n entera, o o pero un apuntador NO es un entero. La raz´n por la cual se asocia un apuntador a un tipo de dato, es por que se debe conocer en cuantos bytes esta o guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador por un “bloque” de memoria, en donde el bloque esta en funci´n del tama˜o del dato. o n Por lo tanto para un apuntador a un char, se agrega un byte a la direcci´n y para un apuntador a entero o a o flotante se agregan 4 bytes. De esta forma si a un apuntador a flotante se le suman 2, el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes.

CAP´ ITULO 8. APUNTADORES

49

8.2.

Apuntadores y Funciones

Cuando C pasa argumentos a funciones, los pasa por valor, es decir, si el par´metro es modificado dentro de la a funci´n, una vez que termina la funci´n el valor pasado de la variable permanece inalterado. o o Hay muchos casos que se quiere alterar el argumento pasado a la funci´n y recibir el nuevo valor una vez que la o funci´n ha terminado. Para hacer lo anterior se debe usar una llamada por referencia, en C se puede simular pasando o un puntero al argumento. Con esto se provoca que la computadora pase la direcci´n del argumento a la funci´n. o o Para entender mejor lo anterior consideremos la funci´n swap() que intercambia el valor de dos argumentos o enteros: void swap(int *px, int *py); main() { int x, y; x = 10; y = 20; printf("x=%d\ty=%d\n",x,y); swap(&x, &y); printf("x=%d\ty=%d\n",x,y); } void swap(int *px, int *py) { int temp; temp = *px; *px = *py; *py = temp; } /* guarda el valor de la direccion x */ /* pone y en x */ /* pone x en y */

8.3.

Apuntadores y arreglos

Existe una relaci´n estrecha entre los punteros y los arreglos. En C, un nombre de un arreglo es un ´ o ındice a la direcci´n de comienzo del arreglo. En esencia, el nombre de un arreglo es un puntero al arreglo. Considerar lo o siguiente: int a[10], x; int *ap; ap = &a[0]; x = *ap; /* ap apunta a la direccion de a[0] */ /* A x se le asigna el contenido de ap (a[0] en este caso) */ /* Se asigna al segundo elemento de ’a’ el valor 100 usando ap*/

*(ap + 1) = 100;

Como se puede observar en el ejemplo la sentencia a[t] es id´ntica a ap+t. Se debe tener cuidado ya que C no e hace una revisi´n de los l´ o ımites del arreglo, por lo que se puede ir f´cilmente m´s alla del arreglo en memoria y a a sobreescribir otras cosas.

Se muestra o o a enseguida la versi´n de esta funci´n que podr´ escribirse: o o ıa int strlen(char *s) { char *p = s. o ya que char s[] es igual que char *s. La funci´n strlen() es una funci´n de la biblioteca est´ndar que regresa la longitud de una cadena. char *t) { while ( (*s++ = *t++) != ’\0’ ). Cuando un arreglo es pasado a una funci´n lo que en realidad se le esta pasando es la localidad de su elemento inicial en memoria. o Por lo tanto: strlen(s) es equivalente a strlen(&s[0]) Esta es la raz´n por la cual se declara la funci´n como: o o int strlen(char s[]). a o void strcpy(char *s. u Con lo comentado se puede entender como los arreglos son pasados a las funciones. } Se muestra enseguida una funci´n para copiar una cadena en otra. Hacer a = ap y a++ ES ILEGAL. Un arreglo NO ES una variable. &a[i] es equivalente con e a+i. Se puede hacer ap = a y ap++. en vez de ap = &a[0]. el direccionamiento de apuntadores se puede expresar como: a[i] que es equivalente a *(ap + i) Sin embargo los apuntadores y los arreglos son diferentes: Un apuntador es una variable. aseg´rese haberla entendido. Este parte es muy importante.CAP´ ITULO 8. APUNTADORES 50 C sin embargo es mucho m´s s´til en su relaci´n entre arreglos y apuntadores. } En los dos ultimos ejemplos se emplean apuntadores y asignaci´n por valor. y una declaraci´n equivalente es int strlen(char *s). return p . y tambi´n *(a + i) en vez de a[i].s. Por ejemplo se puede teclear a u o solamente: ap = a. Nota: Se emplea el uso del caracter ´ o nulo con la sentencia while para encontrar el fin de la cadena. esto es. . while ( *p != ’\0’ ) p++. Y como se ve en el ejemplo. Al igual que en el ejercicio anterior existe en o la biblioteca est´ndar una funci´n que hace lo mismo.

. Guardar los apuntadores en un arreglo diferente donde cada apuntador apunta al primer caracter de cada l´ ınea. C requiere conocer cuantas son las columnas para que o pueda brincar de rengl´n en rengl´n en la memoria. la notaci´n o a[n][m] nos indica que los elementos del arreglo est´n guardados rengl´n por rengl´n. \nDEF . a ABC . 3.5.4.. A continuaci´n se muestra un ejemplo de su uso: ordenar las l´ o ıneas de un texto de diferente longitud. La raz´n de lo anterior.. Ver figura 8.1. Arreglos multidimensionales y apuntadores Un arreglo multidimensional puede ser visto en varias formas en C.CAP´ ITULO 8.. o a 4. a o o Cuando se pasa una arreglo bidimensional a una funci´n se debe especificar el n´mero de columnas — el n´mero o u u de renglones es irrelevante. Con lo anterior se elimina: el manejo complicado del almacenamiento. Si dos l´ ıneas est´n desacomodadas — intercambiar (swap) los apuntadores (no el texto).. es nuevamente los apuntadores. \n . Comparar dos l´ ıneas usando la funci´n de la biblioteca est´ndar strcmp(). se puede declarar el argumento de la funci´n como: o o . Por lo tanto. APUNTADORES 51 8.... p[0] p[1] p[2] p 0 1 2 ABC DEF CAT p 0 1 2 ABC DEF CAT Figura 8.1: Arreglos de apuntadores (Ejemplo de ordenamiento de cadenas). 2. Guardar todas las l´ ıneas en un arreglo de tipo char grande. por ejemplo: Un arreglo de dos dimensiones es un arreglo de una dimensi´n. Los arreglos de apuntadores son una representaci´n de datos que manejan de una forma eficiente y conveniente o l´ ıneas de texto de longitud variable. 8. alta sobrecarga por el movimiento de l´ ıneas. ¿C´mo se puede hacer lo anterior? o 1. donde cada uno de los elementos es en s´ mismo o ı un arreglo... \n CAT . Arreglos de apuntadores En C se pueden tener arreglos de apuntadores ya que los apuntadores son variables. o o Considerando que una funci´n deba recibir int a[5][35]... Observando que \n marca el fin de cada l´ ınea..

..anomb es un arreglo verdadero de 200 elementos de dos dimensiones tipo char.CAP´ ITULO 8. a Sin embargo: . "Feb". }. nos estaremos refiriendo a la direcci´n del primer elemento que se encuentran en el tercer o rengl´n de la matriz supuesta. En donde es v´lido hacer nomb[3][4] y anomb[3][4] en C.. . . "Ene". Lo cual gr´ficamente se muestra en la figura 8.. "Mar". NOTA: si cada apuntador en nomb indica un arreglo de 20 elementos entonces y solamente entonces 200 chars estar´n disponibles (10 elementos). declara un arreglo de 35 apuntadores a enteros. }. .. a Con el primer tipo de declaraci´n se tiene la ventaja de que cada apuntador puede apuntar a arreglos de diferente o longitud. "Mar". Se puede indicar que se hace un manejo m´s eficiente del espacio a a haciendo uso de un arreglo de apuntadores y usando un arreglo bidimensional. APUNTADORES f( int a[][35] ) { .El acceso de los elementos anomb en memoria se hace bajo la siguiente f´rmula 20*renglon + columna o + direcci´n base o . } o a´n u f( int (*a)[35] ) { . char anomb[][15] = { "No mes".. 52 Ahora veamos la diferencia (sutil) entre apuntadores y arreglos. "Ene". char anomb[10][20].. Considerar: char *nomb[] = { "No mes". Considera: char *nomb[10].En cambio nomb tiene 10 apuntadores a elementos.. ´ e a Por lo tanto: int (*a)[35]. mientras que o int *a[35].. El manejo de cadenas es una aplicaci´n com´n o u de esto..2. declara un apuntador a un arreglo de 35 enteros. y por ejemplo si hacemos la siguiente referencia a+2. } En el ultimo ejemplo se requieren los par´nteis (*a) ya que [ ] tiene una precedencia m´s alta que *.... "Feb"..

. ap_punto->x++. ap_punto->y+=2. mientras el c´digo se esta ejecutando.y. }.2: Arreglo de 2 dimensiones VS. "Ene". APUNTADORES anomb No mes\0 Ene\0 Feb\0 13 Mar\0 15 elementos 53 nomb 0 1 2 No mes\0 Ene\0 Feb\0 Figura 8. . } punto.x = punto..7. por ejemplo: o o a func_cualquiera() { static char *nomb[] = { "No mes". } Recordando que con el especificador de almacenamiento de clase static se reserva en forma permanente memoria el arreglo. 8. ap_punto = &punto.6. ap_punto->z=3. Inicializaci´n est´tica de arreglos de apuntadores o a La inicializaci´n de arreglos de apuntadores es una aplicaci´n ideal para un arreglo est´tico interno. /* Se asigna punto al apuntador */ /* Con el operador -> se accesan los miembros */ /* de la estructura apuntados por ap_punto */ . o 8. "Feb". Apuntadores y estructuras Los apuntadores a estructuras se definen f´cilmente y en una forma directa.z..z = 1.y = punto.CAP´ ITULO 8. "Mar". arreglo de apuntadores. Considerar lo siguiente: a main() { struct COORD { float x. punto. struct COORD *ap_punto.

No se puede tener un elemento del tipo variable ya que esto generar´ una definici´n recursiva la cual no esta permitida. Se permite poner una referencia ıa o a un apuntador ya que los los bytes se dejan de lado para cualquier apuntador.3: Esquema de una lista ligada con 2 elementos. Nota: Solamente se puede declarar sig como un apuntador tipo ELEMENTO. } ELEMENTO. char *malloc() — una funci´n de la biblioteca est´ndar que se ver´ m´s adelante. n1. la cual regresa un apuntador al bloque de memoria requerida si se pudo o un apuntador o a nulo en otro caso. *x = 100.8. y.3 o valor *sig valor *sig 54 n1 n2 Figura 8. n2. o No asignar un apuntador a una direcci´n de memoria antes de usarlo o int *x *x = 100. struct ELEMENTO *sig. lo adecuado ser´. digamos int y. La asignaci´n que se hace corresponde a la figura 8. tener primeramente una localidad f´ a ısica de memoria. o a a a Supongamos que se tiene un apuntador char *p Considerar: . Indirecci´n no v´lida o a Supongamos que se tiene una funci´n llamada malloc() la cual trata de asignar memoria din´micamente (en o a tiempo de ejecuci´n).CAP´ ITULO 8. Fallas comunes con apuntadores A continuaci´n se muestran dos errores comunes que se hacen con los apuntadores. APUNTADORES } Otro ejemplo son las listas ligadas: typedef struct { int valor.sig = &n2. int *x. 8. x = &y. ELEMENTO n1.

una cadena corta) u ocurre en una sentencia (una cadena larga). o El c´digo correcto deber´ ser: o a p = (char *) malloc(100). en la segunda l´ ınea se tiene un texto general. el gato y el canario" La palabra ocurrio 3 veces. /* pide 100 bytes de la memoria */ Existe un error en el c´digo anterior. Considerar o u que el n´mero tiene el siguiente formato 99999999. APUNTADORES 55 *p = (char *) malloc(100): *p = ’y’. u o 2. 8. La primera l´ a ınea es una sola palabra. donde cada l´ ıdo a ınea tiene diferente longitud.999999. Leer los datos de la entrada est´ndar.9. entonces p es nulo. La salida t´ ıpica podr´ ser: ıa La palabra es "el" La sentencia es "el perro. Escribir una funci´n que convierta una cadena s a un n´mero de punto flotante usando apuntadores. es decir.CAP´ ITULO 8. Escribir el programa que ordena las l´ ıneas de un texto le´ desde la entrada est´ndar. Ejercicios 1. exit(1). por lo que el c´digo anterior puede ser reescrito como: o p = (char *) malloc(100): /* pide 100 bytes de la memoria */ if ( p == NULL ) { printf("Error: fuera de memoria\n"). no se dar´ en notaci´n cient´ u a o ıfica. y por lo tanto no se podr´ hacer: a *p = ’y’. Escribir un programa que encuentre el n´mero de veces que una palabra dada (esto es. ¿Cu´l es? o a El * en la primera l´ ınea ya que malloc regresa un apuntador y *p no apunta a ninguna direcci´n. } *p = ’y’. Recordar que se debe insertar un caracter nulo antes de procesar. . seg´n lo descrito en la secci´n de arreglo de apuntadores. Leer ambas hasta encontrar un caracter de nueva l´ ınea. a a a u 3. La funci´n o deber´ suministr´rsele una cadena y deber´ devolver un n´mero. Un buen programa en C debe revisar lo anterior. Ahora si malloc no puede regresar un bloque de memoria.

C asume que el apuntador puede ser convertido a cualquier tipo. a 9. que permite convertir un apuntador void a un apuntador tipo char e int respectivamente. o Es usual usar la funci´n sizeof() para indicar el n´mero de bytes. Lo anterior indica que regresar´ un apuntador del tipo void *. cp = (char *) malloc(100). 56 .Cap´ ıtulo 9 Asignaci´n din´mica de memoria y o a Estructuras din´micas a La asignaci´n din´mica de memoria es una caracter´ o a ıstica de C. Le permite al usuario crear tipos de datos y estructuras de cualquier tama˜o de acuerdo a las necesidades que se tengan en el programa.h y es un tipo entero sin signo.1. ip = (int *) malloc(100 * sizeof(int) ). Uso de malloc. intenta obtener 100 bytes y asignarlos a la direcci´n de inicio a cp. n Se revisar´n dos de las aplicaciones m´s comunes: a a Arreglos din´micos a Estructuras din´micas de datos. Si no puede reservar esa cantidad de memoria la funci´n regresa un apuntador nulo o n o NULL Dado que void * es regresado. La forma de lograr la coerci´n (cast) es usando o o (char *) y (int *). El tipo de argumento size_t esta definido en la cabecera stddef. Por lo tanto: char *cp. Esta o u o definida como: void *malloc(size_t size). el cual es el inicio en memoria de la porci´n a o reservada de tama˜o size. sizeof y free La funci´n malloc es empleada com´nmente para intentar “tomar” una porci´n contigua de memoria. por ejemplo: o u int *ip. El compilador de C requiere hacer una conversi´n del tipo.

Los prototipos son dados a continuaci´n: o void *calloc(size_t nmemb. m´s conveniente que malloc. } Cuando se ha terminado de usar una porci´n de memoria siempre se deber´ liberar usando la funci´n free(). ASIGNACION DINAMICA DE MEMORIA Y ESTRUCTURAS DINAMICAS 57 Hacer la conversi´n al tipo de apuntador correcto asegura que la aritm´tica con el apuntador funcionar´ de forma o e a correcta. sizeof(struct COORD) y sizeof(PT) son tambien sentencias correctas. sizeof(int). o La funci´n sizeof() puede ser usada para encontrar el tama˜o de cualquier tipo de dato. Se debe observar tambi´n la diferencia de sintaxis entre calloc a e . for (i=0.2. sizeof(i). o a o Esta funci´n permite que la memoria liberada este disponible nuevemente quiz´s para otra llamada de la funci´n o a o malloc() La funci´n free() toma un apuntador como un argumento y libera la memoria a la cual el apuntador hace o referencia. Si se quiere inicializar la o memoria entonces se puede usar la funci´n calloc. — ya que de a u n esta forma el c´digo se hace independiente del dispositivo (portabilidad).ip++). Cuando se usa la funci´n malloc() la memoria no es inicializada (a cero) o borrada. variable o estructura. struct COORD {float x. calloc() y realloc().y. 9.z}. se pueden hacer cosas como: main() { int *ip. i<100. size_t size). Por ejemplo. en donde se emplea la relaci´n que existe entre o apuntadores y arreglos. ocasionalmente. ip = (int *) malloc(100 * sizeof(int) ). i. o n Simplemente se debe proporcionar uno de los anteriores como argumento a la funci´n. La funci´n calloc es computacionalmente un poco m´s cara o o a pero. struct COORD *pt. En el siguiente ejemplo se reserva memoria para la variable ip. void *realloc(void *ptr. Es una buena pr´ctica usar sizeof() a´n si se conoce el tama˜o actual del dato que se requiere. calloc y realloc Existen dos funciones adicionales para reservar memoria. ip[0] = 1000. o Por lo tanto: int i. para manejar la memoria reservada como un arreglo.´ ´ ´ CAP´ ITULO 9. size_t size). ++i) scanf("%d".

50*sizeof(int) ). se har´. ASIGNACION DINAMICA DE MEMORIA Y ESTRUCTURAS DINAMICAS 58 y malloc. La cual puede crecer en forma din´mica. struct ELEMENTO *sig. Si el bloque se hace m´s grande. Se muestra a continuaci´n el c´digo completo para manipular esta estructura: o o /* cola. La funci´n realloc intenta cambiar el tama˜o de un bloque de memoria previamente asignado. como dos u n argumentos individuales. El nuevo tama˜o o n n puede ser m´s grande o m´s peque˜o. entonces realloc intentar´ asignar un nuevo n a bloque de memoria y copiar´ el contenido anterior. free(liga). entonces el contenido anterior permanece a a n a sin cambios y la memoria es agregada al final del bloque. o Si para el ejemplo anterior. ya que calloc toma el n´mero de elementos deseados (nmemb) y el tama˜o del elemento (size).3. Por lo tanto. Programa de revisi´n o La cola es una colecci´n de ordenada de elementos de la que se pueden borrar elementos en un extremo (llamado o el frente de la cola) o insertarlos en el otro (llamado el final de la cola.´ ´ ´ CAP´ ITULO 9. este nuevo valor ser´ el que deber´ usarse. 9. sizeof(int) ). se quiere reasignar la memoria a 50 enteros en vez de 100 apuntados por ip.4. Si el tama˜o del bloque original no puede ser redimensionado. ip = (int *) calloc(100. /* Asigna memoria para liga */ /* libera la memoria asignada al apuntador liga usando free() */ 9. Listas ligadas Regresando al ejemplo del cap´ ıtulo anterior se defini´ la estructura: o typedef struct { int valor. a ip = (int *) realloc ( ip.h> #define FALSO 0 */ */ . liga = (ELEMENTO *) malloc( sizeof(ELEMENT) ). la funci´n devolver´ un nuevo apuntador (o de a o a valor diferente al anterior). Por lo tanto para asignar a 100 elementos enteros que est´n inicializados a cero se puede hacer: e int *ip. Si no puede ser reasignada nueva memoria la a a funci´n realloc devuelve NULL.c /* Demo de estructuras dinamicas en C #include <stdio. } ELEMENTO. Si el tama˜o se hace m´s peque˜o entonces el contenido n a n sobrante permanece sin cambios. a ELEMENTO *liga.

break. elemento_lista * AgregaDato (elemento_lista * apuntlista. elemento_lista * BorrarDato (elemento_lista * apuntlista). &dato). scanf ("%d". break. void LimpCola (elemento_lista * apuntlista). void Menu (int *opcion).´ ´ ´ CAP´ ITULO 9. opcion. dato). main () { elemento_lista listmember. void ImprCola (elemento_lista * apuntlista). case 2: if (apuntlista == NULL) printf ("<Cola vacia!\n"). ASIGNACION DINAMICA DE MEMORIA Y ESTRUCTURAS DINAMICAS 59 typedef struct nodo { int dato.intentar nuevamente\n"). break. switch (opcion) { case 1: printf ("Ingresa un dato que sera agregado "). case 4: break. struct nodo *liga. LimpCola (apuntlista). } elemento_lista. int dato). *apuntlista. } /* fin de main */ . case 3: ImprCola (apuntlista). apuntlista = AgregaDato (apuntlista. break. else apuntlista = BorrarDato (apuntlista). apuntlista = NULL. } } while (opcion != 4). default: printf ("Opcion no valida . int dato. do { Menu (&opcion).

apuntlista -> liga = NULL.\n"). printf ("El elemento borrado es %d\n". printf ("Teclee 1 para agregar. if ((isdigit (local) == FALSO) && (local != ’\n’)) { printf ("\nSe debe ingresar un entero. apuntlista = apuntlista -> liga. printf("\nEntre\t1 para agregar un dato. apuntlista -> liga = NULL. } } elemento_lista *BorrarDato (elemento_lista *apuntlista) { elemento_lista *tempp. .\n\t3 para mostrar el contenid do { local = getchar (). } else { apuntlista = (struct nodo *) malloc (sizeof (elemento_lista)). apuntlista -> dato = dato. tempp = apuntlista -> liga. apuntlista -> liga = (struct nodo *) malloc (sizeof (elemento_lista)). return lp. 4 para salir\n"). } } while (isdigit ((unsigned char) local) == FALSO). 3 para imprimir.’0’. ASIGNACION DINAMICA DE MEMORIA Y ESTRUCTURAS DINAMICAS 60 void Menu (int *opcion) { char local. apuntlista -> dato = dato.\n\t2 para borrar un dato. } elemento_lista *AgregaDato (elemento_lista *apuntlista. apuntlista -> dato). int dato) { elemento_lista * lp = apuntlista. if (apuntlista != NULL) { while (apuntlista -> liga != NULL) apuntlista = apuntlista -> liga. 2 para borrar. return apuntlista. *opcion = (int) local . free (apuntlista).´ ´ ´ CAP´ ITULO 9.

´ visitar la ra´ recorrer el sub´rbol izquierdo en orden y recorrer el sub´rbol derecho en orden. se debe generar el arbol de ´ la figura 9. Cada rama entonces representa una ´ decisi´n de falso o verdadero.1. Para ordenar los n´meros simplemente asignar a la rama izquierda los n´meros o u u menores respecto al n´mero del nodo. que indica cu´ntos n´meros enteros ser´n guardados en un arreglo. ASIGNACION DINAMICA DE MEMORIA Y ESTRUCTURAS DINAMICAS return tempp. es decir. Por lo que la ız. a a salida deber´ ser: a Los valores ordenados son: 0 3 4 4 5 5 7 9 14 14 15 16 17 18 20 Mostrar 10 valores por l´ ınea. Un arbol binario es u ´ ´ una estructura tipo arbol con solamente 2 (posibles) ramas de cada nodo. Para obtener una lista ordenada en forma ascendente. u Por lo tanto. } printf ("\n"). apuntlista -> dato). si la lista de entrada es: 14 15 4 9 7 18 3 5 16 4 20 17 0 14 5. y en la rama derecha el resto (es decir. Escribir un programa que lea un n´mero. los que son mayores o iguales a). u a u a crear el arreglo para almacenar el tama˜o exacto de los datos y entonces leer los enteros que ser´n guardados n a en el arreglo. . else while (apuntlista != NULL) { printf ("%d\t". apuntlista = apuntlista -> liga. Escribir un programa para ordenar una secuencia de n´meros usando un arbol binario.´ ´ ´ CAP´ ITULO 9. } void LimpCola (elemento_lista *apuntlista) { while (apuntlista != NULL) { apuntlista = BorrarDato (apuntlista). } } 9. } 61 void ImprCola (elemento_lista *apuntlista) { if (apuntlista == NULL) printf ("La cola esta vacia !!\n").5. 2. recorrer el arbol en preorden (depth-first order). Ejercicios 1.

ASIGNACION DINAMICA DE MEMORIA Y ESTRUCTURAS DINAMICAS 62 14 4 15 3 9 14 18 0 7 16 20 5 17 4 5 Figura 9.´ ´ ´ CAP´ ITULO 9. .1: Arbol binario.

Un apuntador a un apuntador es una forma de direccionamiento indirecto m´ltiple. **ppch). El concepto de arreglos de apuntadores es directo ya que el arreglo mantiene su significado claro. a Como se usan los apuntadores en la l´ ınea de entrada. u Como se ve en la figura 10.1. o una cadena de apuntadores. Apuntadores a apuntadores Un arreglo de apuntadores es lo mismo que apuntadores a apuntadores. /* Un caracter */ char *pch. pch = &ch. /* Un apuntador a un apuntador a caracter */ ch = ’A’. se pueden confundir los apuntadores a apuntadores. Se puede llevar direccionamiento indirecto m´ltiple a cualquier extensi´n deseada. o incluso bueno de usar. Sin embargo. 10. pero hay pocos casos donde u o m´s de un apuntador a un apuntador sea necesario. ıcil Se puede tener un apuntador a otro apuntador de cualquier tipo. Y se revisan los apuntadores a funciones. En este cap´ ıtulo se profundizan algunos t´picos que o ya han sido mencionados brevemente y otros que completan la revisi´n de apuntadores en C. En el caso de un apuntador a un apuntador. el primer apuntador contiene la direcci´n o del segundo apuntador. Considere el siguiente c´digo: o main() { char ch.Cap´ ıtulo 10 T´picos avanzados con apuntadores o Se han revisado varias aplicaciones y t´cnicas que usan apuntadores en los cap´ e ıtulos anteriores. en el caso de un apuntador normal. el valor del apuntador es la direcci´n de la variable o que contiene el valor deseado. La direcci´n indirecta en exceso es a o dif´ de seguir y propensa a errores conceptuales. As´ mismo se han ı introducido algunos temas avanzados en el uso de apuntadores. ppch = &pch. /* Un apuntador a caracter */ char **ppch. printf("%c\n". o En este cap´ ıtulo se desarrolla lo siguiente: Se examinan apuntadores a apuntadores con m´s detalle.1. } 63 /* muestra el valor de ch */ . que apunta a la variable que contiene el valor deseado.

. a o 10. Los argumentos son dados o tecleados despu´s del nombre del programa al momento de ser ejecutado el programa. . Tomando un paso m´s all´ lo anterior.c son los argumentos. ppch[1]..1: Apuntador a un apuntador.. Por lo tanto. y apuntador a una cadena. un uso com´n y u conveniente es declarar un apuntador a un apuntador. Se debe recordar que char * se refiere a una cadena la cual termina con un nulo.2.c donde gcc es el compilador y -o prog prog. se pueden tener varias cadenas apuntadas por el apuntador.3: Apuntador a varias cadenas. Pero o o ¿qu´ significa lo anterior en la pr´ctica? e a ppch pch ch Figura 10. Para poder usar los argumentos en el c´digo se debe definir como sigue la funci´n main.2. por ejemplo: gcc -o prog prog.´ CAP´ ITULO 10. e Lo anterior se ha visto al momento de compilar. los cuales pueden ser usados en los programas. ver figura 10.3 String 0 ppch String 1 String 3 String 4 String 5 String 6 pch Figura 10. y el apuntador a una cadena. o o .2: Apuntador a un apuntador. ver figura a a 10. TOPICOS AVANZADOS CON APUNTADORES 64 Lo anterior se puede visualizar como se muestra en la figura 10. Esto es id´ntico a haber e declarado char *ppch[]. Entrada en la l´ ınea de comandos C permite leer argumentos en la l´ ınea de comandos. y apuntador a un char . la cual a su vez se refiere a la direcci´n de memoria de la variable ch. Se pueden hacer referencias a cadenas individuales mediante ppch[0]. en donde se observa que **ppch se refiere a la direcci´n de memoria de *pch.1. String ppch pch Figura 10. Una aplicaci´n com´n de lo anterior es en los argumentos de la l´ o u ınea de comandos que se revisar´n a continuaci´n.

TOPICOS AVANZADOS CON APUNTADORES main(int argc. char **argv) { /* Este programa muestra los argumentos de la linea de comandos */ int i. u * argv es un arreglo de cadenas que tiene a cada uno de los argumentos de la l´ ınea de comandos — incluyendo el nombre del programa en el primer elemento del arreglo.argc cuenta el n´mero de argumentos incluyendo el nombre del programa. ++i) printf("\t\targv[%d]: %s\n". o * argc es el n´mero de argumentos dados — incluyendo el nombre del programa. char **argv) o main(int argc. char *argv[]) 65 Con lo que la funci´n principal tiene ahora sus propios argumentos. for (i=0.Las comillas dobles " " son ignoradas y son usadas para incluir espacios dentro de un argumento.´ CAP´ ITULO 10. . printf("argc = %d\n\n". i.argc).Los espacios en blanco delimitan el fin de los argumentos. argv[i]). i<argc. u . Estos son solamente los unicos argumentos o ´ que la funci´n main acepta.argv[0] contiene el nombre del programa. Se muestra a continuaci´n un programa de ejemplo: o main (int argc. . } Suponiendo que se compila y se ejecuta con los siguientes argumentos: args f1 "f2 y f3" f4 5 FIN La salida ser´: a argc = 6 argv[0]: argv[1]: argv[2]: argv[3]: argv[4]: argv[5]: args f1 f2 y f3 f4 5 FIN Observar lo siguiente: . .

los cuales son equivalentes. size_t tam. cero : si el primer valor es igual que el segundo. Como o a o se ver´ m´s adelante la biblioteca est´ndar de C da funciones para ordenamiento (qsort) y para realizar b´squeda a a a u (bsearch). La funci´n compar debe regresar un determinado valor entero de acuerdo al resultado de comparaci´n. a las cuales se les pueden pasar funciones. .h es: o void qsort(void *base. La funci´n de la biblioteca est´ndar qsort es muy util y esta dise˜ada para ordenar un arreglo usando un valor o a ´ n como llave de cualquier tipo para ordenar en forma ascendente. TOPICOS AVANZADOS CON APUNTADORES 66 10. mayor que cero : si el primer valor es mayor que el segundo. un uso com´n es cuando se u pasan apuntadores a funciones como par´metros en la llamada a una funci´n. o Para que trabaje en forma completa el compilador es conveniente que se tengan los prototipos completos de las funciones y los apuntadores a las funciones. Por ejemplo. int (*pf) (int) = &f. Sin embargo. a Se pueden hacer cosas como: ans = f(5). ans = pf(5). El argumento base apunta al comienzo del vector que ser´ ordenado. se pueden pasar los datos y la funci´n que ser´ usada por alguna funci´n de control. por ejemplo: int f(int). const void *)). Ahora f() regresa un entero y toma un entero como par´metro. para que pf apunte a la funci´n f(). El prototipo de la funci´n qsort de la biblioteca stdlib. Para declarar un apuntador a una funci´n se debe hacer: o int (*pf) (). Observar que qsort conserva su independencia respecto al tipo de dato al dejarle la responsabilidad al usuario. entonces simplemente se debe escribir: o pf = &f.´ CAP´ ITULO 10. n o La funci´n qsort llama a la funci´n compar la cual es definida por el usuario para comparar los datos cuando o o se ordenen. nmiemb indica el tama˜o del arreglo. int (*compar)(const void *.3. Apuntadores a funciones Los apuntadores a funciones son quiz´ uno de los usos m´s confusos de los apuntadores en C. que o o debe ser: menor que cero : si el primer valor es menor que el segundo. a o Lo anterior es especialmente util cuando se deben usar distintas funciones quiz´s para realizar tareas similares ´ a con los datos. Los apuntadores a a a funciones no son tan comunes como otros usos que tienen los apuntadores. tam a n es el tama˜o en bytes de cada elemento del arreglo y el argumento final compar es un apuntador a una funci´n. o Suponiendo que se tiene una funci´n int f(). size_t nmiemb. Lo cual declara un apuntador pf a una funci´n que regresa un tipo de dato int. Todav´ no se ha indicado a que o ıa funci´n apunta.

se o o hace un cast para forzar el tipo void * al tipo char *. cad[i]). } int comp(const void *i. pero el programa deber´ permitir un argumento opcional tal que a ultlin n muestra las ultimas n l´ ´ ıneas. /* empleando (char *) */ return *a . El programa deber´ hacer el mejor uso del espacio de a almacenamiento.) a 2. printf("\n\nArreglo original: \n"). } 10. Escribir un programa que muestre las ultimas l´ ´ ıneas de un texto de entrada. sizeof(char). Escribir un programa que ordene una lista de enteros en forma ascendente. cad[i]).h> int comp(const void *i. const void *j). a 3. for (i=0. main() { int i. donde n es un entero.4. observar que en la funci´n comp. i++) printf("%c". TOPICOS AVANZADOS CON APUNTADORES 67 A continuaci´n se muestra un ejemplo que ordena un arreglo de caracteres. i++) printf("%c". #include <stdlib. a = (char *) i. Por defecto o “default” n deber´ ser a 7. se hace cast */ b = (char *) j. i<strlen(cad). Ejercicios 1. printf("\n\nArreglo ordenado: \n"). printf("\n"). Escribir un programa que lea la siguiente estructura y ordene los datos por la llave usando qsort . /* Para forzar void * al tipo char *. Sin embargo. char cad[] = "facultad de ciencias fisico-matematicas". const void *j) { char *a. (El texto de entrada podr´ ser le´ de un archivo dado desde la l´ a ıdo ınea de comandos o leyendo un archivo de la entrada est´ndar.*b. for (i=0. qsort(cad. strlen(cad). *b. si una opci´n r esta o presente en la l´ ınea de comandos el programa deber´ ordenar la lista en forma descendente. comp ).´ CAP´ ITULO 10. i<strlen(cad).

. int algo_mas.´ CAP´ ITULO 10. } Record. TOPICOS AVANZADOS CON APUNTADORES 68 typedef struct { char llave[10].

o Los operadores de desplazamiento. El operador unario ~ s´lo requiere un argumento a la derecha del operador. Operadores sobre bits Operador & | ^ ~ << >> Acci´n o Y O O exclusiva (XOR) Complemento a uno Negaci´n o Desplazamiento a la izquierda Desplazamiento a la derecha Los operadores sobre bits de C se resumen en la siguiente tabla: No se debe confundir el operador & con el operador &&: & es el operador Y sobre bits. Se debe recordar que un desplazamiento no es una rotaci´n: los bits desplazados en un extremo no vuelven al otro. donde un desplazamiento a la izquierda es multiplicar por 2 y a uno a la derecha dividir por 2. la computadora trae ceros en el otro extremo. mueven todos los bits en una posici´n hacia la derecha o la izquierda o un determinado n´mero de posiciones. o Similarmente los operadores | y ||. La combinaci´n de apuntadores y operadores a nivel bit hacen de C util para muchas aplicaciones de bajo nivel o ´ y pueden casi reemplazar al c´digo ensamblador. >> y <<.1. El formato general de la sentencia de desplazamiento a la derecha es: u variable >> num_pos_de_bit y el formato general de desplazamiento a la izquierda es variable << num_pos_de_bit Como los operadores desplazan bits en un sentido. 69 . aplicaciones del tipo sistemas) operan actualmente a bajo nivel donde bits individuales deben ser manipulados.Cap´ ıtulo 11 Operadores de bajo nivel y campos de bit Se ha visto como los apuntadores nos dan control sobre las operaciones de bajo nivel de la memoria. Una aplicaci´n que tienen los operadores de desplazamiento de bits es para realizar multiplicaciones y divisiones o r´pidas con enteros. Como se ve en la siguiente tabla. && es el operador l´gico Y. Se pierden y los o ceros tra´ ıdos los reemplazan.) 11. (Solamente un 10 % aproximadamente de UNIX esta en c´digo o o ensamblador el resto es C. Muchos programas (por ejemplo.

ciertas interfaces de dispositivo transmiten informaci´n que se codifica en bits dentro de un byte. se pueden almacenar varias variables booleanas en un byte. return count. Un campo de bit es un tipo especial de e estructura que define la longitud en bits que tendr´ cada elemento. tipo nombre n : longitud. El formato general de una definici´n de campo a o de bit es: struct nomb estruct { tipo nombre 1 : longitud. . El ciclo for iterativamente desplaza a la derecha x hasta que x se hace 0. o El m´todo que C usa para acceder a los bits se basa en la estructura. . y si este es 1 entonces incrementa count. se muestra la siguiente funci´n contbit. Este m´todo puede ser util´ ´ e ısimo por una serie de razones: .Si el almacenamiento es limitado.ciertas rutinas de encriptaci´n necesitan acceder a los bits en un byte.CAP´ ITULO 11. 11. si se quieren multiplicaciones o divisiones r´pidas por 2 use desplazamientos.2. x & 01 enmascara el primer bit de x. a Con la finalidad de ilustrar algunos puntos de los operadores sobre bits. x << 2. x << 3. x << 1. u o int contbit(unsigned char x) { int count. . x>>=1) if ( x & 1) count++. x >> 2. tipo nombre 2 : longitud. Por lo a a o o tanto. x!=0. x>>=1 es equivalente a x = x >> 1. . Campos de bit Al contrario de la mayor´ de los lenguajes de programaci´n. x >> 1. C tiene un m´todo predefinido para acceder a un ıa o e unico bit en un byte. o la cual cuenta los bits puestos a 1 en un n´mero de 8 bits (unsigned char) pasado como un argumento a la funci´n. o . Ejecuci´n o 0 0 0 1 1 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 Valor de x 7 14 112 192 96 24 70 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 Los desplazamientos son mucho m´s r´pidos que la multiplicaci´n actual (*) o la divisi´n (/) por dos. OPERADORES DE BAJO NIVEL Y CAMPOS DE BIT char x x = 7. . } En esta funci´n se ilustran varias caracter´ o ısticas de C: El ciclo for no es usado para simplemente contar. for (count=0.

por alguna de las siguientes razones: Los enteros podr´ ser con o sin signo. ıan Muchos compiladores limitan el n´mero m´ximo de bits en el campo de bit al tama˜o de un integer. Se debe declarar los campos de bits de longitud 1 como unsigned.1. u a Se permite mezclar los tipos “normales” con los campos de bit. La forma de accesar los miembros es en la forma usual: paquete. unsigned int b3:1. Con estructura anterior se tiene que: Solamente los n bits m´s bajos son asignados a un n´mero de n bits. unsigned int tipo:4. el siguiente campo de bit podr´ ser guardado consecutivamente ıa en memoria (traslapando los l´ ımites entre las localidades de memoria) o en la siguiente palabra de memoria. ya que un bit unico no puede tener signo. unsigned int b2:1. Si no fuera el caso. Portabilidad Los campos de bit son una forma conveniente de expresar muchas operaciones dificiles. el campo tipo a u no puede tomar valores mayores que 15. donde la longitud a m´xima del campo es menor que o igual a la longitud de la palabra entera de la computadora. ya que es de 4 bits de largo. uno de 4 bits (tipo) y otro de 9 bits (ent raro). ıa Algunos miembros campos de bit son guardados de izquierda a derecha. b3 y b4). considerar esta definici´n de estructura: o struct empaquetado { unsigned int b1:1. unsigned int ent_raro:9. } paquete. La estructura empaquetado contiene 6 miembros: 4 banderas de 1 bit (b1.2. Sin embargo. Por lo tanto. u a n el cual podr´ ser de 16 bits o de 32 bits. Los campos de bit son siempre convertidos a un tipo entero cuando se hace alg´n c´lculo con ellos. unsigned o signed.tipo = 7. otros son guardados de derecha a izquierda en memoria.CAP´ ITULO 11. unsigned int b4:1. Si los campos de bits son muy largos. a entonces algunos compiladores podr´ permitir traslape en memoria para los campos. . mientras otros podr´ guardar ıan ıan el siguiente campo en la siguiente palabra. C autom´ticamente empaca los campos de bit anteriores tan compactamente como sea posible. los campos de bit carecen de portabilidad entre plataformas. OPERADORES DE BAJO NIVEL Y CAMPOS DE BIT } 71 Se debe declarar un campo de bit como int. 11. b2. ´ Por ejemplo.

Ejercicios 1. con n bits que empiezan en la posici´n p y estan a o o la derecha de una variable y unsigned char. sin embargo la entrada puede ser en a forma decimal. si x = 10101010 (170 decimal). entonces se necesita tomar 3 bits de de y (111) y ponerlos en x en la posici´n 10xxx 010 para obtener la respuesta 10111010.CAP´ ITULO 11. Escribir una funci´n que rote (no desplaze) a la derecha n posiciones de bits de x del tipo unsigned char.3. y = 10100111 (167 decimal). Escribir una funci´n que invierta los bits de x (unsigned char) y guarde la respuesta en y. o La respuesta deber´ mostrarse en forma binaria (ver primer ejercicio). Escribir una funci´n que muestre un n´mero de 8 bits (unsigned char) en formato binario. dej´ndolos en x en la posici´n p y a la izquierda dejando los otros a o bits sin cambio. p = 6 da x = 10111010 (binario) 3. OPERADORES DE BAJO NIVEL Y CAMPOS DE BIT 72 11.n. La salida podr´ ser como la siguiente: ıa x = 10101010 (binario) x invertida = 01010101 (binario) 4. Por ejemplo. sin embargo la entrada puede estar en a forma decimal. o u 2. a La salida podr´ ser como la siguiente: ıa x = 10100111 (binario) x rotada por 3 = 11110100 (binario) . Escribir una funci´n ponerbits(x. o La respuesta deber´ mostrarse en forma binaria (ver primer ejercicio). n = 3 y p = 6. La salida podr´ ser como la siguiente: ıa x = 10101010 (binario) y = 10100111 (binario) ponerbits n = 3.p. La o respuesta deber´ mostrarse en forma binaria (ver primer ejercicio) y la entrada puede ser en forma decimal.y) que regrese x.

Cada vez que el compilador encuentra el nombre de macro. ´ El preprocesador tiene m´s o menos su propio lenguaje el cual puede ser una herrramienta muy poderosa para el a programador. fin se puede hacer: o #define inicio { #define fin } Durante la compilaci´n todas las ocurrencias de inicio y fin ser´n reemplazadas por su correspondiente { o } o a delimitador y las siguientes ´tapas de compilaci´n de C no encontrar´n ninguna diferencia..son m´s f´ciles de modificar a a ..1..y el c´digo de C es m´s transportable entre diferentes arquitecturas de m´quinas. a a . Por ejemplo: 73 . los argumentos reales encontrados en el programa reemplazan los argumentos asociados con el nombre de la macro.. } delimitadores que haya inventado el programador como inicio .Cap´ ıtulo 12 El preprocesador de C 12. a a .son m´s f´ciles de leer.1.1. e o a La directiva #define se usa para definir constantes o cualquier sustituci´n de macro. Todas las directivas de preprocesador o comandos inician con un #. Por ejemplo.los programas son m´s f´ciles de desarrollar. #define El preprocesador tambi´n permite configurar el lenguaje. o a a 12. Las ventajas que tiene usar el preprocesador son: . Su formato es el siguiente: o #define <nombre de macro><nombre de reemplazo> Por ejemplo: #define FALSO 0 #define VERDADERO !FALSO La directiva #define tiene otra poderosa caracter´ ıstica: el nombre de macro puede tener argumentos. para cambiar a las sentencias de bloque e de c´digo { . Directivas del preprocesador Como se comento en el primer cap´ ıtulo el preprocesamiento es el primer paso en la ´tapa de compilaci´n de un e o programa –esta propiedad es unica del compilador de C.

12.2.1. El formato o general es: #undef <nombre de macro> El uso principal de #undef es permitir localizar los nombres de macros s´lo en las secciones de c´digo que los o o necesiten.1. Como se puede observar donde se coloque MIN. MIN(x. EL PREPROCESADOR DE C #define MIN(a. As´ despu´s de que el compilador hace la sustituci´n.y) ).s+t). printf("EL minimo es %d\n". Por lo tanto. y=20. excepto que x e y a o ser´n usados como los operandos. } 74 Cuando se compila este programa. M_PI es el valor de pi */ /* y esta definida en la biblioteca math. #include La directiva del preprocesador #include instruye al compilador para incluir otro archivo fuente que esta dado con esta directiva y de esta forma compilar otro archivo fuente. a o si en el c´digo se hubiera puesto algo como: o x = MIN(q+r.3. la sentencia printf ser´ ´sta: a ı e o ae printf("El minimo es %d\n". el texto ser´ reemplazado por la definici´n apropiada. se paga este incremento de o o velocidad con un incremento en el tama˜o del programa porque se duplica el c´digo.b) (a < b) ? a : b main() { int x=10. (x < y) ? x : y). El archivo fuente que se leer´ se debe encerrar entre a comillas dobles o par´ntesis de angulo.y). Sin embargo.0) /* Convierte grados sexagesimales a radianes. el compilador sustiuir´ la expresi´n definida por MIN(x.CAP´ ITULO 12.h */ #define IZQ_DESP_8 <<8 La ultima macro IZQ DESP 8 es solamente v´lida en tanto el reemplazo del contexto es v´lido. el c´digo podr´ verse de la siguiente forma: e o ıa x = ( q+r < s+t ) ? q+r : s+t. n o 12. El uso de la sustituci´n de macros en el lugar de las funciones reales tiene un beneficio importante: incrementa o la velocidad del c´digo porque no se penaliza con una llamada de funci´n. Por ejemplo: e ´ . por ejemplo: x = ´ a a y IZQ DESP 8. Otros ejemplos usando #define pueden ser: #define Deg_a_Rad(X) (X*M_PI/180. #undef Se usa #undef para quitar una definici´n de nombre de macro que se haya definido previamente. despu´s del preprocesamiento.

CAP´ ITULO 12. e o El formato general de #ifdef es: #ifdef <nombre de macro> ¡secuencia de sentecias¿ #endif Si el nombre de macro ha sido definido en una sentencia #define. para poner el tama˜o de un entero para un programa portable entre TurboC de DOS y un sistema n operativo con UNIX. #else char moneda[]="franco". entonces si se quiere compilar para TurboC se puede definir una macro TURBOC.4. sabiendo que TurboC usa enteros de 16 bits y UNIX enteros de 32 bits. se compilar´ la secuencia de sentecias entre a el #ifdef y #endif. Se pueden as´ mismo evaluar otro c´digo en caso se cumpla otra condici´n. Los archivos incluidos usualmente contienen los prototipos de las funciones y las declaraciones de los archivos cabecera (header files) y no tienen c´digo de C (algoritmos). #elif PAIS_ACTIVO == EUA char moneda[]="dolar". la cual ser´ usada de la siguiente forma: a . EL PREPROCESADOR DE C #include <archivo> #include "archivo" 75 Cuando se indica <archivo> se le dice al compilador que busque donde estan los archivos incluidos o “include” del sistema. Si se usa la forma "archivo" es buscado en el directorio actual. Siempre se debe terminar con #endif para delimitir el o fin de esta sentencia.1. cuando no se cumple ninguna ı o o usando #elif o #else respectivamente. Usualmente los sistemas con UNIX guardan los archivos en el directorio /usr/include. Por ejemplo. o 12. #endif Otro m´todo de compilaci´n condicional usa las directivas #ifdef (si definido) y #ifndef (si no definido). o bien. Por ejemplo. #if Inclusi´n condicional o La directiva #if evalua una expresi´n constante entera. El formato general de #ifdef es: #ifndef <nombre de macro> ¡secuencia de sentecias¿ #endif Las directivas anteriores son utiles para revisar si las macros est´n definidas — tal vez por m´dulos diferentes o ´ a o archivos de cabecera. donde el programa esta siendo ejecutado. #define MEX 0 #define EUA 1 #define FRAN 2 #define PAIS_ACTIVO MEX #if PAIS_ACTIVO == MEX char moneda[]="pesos". es decir.

por ejemplo: gcc -DDEBUG prog. o #else printf("Version del programa 1.0\n"). GCC a ignora los archivos de entrada que no requieran preprocesamiento. ver el siguiente ejemplo: x = y * 3. Control del preprocesador del compilador Se puede usar el compilador gcc para controlar los valores dados o definidos en la l´ ınea de comandos.2. EL PREPROCESADOR DE C #ifdef TURBOC #define INT_SIZE 16 #else #define INT_SIZE 32 #endif 76 12. La salida del preprocesamiento es enviada a la entrada est´ndar.x. Otras directivas del preprocesador La directiva #error forza al compilador a parar la compilaci´n cuando la encuentra. Esto permite alguna flexibilidad para configurar valores ademas de tener algunas otras funciones utiles. #ifdef DEBUG printf("Depurando: variables x e y iguales a \n". se pueden filtrar variables para mostrar su valor. se usa la ´ opci´n -Dmacro[=defn]. Las aplicaciones pueden ser diversas. por ejemplo: o gcc -DLONGLINEA=80 prog. #endif Como los comandos del preprocesador pueden estar en cualquier parte de un programa. por lo anterior no se o o e e esta propiamente compilando el programa. Para lo anterior.3.c -o prog que hubiese tenido el mismo resultado que #define LONGLINEA 80 en caso de que hubiera dentro del programa (prog. ıa o Tambi´n se puede poner un s´ e ımbolo sin valor. 12. #endif La opci´n -E hace que la compilaci´n se detenga despu´s de la ´tapa de preprocesamiento. Por ejemplo: o .0 (Estable)\n").CAP´ ITULO 12.c) alg´n #define o #undef pasar´ por encima de la entrada u ıa de la l´ ınea de comandos.y). y el compilador podr´ mandar un “warning” sobre esta situaci´n. cuando se esta depurando. como por ejemplo cuando se quiere como una bandera para depuraci´n se o puede hacer algo como lo siguiente: #ifdef DEBUG printf("Depurando: Versi´n del programa 1.c -o prog En donde el valor que se toma es de 1 para esta macro. Se usa principalmente para o depuraci´n.

b.h" #else #error Sistema Operativo incorrecto #endif 77 La directiva #line n´mero “cadena” informa al preprocesador cual es el n´mero siguiente de la l´ u u ınea de entrada. lo siguiente especifica que el contador de l´ ınea empezar´ con 100. Ejercicios 1. Por ejemplo. a #line 100 "test. Incorporar las macros en un programa demostrativo en donde se pida al usuario tres e n´meros y se muestre el m´s peque˜o.h> #elifdef OS_UNIX #include "default. /* macro predefinida para el nombre */ } 12. Definir un nombre de macro de preprocesador para seleccionar: los bits menos significativos de un tipo de dato unsigned char. e . linea 102 */ printf("%s\n".4.b). los mensajes de error producidos por el compilador de C pueden referenciar el nombre del archivo y la l´ ınea de la fuente original en vez de los archivos intermedios de C.c" /* inicializa el contador de linea y nombre de archivo */ main() /* linea 100 */ { printf("%d\n". Por ejemplo. Esto es usado frecuentemente cuando son traducidos otros lenguajes a C.__FILE__).c) en a n t´rminos de min(a.CAP´ ITULO 12. Definir otra macro min3(a. /* macro predefinida. u a n 2. EL PREPROCESADOR DE C #ifdef OS_MSDOS #include <msdos. el n-´simo bit (asumiendo que el menos significativo es 0) de un tipo de dato unsigned char.__LINE__).b) para determinar el entero m´s peque˜o. Definir una macro min(a. Cadena es opcional y nombra la siguiente l´ ınea de entrada.

ıas ıas Por ejemplo: make. La funci´n a o open() es un ejemplo de una llamada al sistema. o Ejemplos de las funciones de la biblioteca est´ndar que han sido vistas son las funciones de E/S de alto nivel a –fprintf(). etc. en donde la salida de un programa puede ser la o entrada de otro. Los programas escritos a a con UNIX y C est´ndares deben correr en cualquier m´quina pr´cticamente sin ning´n problema. awk. Uso de funciones de bibliotecas y llamadas del sistema Para usar las bibliotecas de funciones y las llamadas al sistema en un programa de C simplemente se debe llamar la funci´n apropiada de C. para editar. . Utiler´ de UNIX Hay cerca de 200 utiler´ que permiten ejecutar muchas rutinas sin escribir nuevos programas.2. Ejemplos de estas son el sistema b´sico de E/S. Sin embargo UNIX es mucho m´s util que lo anterior. 78 .. UNIX y las bibliotecas est´ndar a Existe una relaci´n estrecha entre C y el sistema operativo que ejecuta los programas de C.Cap´ ıtulo 13 C. grep diff. Biblioteca de funciones Que son adiciones al sistema operativo. que son el coraz´n del sistema o operativo o del kernel de UNIX. a Manejo de archivos El sistema jer´rquico de archivos emplea muchas rutinas de manejo de archivos. a a a u Multiusuario/Multitarea Muchos programas pueden compartir la capacidad de procesamiento de las m´quinas. Todas ellas pueden ser accesadas a desde programas de C. a ´ 13. En este cap´ ıtulo se ver´ como C y UNIX interactu´n juntos. entre otras cosas.1. e Entubamiento o Pipe Permite la conexi´n entre programas. 13. Las llamadas est´n actualmente escritas en C. compilar o ejecutar programas. a Programaci´n del Shell UNIX suministra un int´rprete de comandos poderoso que entiende mas de 200 comandos o e y que puede tambi´n correr UNIX o programas del usuario. acceso al reloj del sistema. Llamadas al sistema UNIX tiene aproximadamente 60 llamadas al sistema. Lo anterior puede hacerce desde la l´ ınea de comandos o dentro de un programa de C. malloc(). El sistema operativo o UNIX esta escrito en su mayor parte con C. a a Se usa UNIX para manejar el espacio del archivo.. Ventajas del usar UNIX con C Portabilidad Unix o alguna variante de UNIX est´n disponibles en muchas m´quinas.

a Todas las funciones matem´ticas como sin(). etc. o o Informaci´n de casi todas las llamadas al sistema y funciones de biblioteca est´n disponibles en las p´ginas del o a a man. sqrt() son funciones de la biblioteca est´ndar de maa a tem´ticas (math.c -o matprog. Por ejemplo: o o man drand48 nos dar´ informaci´n acerca de ´ste generador de n´meros aleatorios. a Para muchas llamadas del sistema y funciones de las bibliotecas se tiene que incluir el archivo cabecera apropiado. de otra forma la funci´n probablemente o producir´ resultados extra˜os.h. drand48().c -lm La opci´n final -lm es una instrucci´n para ligar la biblioteca matem´tica con el programa. etc. math. Se encuentran disponibles en l´ ınea con tan s´lo teclear man y el nombre de la funci´n. por ejemplo: stdio. C. a o e u . o Es importante asegurarse que los argumentos tengan los tipos esperados. srandom(). Para usar una funci´n se debe asegurar de haber puesto los correspondientes #include en el archivo de C.) son miembros de la biblioteca o a est´ndar stdlib.´ CAP´ ITULO 13.h. UNIX Y LAS BIBLIOTECAS ESTANDAR 79 Operadores aritm´ticos.h el comando podr´ ser de la siguiente forma: ıa gcc matprog.h. Por ejemplo. cos(). para compilar un programa que incluya funciones de la biblioteca math. e u y funciones para conversi´n de cadenas a los tipos b´sicos de C (atoi(). a n Algunas bibliotecas requieren opciones extras antes de que el compilador pueda soportar su uso. La p´gina de man o o a a para cada funci´n usualmente informa si se requiere alguna bandera de compilaci´n especial.h). De o esta forma la funci´n puede ser llamada correctamente. atof(). lrand48(). generadores de n´meros aleatorios — random().

Se muestra un ejemplo donde se hace uso de la funci´n div t: o 80 . ldiv_t ldiv(long int numer. Funciones aritm´ticas e Hay cuatro funciones enteras b´sicas: a int abs(int j). /* cociente */ /* residuo */ La estructura ldiv t es definida de una forma similar. La estructura div t esta definida en stdlib. Fundamentalmente hay dos funciones para enteros y para compatibilidad con enteros largos. long int denom).h> Las funciones de la biblioteca pueden ser agrupadas en tres categor´ b´sicas: ıas a Aritm´ticas. Se consideran dentro del cap´ ıtulo en forma breve.h> Para usar todas las funciones de ´sta biblioteca se debe tener la siguiente directiva e #include <stdlib. 14. int rem. e N´meros aleatorios. } div_t. y u Conversi´n de cadenas. long int labs(long int j). div_t div(int numer. o div() Calcula el valor numer entre denom y devuelve el cociente y el resto en una estructura llamada div t que contiene dos miembros llamados quot y rem.Cap´ ıtulo 14 Biblioteca <stdlib. int denom).1. abs() La funci´n regresa el valor absoluto del argumento entero j. o El uso de todas las funciones es sencillo.h como sigue: typedef struct { int quot.

rem).quot. rand() La funci´n devuelve un entero pseudo-aleatorio entre 0 y 215 − 1 (RAND MAX). res. Un ejemplo sencillo del uso del tiempo de la fecha es inicializando la semilla a trav´s de una llamada: e srand( (unsigned int) time( NULL ) ). En la pr´ctica ninguna funci´n produce datos aleatorios verdaderos — las funciones a o producen n´meros pseudo-aleatorios. Los n´meros aleatorios son calculados a partir de una f´rmula dada (los u u o distintos generadores usan diferentes f´rmulas) y las secuencias de n´meros que son producidas se repiten.CAP´ ITULO 14. BIBLIOTECA <STDLIB. printf("Respuesta:\n\t Cociente = %d\n\t Residuo = %d\n". ya que ´ste siempre estar´ cambiando. res = div(num. Por lo tanto. ** El segundo argumento de la funcion indica el numero de tarjetas. res. a Una t´cnica com´n para introducir m´s aleatoriedad en el generador de n´meros aleatorios es usando el tiempo e u a u y la fecha para inicializar la semilla. El siguiente programa tarjeta.c muestra el uso de estas funciones para simular un paquete de tarjetas que esta siendo revueltas. Todas ellas operan con la misma u a idea b´sica pero generan secuencias diferentes de n´meros (basadas en funciones generadoras diferentes) sobre rangos a u diferentes de n´meros. tales como juegos. div_t res. void srand(unsigned int semilla).h> main() { int num=8. u El conjunto m´s simple de funciones es: a int rand(void). o srand() Establece su argumento como la semilla de una nueva serie de enteros pseudo-aleatorios. den=3.2. . } que genera la siguiente salida: Respuesta: Cociente = 2 Residuo = 2 81 14. Una o u semilla (seed) es usualmente inicializada para que la secuencia sea generada.H> #include <stdlib.den). /* ** Se usan numeros aleatorios para revolver las "tarjetas" de la baraja. u ´ simulaciones y experimentos. e a Hay muchas funciones de n´meros (pseudo) aleatorios en la biblioteca est´ndar. si la semilla es siempre inicializada con el mismo valor todo el tiempo. N´ meros aleatorios u Los n´meros aleatorios son utiles en programas que necesitan simular eventos aleatorios. el mismo conjunto ser´ siempre calculado.

} } 82 14. static int primera_vez = VERDADERO. enteros largos y valores flotantes.CAP´ ITULO 14. baraja[ alguno ] = baraja[ i ].h> #define VERDADERO 1 #define FALSO 0 void intercambiar( int *baraja. } /* ** "intercambiar" empleando pares de cartas. int temp. srand es ** llamada para inicializar el generador de numeros aleatorios. int atol(const char *cadena) Convierte una cadena a un valor entero largo. Estas son: double atof(const char *cadena) Convierte una cadena a un valor flotante. /* ** Inicializar el generador de numeros con la fecha actual ** si aun no se ha hecho. BIBLIOTECA <STDLIB. */ #include <stdlib. double strtod(const char *cadena. i -= 1 ){ int alguno. alguno = rand() % i. char **finap) Convierte una cadena a un valor de punto flotante. baraja[ i ] = temp.H> ** La primera vez que esta funcion es llamada. srand( (unsigned int)time( NULL ) ). Conversi´n de cadenas o Existen unas cuantas funciones para convertir cadenas a enteros.h> #include <time. i > 0.3. temp = baraja[ alguno ]. */ if( primera_vez ){ primera_vez = FALSO. */ for( i = n_cartas . int n_cartas ) { int i. int atoi(const char *cadena) Convierte una cadena a un valor entero. .1.

size_t nmemb. &cad7). /* i = atof(cad2). const void *base.44 */ 1234 */ 123 */ 0 */ /* f=1.230000E+25 y cad7=hola*/ Nota: Los caracteres en blanco son saltados. Caracteres ilegales son ignorados. Si la conversi´n no puede ser hecha se regresa cero y errno es puesta con el valor ERANGE. hay una funci´n para b´squeda binaria. /* i = atoi(cad4). "123cuatro". const void *)). B´ squeda y ordenamiento u La biblioteca stdlib. int (*compar)(const void *. A continuaci´n se muestra un ejemplo que hace uso de la funci´n bsearch(): o o . "55. ya que esta dise˜ada para ordenar un arreglo por un valor o a ´ n llave de cualquier tipo en orden ascendente. int base) Convierte una cadena a un entero largo de acuerdo a una base dada. 100 */ 55. char *finap. BIBLIOTECA <STDLIB.CAP´ ITULO 14. " 1234". El prototipo de la funci´n de acuerdo a stdlib. int base) Convierte una cadena a un entero largo sin signo. "123E23Hola". pero para ver un ejemplo pasar al cap´ ıtulo indicado. la cual deber´ estar entre 2 y 36 inclusive. La funci´n qsort de la biblioteca est´ndar es muy util. o o Para completar la lista se anota el prototipo.h o u como: void *bsearch(const void *key. /* i = strtod(cad6. float f: i f i i i f = = = = = = atoi(cad1). "100".h tiene dos funciones utiles para hacer b´squeda y ordenamiento de datos de cualquier tipo. int (*compar)(const void *.3. por ejemplo: char char char char char char char *cad1 = *cad2 = *cad3 = *cad4 = *cad5 = *cad6 = *cad7. "invalido123". o 14. /* f = atoi(cad3).444". const void *)). /* i = atoi(cad5). int i. Varias de las funciones se pueden usar en forma directa. char *finap.H> 83 double strtol(const char *cadena.h es: o void qsort(void *base.4. size_t tam. Similarmente. size_t nmiemb. size_t size. a unsigned long strtoul(const char *cadena. bsearch() la cual tiene el siguiente prototipo en stdlib. ´ u La funci´n qsort() ya fue vista previamente en la secci´n 10. con tal de que los elementos del arreglo sean de un tipo fijo.

/* valor del indice que sera buscado */ resp = (Registro *) bsearch(&llave. if (resp != NULL ) printf("Se localizo la llave %d\n". void const *j) { int a.llave = 12.llave arreglo[3]. a = ((Registro *) i)->llave. arreglo[0].llave).H> 84 #define TAM 10 #include <stdio. llave.resp->llave).h> typedef struct { int llave. int compara_registro(void const *i. Registro *resp. scanf("%d". char informacion[30].llave = 0. Observar que el tipo del argumento de la llave debe ser del mismo tipo que la del arreglo de los elementos (en nuestro caso Registro).llave = -9.&llave. arreglo[1]. } La funci´n bsearch() regresa un apuntador al registro que coincide con la llave dada. el valor NULL si no o encuentra el registro.b. } Registro. Registro arreglo[TAM].llave arreglo[9].llave = 4. printf("Introduce el valor de la llave que se buscara en el arreglo: "). int long_arreglo = TAM. arreglo[8]. b.llave = = = = -43.h> #include <stdlib. arreglo[7]. long_arreglo. } main() { Registro llave. arreglo[5]. compara_registro). arreglo[2].llave arreglo[6]. . arreglo[4]. o bien.llave).llave = 30. arreglo. 3. BIBLIOTECA <STDLIB. else printf("No se localizo ningun elemento con la llave %d\n". sizeof(Registro).CAP´ ITULO 14. 203. b = ((Registro *) j)->llave. 10. return a .llave = 9.

H> 85 14. Escribir un programa que simule el melate. BIBLIOTECA <STDLIB.5. en donde se seleccionan 6 n´meros enteros en un rango de 1 al 44 u 3. Ejercicios 1.CAP´ ITULO 14. Escribir un programa que simule el lanzamiento de un dado. u . 2. Escribir un programa que lea un n´mero de la l´ u ınea de entrada y genere un n´mero flotante aleatorio en el u rango de 0 a el n´mero de entrada.

h>. Es similar a calcular el arco tangente de y / x.Cap´ ıtulo 15 Biblioteca <math.h> La biblioteca de matem´ticas es relativamente f´cil de usar. No se proporciona ejemplo de las mismas. Se debe incluir a a la directiva de preprocesamiento #include <math. excepto en que los signos de ambos argumentos son usados para determinar el cuadrante del resultado. double y) Calcula el resto de la divisi´n de x entre y.c -o progmat -lm Un error com´n es el olvidar incluir el archivo <math. donde x est´ dado en radianes. a double cosh(double x) Devuelve el coseno hiperb´lico de x. a double fmod(double x. double fabs(double x) Devuelve el valor absoluto del n´mero en punto flotante x. al igual que las vistas previamente.n * y. u 15. double frexp(double x. Son f´ciles de usar y algunas de ellas han sido ya o a a usadas previamente. double ceil(double x) Redondea x hacia arriba al entero m´s cercano.h>. a double cos(double x) devuelve el coseno de x. double atan2(double y. u double floor(double x) Redondea x hacia abajo al entero m´s cercano. double x) Calcula el arco tangente de las dos variables x e y. adem´s de recordar de ligar la biblioteca de matem´ticas al a a compilar: gcc progmat. int *exp) Se emplea para dividir el n´mero x en una fracci´n normalizada y un u o exp exponente que se guarda en exp x = res × 2 . El valor devuelto es x . double acos(double x) Calcula el arco coseno de x. double asin(double x) Calcula el arco seno de x. 86 . o donde n es el cociente de x / y. Funciones matem´ticas a Se muestra a continuaci´n una lista de funciones matem´ticas. o double exp(double x) Devuelve el valor de e (la base de los logaritmos naturales) elevado a la potencia x.1. double atan(double x) Devuelve el arco tangente en radianes.

int exp) Devuelve el resultado de multiplicar el n´mero x por 2 elevado a exp u (inversa de frexp). double *iptr) Divide el argumento x en una parte entera y una parte fraccional. M_LN10 El logaritmo natural de 10. M_PI π M_PI_2 π/2 M_PI_4 π/4 M_1_PI 1/π M_2_PI 2/π √ M_2_SQRTPI 2/ π M_SQRT2 La ra´ cuadrada positiva de 2 ız M_SQRT1_2 La ra´ cuadrada positiva de 1/2 ız . double log(double x). M_E La base de los logaritmos naturales e. ız double tan(double x) Devuelve la tangente de x. M_LOG2E El logaritmo de e de base 2. La parte entera se guarda en iptr. Siempre es aconsejable usar a estas definiciones. Devuelve el logaritmo neperiano de x. double sinh(double x) Regresa el seno hiperb´lico de x. double log10(double x) Devuelve el logaritmo decimal de x.CAP´ ITULO 15. BIBLIOTECA <MATH. o double sqrt(double x) Devuelve la ra´ cuadrada no negativa de x. 87 double ldexp(double x. double pow(double x. double modf(double x.H> long int labs(long int j) Calcula el valor absoluto de un entero largo. Constantes matem´ticas a La biblioteca de matem´ticas define varias constantes (por lo general desechadas). double y) Devuelve el valor de x elevado a y. M_LN2 El logartimo natural de 2. double sin(double x) Devuelve el seno de x. M_LOG10E El logaritmo de e de base 10. o 15.2. double tanh(double x) Devuelve la tangente hiperb´lica de x.

errno A la variable especial del sistema errno algunas llamadas al sistema (y algunas funciones de biblioteca) le dan un valor entero. se muestra primero.h> 16. para poder e a detectar posibles errores. luego un signo de dos puntos y un espacio en blanco.h En este cap´ ıtulo se ver´n varias formas de entrada y salida (E/S). para indicar que ha habido un error. La cadena de caracteres s que se pasa como argumento.h> y para ser usada dentro de un programa debe ser declarada de la siguiente forma: extern int errno. pero no es puesta a cero en una llamada no err´nea.1.1.1. el mensaje y ´ un salto de l´ ınea.1. Se han mencionado brevemente algunas formas a y ahora se revisar´n con un poco m´s de detalle. La funci´n de la biblioteca est´ndar perror ´ o a es la indicada para hacerlo. perror() El prototipo de la funcion perror es: void perror(const char *s). 16. que toma un valor o o o cuando ocurre un error.2. o 16. por lo que se debe poner errno a cero antes de la llamada. a a Los programas que hagan uso de las funciones de la biblioteca de E/S deben incluir la cabecera. Es usada conjuntamente con la variable errno y frecuentemente cuando se encuentra un error se desea terminar el programa antes. El c´digo del error se toma de la variable externa errno. por ultimo. esto es: #include <stdio. Para ser de m´s utilidad. para ver como trabajan con perror. algunas veces una funci´n o o o tambi´n puede devolver -1 como valor v´lido. Reportando errores En muchas ocasiones es util reportar los errores en un programa de C.Cap´ ıtulo 16 Entrada y salida (E/S) stdio. El valor s´lo es significativo cuando la llamada devolvi´ un error (usualmente -1).h. 88 . describiendo el ultimo error o a ´ encontrado durante una llamada al sistema o a ciertas funciones de biblioteca. Adem´s se revisa la funci´n exit() y errno. que en un sentido estricto a o no son parte de la biblioteca stdio. Esta variable esta definida en la cabecera #include <errno. la cadena de caracteres pasada como argumento deber´ incluir el nombre a ıa de la funci´n que incurri´ en el error. La funci´n perror() produce un mensaje que va a la salida est´ndar de errores.

Esto conduce a un uso eficiente de E/S pero se debe tener cuidado: los datos escritos a un buffer no aparecen en un archivo (o dispositivo) hasta que el buffer es escrito (con \n se puede hacer). exit La funci´n exit tiene el siguiente prototipo de acuerdo a la cabecera #include <stdlib.CAP´ ITULO 16. despu´s se puede a o e accesar y entonces se cierra. FILE. ENTRADA Y SALIDA (E/S) STDIO. Observar que el apuntador del archivo actualmente apunta a ´ste buffer.h>: o void exit(int status). e Figura 16. Por lo tanto simplemente se necesita referirse a la estructura para realizar entrada y salida de datos.H 89 16. un pedazo fijo de area temporal de la memoria (el buffer) es le´ o ´ ıdo escrito a un archivo. 16. Lo siguiente se muestra en la figura 17.3. Para usar los flujos entonces se debe declarar una variable o apuntador de este tipo en el programa. La funci´n produce la terminac´n normal del programa y la devoluci´n de status al proceso padre o al sistema o o o operativo. Existe una estructura interna de C.2.   ¡   ¡ Sistema Operativo   ¡   ¡   ¡   ¡   ¡   ¡   ¡ Dispositivo de Almacenamiento Archivo o disco   ¡   ¡   ¡   ¡   ¡   ¡   ¡ Buffer   ¡   ¡   ¡   ¡   ¡   ¡   ¡   ¡   ¡   ¡   ¡   ¡   ¡ Base del buffer Apuntador de archivo . Flujos Los flujos son una forma flexible y eficiente para leer y escribir datos.1.1. es decir. la cual representa a todas los flujos y esta definida en stdio.h. No se requiere conocer m´s detalles acerca de la definici´n. El flujo de E/S usa un BUFFER.1: Modelo de entrada salida usando un buffer. Se debe abrir un flujo antes de realizar cualquier E/S. El valor de status es usado para indicar como ha terminado el programa: o sale con un valor EXIT_SUCCESS en una terminaci´n o o sale con un valor EXIT_FAILURE en una terminaci´n o Por lo tanto cuando se encuentre un error se llamar´ a la funci´n como exit(EXIT_FAILURE) para terminar un a o programa con errores. Cualquier salida anormal del c´digo o puede causar problemas.

3. La consola es el dispositivo predefinido para stdout y stderr. ENTRADA Y SALIDA (E/S) STDIO. pero depende del sistema operativo. o a a . programas. si se espera entrada desde el teclado para un programa llamado entrada. si se tiene un programa llamado salida. En esta secci´n se revisar´n con m´s detalle. Quiz´s las m´s comunes son: getchar() y a a a putchar(). main() { int ch.4.h): stdin. stdout y stderr.CAP´ ITULO 16. El flujo stderr siempre va a la consola o la pantalla. entrada < archivo_ent Con | entubamiento o pipe se coloca stdout de un programa en stdin de otro. E/S formateada Se han visto ya algunos ejemplos de como C usa la E/S formateada. que usualmente muestra en pantalla algo. Todas ellas usan texto como m´todo de E/S. Los flujos predefinidos son autom´ticamente abiertas. 16. dispositivos de E/S como el teclado. } Otras funciones relacionadas son: int getc(FILE *flujo). int putc(char ch. etc. FILE *flujo). Por lo tanto.H 90 16. (void) putchar((char) ch). El teclado lo es para stdin. la consola. entonces: salida > archivo_sal mandar´ la salida al archivo archivo sal a < redirecciona stdin desde un archivo a un programa. prog1 | prog2 Por ejemplo. Se hace redireccionamiento desde la l´ ınea de comandos con: > que redirecciona stdout a un archivo. ch = getchar().1. E/S Basica Hay un par de funciones que dan las facilidades b´sicas de E/S. Por lo tanto. se puede leer en forma similar la entrada desde un archivo. e Los flujos stdin y stdout pueden ser usadas con archivos. Flujos predefinidos En UNIX se tienen predefinidos 3 flujos (en stdio.2. a Redireccionamiento Lo siguiente no es parte de C. Est´n definidas y son usadas como sigue: a int getchar(void) — lee un caracter de stdin int putchar(char ch) — escribe un caracter a stdout y regresa el caracter escrito. mandar la salida (usualmente a consola) de un programa directamente a la impresora out | lpr 16.

n´mero entero para el ancho del campo.d en donde m es el ancho del campo.E g.ddd .1. especificadores de conversi´n — precedidos por % y listados en la tabla 16.23e002 e o f la que sea ´ m´s compacta a caracter % Cuadro 16.X u s f e.17.5% . La funci´n devuelve el n´mero o u de caracteres impresos. ENTRADA Y SALIDA (E/S) STDIO.G % Tipo char int int int int char * double/float ” ” Resultado un s´lo caracter o n´mero base diez u n´mero base ocho u n´mero base dieciseis u notaci´n min´s/may´s o u u entero sin signo impresi´n de cadena o terminada por nulo formato -m. u Por lo tanto: printf("%-2..signo menos para justificar a la izquierda.1: Caracteres de format printf/scanf Entre el % y el caracter de formato se puede poner: .23478). y d es la precisi´n de d´ o ıgitos despu´s del punto decimal o el e n´mero de caracteres de una cadena... o Especificador ( %) c i. printf El prototipo de la funci´n esta definido como: o int printf( const char *formato. Notaci´n Cient´ o ıfica -1.5%%\n").1. lista arg .H 91 16. La cadena de formateo tiene dos tipos de objetos: caracteres ordinarios — estos son copiados a la salida..3f\n". u m.CAP´ ITULO 16.). genera la siguiente salida: VAT=17. la salida en pantalla ser´: a 17.d o x.4.235 y printf("VAT=17. que muestra en stdout la lista de argumentos de acuerdo al formato especificado.

CAP´ ITULO 16. Archivos Los archivos son la forma m´s com´n de los flujos. Lee de la entrada est´ndar (stdin) y coloca la entrada en la direcci´n de las variables indicadas en lista args...4. const char *modo). La funci´n fopen() hace lo siguiente: o FILE *fopen(const char *nomb. Importante: se requiere la direcci´n de la variable o un apuntador con scanf. scanf("%s". fopen regresa un apuntador a un FILE.. /* Se declara un flujo */ flujo = fopen("miarch. La cadena modo controla el tipo de acceso.). Para el caso de un arreglo o cadena s´lo se requiere el nombre del mismo para poder usar scanf ya que corresponde o al inicio de la direcci´n de la cadena. &i). exit(1). scanf La funci´n esta definida como sigue: o int scanf( const char *formato.dat". FILE *flujo. } . a o Regresa el n´mero de caracteres le´ u ıdos. para abrir un archivo denominado miarch.5."r"). ENTRADA Y SALIDA (E/S) STDIO. En la cadena nomb se pone el nombre y la trayectoria del archivo que se desea accesar. 16.dat". o char cadena[80]. Si un archivo no puede ser accesado por alguna raz´n un o apuntador NULL es devuelto..H 92 16..dat para lectura haremos algo como lo siguiente. o Por ejemplo: scanf("%d". La cadena de control de formateo es similar a la de printf.cadena). . lista arg . Por lo tanto.. Los modos son: “r” lectura."miarch. a u Lo primero que se debe hacer es abrir el archivo."r")) == NULL ) { printf("No se pudo abrir %s\n". es una buena pr´ctica revisar si un archivo se pudo abrir correctamente a if ( (flujo = fopen("miarch. “w” escritura.2.dat"). y “a” Para abrir un archivo se debe tener un flujo (apuntador tipo archivo) que apunte a la estructura FILE.

Lectura y escritura de archivos Las funciones fprintf y fscanf son com´nmente empleadas para accesar archivos. Por ejemplo: e fprintf(stderr."%s". o Con ambas se consigue el mismo resultado. sprintf y sscanf Son parecidas a fprintf y fscanf excepto que escriben/leen una cadena. args . FILE *s) Estas son parecidas a getchar y putchar.cadena). getc esta definida como una macro del preprocesador en stdio. o bien. ENTRADA Y SALIDA (E/S) STDIO.."%s". if ( (flujo = fopen( . etc.string). Por lo a tanto. 16. Otras funciones para archivos son: int getc(FILE *flujo) int fgetc(FILE *flujo) int putc(char ch. usar: int fflush(FILE *flujo). fgetc es una funci´n de la biblioteca de C.. int sprintf(char *cadena.. Para el volcado de los datos de los flujos a disco.. "Kilometros por litro = %2.. el cual deber´ ser a abierto con fopen(). El apuntador al flujo es autom´ticamente incrementado con todas las funciones de lectura y escritura. Tambi´n se puede tener acceso a los flujos predeterminados con fprintf. ) Por ejemplo: float tanque_lleno = 47.CAP´ ITULO 16.0. /* litros */ float kilometros = 400. cahr *formato.. int fclose(FILE *flujo). const char *formato. ). char *formato. args . ) int sscanf(char *cadena.. char km_por_litro[80]...5. args . sprintf( km_por_litro.h."<<No se puede calcular!!\n").. .H 93 16. FILE *s) int fputc(char ch.1. const char *formato. haciendo previamente un volcado. char *cadena[80]. kilometros/tanque_lleno). ). args . FILE *flujo. int fscanf(FILE *flujo.3f". fscanf(stdin. )) != NULL) fscanf(flujo. Las funciones son similares a printf y scanf excepto que los datos son le´ ıdos desde el flujo. u int fprintf(FILE *flujo.6. no se debe preocupar en hacer lo anterior. para disasociar un flujo a un archivo.

e Otras funciones son: int close(int fd). puede tambi´n ser usada para crear un archivo.linea). void clearerr(FILE *flujo). char *buffer. int ferror(FILE *flujo). char *buffer. el cual da un entero unico para identificar cada archivo. Por lo tanto para leer un flujo. u No hay facilidades de formateo –ya que se est´n manipulando bytes de informaci´n. ENTRADA Y SALIDA (E/S) STDIO. ´ Para abrir un archivo usar: int open(char* nomb. ´ flag controla el acceso al archivo y tiene los siguientes macros definidas en fcntl.CAP´ ITULO 16. a O EXCL abrir en forma exclusiva. int perms). E/S de bajo nivel o sin almacenamiento intermedio Esta forma de E/S es sin buffer –cada requerimiento de lectura/escritura genera un acceso al disco (o dispositivo) directamente para traer/poner un determinado n´mero de bytes. int flag). unsigned longitud). unsigned longitud).H 94 16. int fileno(FILE *flujo). como un entero. ferror() inspecciona el indicador de error para el flujo indicado. feof() devuelve verdadero si el flujo indicado esta en el fin del archivo. se emplea un manejador de archivo de bajo nivel o descriptor de archivo. . Petici´n del estado del flujo o Existen unas cuantas funciones utiles para conocer el estado de alg´n flujo y que tienen los prototipos siguientes: ´ u int feof(FILE *flujo). que regresa un descriptor de archivo o -1 si falla. int read(int fd. clearerr() limpia los indicadores de fin-de-fichero y error para el flujo indicado."%s". La funci´n: o int creat(char* nomb. int write(int fd. 16. O RDONLY s´lo lectura o O RDWR lectura y escritura O WRONLY s´lo escritura o para ver otras opciones usar man.6. a o Lo anterior significa que se estan usando archivos binarios (y no de texto). ser´ creado. l´ ınea a l´ ınea se podr´ hacer algo como: ıa while ( !feof(fp) ) fscanf(fp. fp.1.h: O APPEND el archivo se abrir´ en modo de s´lo a˜adir a o n O CREAT si el archivo no existe. En vez de manejar apuntadores de archivos. regresando verdadero si un error ha ocurrido. fileno() examina el argumento flujo y devuelve su descriptor de fichero.7.

S_IRUSR | S_IWUSR) ) == -1) { /* Error. sizeof(int)) ) == -1) { /* Error en la escritura */ perror("Archivo datos. } else printf("Escritos %d bytes\n".h> main(int argc. } else printf("Descriptor de archivo %d\n". ENTRADA Y SALIDA (E/S) STDIO.2.H 95 que pueden ser usadas para cerrar un archivo y leer/escribir un determinado n´mero de bytes de la memoria/hacia u un archivo en la localidad de memoria indicada por buffer. Se tiene a continuaci´n dos aplicaciones que usan algunas de las funciones indicadas: o #include #include #include #include <stdio. if ( (bytes_esc = write(df. o u Las funciones read y write regresan el n´mero de bytes le´ u ıdos/escritos o -1 si fallan. } else printf("Descriptor de archivo %d\n".df). . exit(1).34.h> <sys/stat. /* En primer lugar se escribe el n´umero de flotantes que seran escritos */ num_flot = 3. int num_flot. apertura"). archivo no abierto */ perror("Archivo datos. O_WRONLY) ) == -1) { /* Error. &num_flot.CAP´ ITULO 16.33}. apertura").h> <fcntl. /* Despues se abre para solamente escribir */ if ( (df = open(argv[1].bytes_esc). La funci´n sizeof() es com´nmente usada para indicar la longitud. O_CREAT. int bytes_esc. archivo no abierto */ perror("Archivo datos. /* Primeramente se crea el archivo */ if ( (df = open(argv[1]. exit(1). int df. char **argv) { float buffer[]={23. escritura").1112.df).34. exit(1).h> <sys/types.

CAP´ ITULO 16. exit(1). archivo no abierto */ perror("Archivo datos").h> #include <fcntl. buffer. int num_flot. buffer. exit(1).H /* Se escribe el arreglo de flotantes */ if ( (bytes_esc = write(df. int bytes_leidos. Los flotantes estan despues del entero. } if ( (bytes_leidos = read(fd. O_RDONLY)) == -1) { /* Error. } if ( (bytes_leidos = read(fd. num_flot*sizeof(float))) == -1) { /* Error en la lectura */ exit(1). } else printf("Escritos %d bytes\n". close(df). char **argv) { float buffer[1000]. } Ejemplo de lectura del ejemplo anterior: 96 /* /* /* /* Este programa lee una lista de flotantes de un archivo binario. */ */ */ */ #include <stdio. El primer byte del archivo es un entero indicando cuantos flotantes hay en el archivo.bytes_esc). } if ( num_flot > 999 ) { /* arch muy grande */ exit(1). int fd. sizeof(int))) == -1) { /* Error en la lectura */ exit(1). escritura"). el nombre del archivo se da en la linea de comandos.h> main(int argc. num_flot*sizeof(float)) ) == -1) { /* Error en la escritura */ perror("Archivo datos. ENTRADA Y SALIDA (E/S) STDIO. if ( (fd = open(argv[1]. } . &num_flot.

.que el segundo nombre del archivo sea de escritura o mostrar "No se puede abrir archivo .CAP´ ITULO 16. a . 3.. Por defecto n deber´ ser 5. Copiar bloques de 512 bytes cada vez.. Escribir un programa ultimas”que muestre las ultimas n l´ ¨ ´ ıneas de un archivo de texto...el primer nombre de archivo sea de lectura o mostrar "No se puede abrir archivo . n y el nombre del archivo deber´n especificarse desde la l´ a ınea de comandos. .8. para e 2. ENTRADA Y SALIDA (E/S) STDIO. Ejercicios 1.tenga dos argumentos o mostrar "El programa requiere dos argumentos". Escribir un programa que compare dos archivos y muestre las l´ ıneas que difieran. Los 2 nombres de los archivos son dados como los primeros argumentos del programa. Tip: buscar rutinas apropiadas para manejo de cadenas y manejo de archivos. Revisar que el programa: . El programa no deber´ ser muy grande. para lectura". El programa deber´ hacer a a el mejor uso del espacio de almacenamiento. Escribir un programa para copiar un archivo en otro.H } 97 16.

respectivamente.1. size t n) — Compara los primeros n caracteres de dos cadenas. Devuelve un entero menor.Cap´ ıtulo 17 Manejo de cadenas <string. Todas las funciones de la biblioteca est´ndar de C lo requieren para una operaci´n satisfactoria. Funciones b´sicas para el manejo de cadenas a Todas las funciones para manejo de cadenas tienen su prototipo en: #include <string. char *s2. o mayor que s2. Una cadena en C es una o secuencia de cero o m´s caracteres seguidas por un caracter NULL o \0: a DESCRIPCION F 0 A C U L T A D . int strcmp(const char *s1. const char *s2) — Compara las dos cadenas de caracteres s1 y s2.1: Representaci´n de un arreglo.h> Las funciones m´s comunes son descritas a continuaci´n: a o char *strcpy(const char *dest. igual a. menor que. ya que con ´ste es como C define y maneja las longitudes o e de las cadenas. u int strlen(const char *s) — Calcula la longitud de la cadena de caracteres. Las cadenas no deben solaa parse. debe ser suficientemente grande como para alojar la copia. 17. \0 49 Figura 17.. no se deber´n encontrar problemas. size t n) — Agrega n caracteres de s2 a s1. 98 .. const char *orig) — Copia la cadena de caracteres apuntada por orig (incluyendo el car´cter terminador ’\0’) al vector apuntado por dest. int strncmp(const char *s1. igual o mayor que cero si se encuentra que s1 es. a o En general.h> Recordando la presentaci´n de arreglos hecha (cap´ o ıtulo 5) en donde las cadenas est´n definidas como un arreglo a de caracteres o un apuntador a una porci´n de memoria conteniendo caracteres ASCII. o Es importante preservar el caracter de terminaci´n NULL.. aparte de algunas funciones restringidas en longitud (strncat()... const char *s2.... char *strncat(char *s1. y la de destino. char *strerror(int errnum) — Devuelve un mensaje de error que corresponde a un n´mero de error. al menos que se creen cadenas a mano. Se deber´n usar las funciones para manejo de a a cadenas y no tratar de manipular las cadenas en forma manual desmantelando y ensamblando cadenas. strncmp() y strncpy()).

pero solamente para los primeros n caracteres. const char *s2) — Localiza la primera ocurrencia de la cadena s2 en la cadena s1. longitud). int longitud. char *s2. int longitud = 2. e Cero — si s1 y s2 son l´xicamente iguales. por ejemplo: o ıa char *s1 = "Hola".1. o un apuntador nulo si no hay un caracter de s2 a que exista en s1.CAP´ ITULO 17. ´ char *strstr(const char *s1. Realizan una tarea similar. int c) — Devuelve un puntero a la primera ocurrencia del car´cter c en la a cadena de caracteres s. por ejemplo: char *s1 = "Hola". const char *s2) — versi´n que ignora si son may´sculas o min´sculas de o u u strcmp(). El uso de muchas funciones es directo. MANEJO DE CADENAS <STRING. Observar que el caracter a de terminaci´n NULL podr´ ser violado cuando se usa estas funciones. strncasecmp(const char *s1. size t n) — Copia los primeros n caracteres de s2 a s1.1. int c) — Encuentra la ultima ocurrencia del caracter c en la cadena. char *s2 = 2. const char *s2. el cual es el arreglo destino. strncmp() y strncpy() son versiones m´s restringidas que sus contrapartes a m´s generales. /* s2 = "Ho" */ donde s2 no tiene el terminador NULL. char *strpbrk(const char *s1. . char *strrchr(const char *s. o La funci´n strcmp() compara lexicogr´ficamente las dos cadenas y regresa: o a Menor que cero — si s1 es l´xicamente menor que s2. size t n) — versi´n insensible a may´sculas o min´sculas o u u de strncmp() que compara los primeros n caracteres de s1. s1. B´squeda en cadenas u La biblioteca tambi´n proporciona varias funciones de b´squeda en cadenas.H> char *strncpy(const char *s1. e u char *strchr(const char *s.s1). 17. e Las funciones de copiado strncat(). 99 Observar que tanto strcat() y strcopy() regresan una copia de su primer argumento. Observar tambi´n que orden de los argumentos es arreglo destino seguido por arreglo fuente lo cual a veces e es una situaci´n para hacerlo incorrectamente. const char *s2. e Mayor que cero — si s1 es l´xicamente mayor que s2. longitud = strlen("Hola"). strcasecmp(const char *s1. /* long = 4 */ (void) strcpy(s2. const char *s2) — Regresa un apuntador a la primera ocurrencia en la cadena s1 de cualquier car´cter de la cadena s2. (void) strncpy(s2.

t1 = strtok(NULL. const char *s2) — Divide la cadena apuntada a s1 en una secuencia de “tokens”. resp = strstr(s1. resp = strchr(s1. si deseamos dividir la cadena s1 usando cada espacio e imprimir cada “token” en una nueva l´ ınea har´ ıamos lo siguiente: char s1[] = "Hola muchacho grande". la localidad de la primera o." "). En este caso.t1)."aeiou"). la o o posici´n es recordada y cualquir llamada subsecuente a strtok() iniciar´ en ´sa posici´n si en estas subsecuentes o a e o llamadas el primer argumento no es NULL. o o Se termina cuando t1 es NULL. for ( t1 = strtok(s1. resp apunta a la localidad s1 + 1. Se esta asignando tokens de s1 a t1 hasta la terminaci´n llamando a strtok() con el primer o argumento NULL."la"). Si s2 apunta a una cadena de longitud cero (esto es. char *t1.H> size t strspn(const char *s1. char *resp. char *strtok(char *s1. por ejemplo: char *s1 = "Hola". res = strpbrk(s1. Se emplea un ciclo for en una forma no regular de conteo: En la inicializaci´n se llama a la funci´n strtok() con la cadena s1. ´ size t strcspn(const char *s1. . la cadena ). Sin embargo. const char *s2) — Calcula la longitud del segmento inicial de s1 que consta unicamente de caracteres en s2. 100 Despu´s de la ejecuci´n. Si el primer argumento no es NULL o a o entonces la funci´n encuentra la posici´n de cualquiera de los caracteres del segundo argumento. e o La funci´n strpbrk() es una funci´n m´s general que busca la primera ocurrencia de cualquier grupo de caraco o a teres. por ejemplo: a char *s1 = "Hola". cada uno de ellos esta delimitado por uno o m´s caracteres de la cadena apuntada por s2. Por ejemplo: o char *s1 = "Hola". resp apunta a la localidad s1 + 2.CAP´ ITULO 17. a Las funciones strchr() y strrchr() son las m´s simples de usar. la cual tendr´ resp = s1 + 2. La funci´n strstr() regresa un apuntador a la cadena de b´squeda especificada o un apuntador nulo si la cadena o u no es encontrada.’l’). char *resp. " ") ) printf("%s\n". la funci´n regres s1. t1 != NULL. MANEJO DE CADENAS <STRING. Por ejemplo. a La funci´n strtok() es un poco m´s complicada en cuanto a operaci´n. char *resp. const char *s2) — Regresa el n´mero de caracteres al principio de s1 u que no coinciden con s2.

size t n) — Compara dos buffers. int tolower(int c) — Convierte la letra c a min´sculas. int isgraph(int c) — Verdad si c es un caracter imprimible.h>: void *memchr(void *s. int ifuente[TAM]. int c. /* Copia arreglo de enteros */ . void *memmove(void *dest.h> Finalmente se ver´ un resumen de algunas funciones b´sicas de memoria. TAM). Las funciones para conversi´n de caracteres son: o int toascii(int c) — Convierte c a ASCII o un unsigned char de 7 bits. void *memcpy(void *dest. dest[TAM]. borrando los bits altos. void *fuente. int isascii(int c) — Verdad si c es ASCII. int iscntrl(int c) — Verdad si c es un caracter de control. El uso de estas funciones es directo y parecido a las operaciones de comparaci´n de caracteres (excepto que la o longitud exacta (n) de todas las operaciones deber´ ser indicada ya que no hay una forma propia de terminaci´n).2. 17. size t n) — Pone todos los bytes de un buffer a un caracter dado. incluyendo el espacio en blanco. no son funciones estrica a tamente de cadenas. por lo que la funci´n sizeof() ayuda en estos o casos. exceptuando el espacio en blanco. Sin embargo. MANEJO DE CADENAS <STRING. memcpy(dest. fuente. por ejemplo: char fuente[TAM]. e int isalpha(int c) — Verdad si c es una letra.h> la cual contiene muchas funciones utiles para convertir y probar ´ caracteres individuales. no se dan ejemplos. u int isprint(int c) — Verdad si c es un caracter imprimible. int c. int memcmp(void *s1.h> o Una biblioteca relacionada #include <ctype. a o Observar que en todos los casos bytes de memoria son copiados. Prueba y conversi´n de caracteres <ctype. Las funciones m´s comunes para revisar caracteres tienen los siguientes prototipos: a int isalnum(int c) — Verdad si c es alfanum´rico.TAM*sizeof(int)). int islower(int c) — Verdad si c es una letra min´scula. /* Copia chars (bytes) OK */ memcpy(idest. void *fuente. size t n) — Busca un caracter en un buffer. int ispunct(int c) — Verdad si c es un signo de puntuaci´n. size t n) — Mueve un n´mero de bytes de un buffer a otro.CAP´ ITULO 17.ifuente.idest[TAM]. o int isspace(int c) — Verdad si c es un espacio int isupper(int c) — Verdad si c es una letra may´scula. void *s2. si es posible.H> 101 17. u void *memset(void *s. pero tienen su prototipo en #include <string. Operaciones con la memoria <memory. u El uso de estas funciones es directo y por lo tanto.3. si es posible. int isdigit(int c) — Verdad si c es un d´ ıgito decimal. u int toupper(int c) — Convierte la letra c a may´sculas. size t n) — Copia un buffer dentro de otro. u int isxdigit(int c) — Verdad si c es un d´ ıgito hexadecimal.

3.H> 102 La funci´n memmove() se comporta de la misma forma que memcpy() excepto que las localidades de la fuente y o el destino podr´ traslaparse. Escribir un programa que invierta el contenido de la memoria en bytes. Escribir una funci´n que convierta todos los caracteres de una cadena a may´sculas. si una cadena de entrada es un pal´ o ındromo. o de derecha a izquierda. Ejercicios 1. ıan La funci´n memcmp() es similar a strcmp() excepto que unsigned bytes son comparados y se devuelve cero si s1 o es menor que s2. ANA. . Sugerir una posible implementaci´n de la funci´n strtok(): o o a) usando otras funciones de manejo de cadenas. etc. si se tienen n bytes. Por ejemplo. 2. MANEJO DE CADENAS <STRING. Escribir una funci´n similar a strlen que pueda manejar cadenas sin terminador. etc. ¿C´mo se logra el almacenamiento de una cadena separada? o 4.CAP´ ITULO 17. Un pal´ ındromo es una palabra que se lee igual de izquierda a derecha. b) desde los principios de apuntadores. Escribir una funci´n que regrese verdad. Es decir. Tip: se necesitar´ conocer y o a pasar la longitud de la cadena. el byte n-1 se intercambia con el byte 1. 17. o u 5.4. el byte de memoria n se invierte con el byte 0.

argv[0]). ıas son ejemplos que han sido revisados ya en el sistema operativo.Cap´ ıtulo 18 Acceso de Archivos y llamadas al sistema de directorios Existen muchas utiler´ que permiten manipular directorios y archivos. Se tiene a continuaci´n la emulaci´n del comando cd de Unix con C: o o #include <stdio. 18.h> #include <unistd. Se revisar´ en este cap´ a ıtulo como hacer las mismas tareas dentro de un programa en C. if (argc < 2) { printf("Uso: %s <directorio> \n". La funci´n a o devuelve un apuntador a la cadena o NULL si un error ocurre. es decir. mkdir. } if (chdir(argv[1]) != 0) { 103 . rm. Funciones para el manejo de directorios <unistd.h> Para realizarlo se requiere llamar las funciones apropiadas para recorrer la jerarqu´ de directorios o preguntar ıa sobre los contenidos de los directorios. char *getcwd(char *path.path es un apuntador a una cadena donde la trayectoria ser´ regresada. int chdir(char *path) — cambia al directorio indicado en la cadena path. cp.h> #define TAM 80 main(int argc. char **argv) { char cadena[TAM]. size t tam) — Obtiene la trayectoria completa. etc. desde la ra´ del ız directorio actual. Los comandos cd. exit(1). ls.1.

llamando select() en cada entrada de directorio. y puestas en la matriz listanomb. Puede ser usada como funci´n de comparaci´n para que la funci´n scandir() ponga las entradas de directorio o o o en orden alfab´tico. char trayectoria[MAXPATHLEN]. main() { int contar. Las entradas para las que o select() devuelve un valor distinto de cero se almacenan en cadenas que se asignan de la memoria con malloc().TAM). int (*compar)(const struct dirent **. if ( getwd(trayectoria) == NULL ) .1.argv[0]). ACCESO DE ARCHIVOS Y LLAMADAS AL SISTEMA DE DIRECTORIOS printf("Error en %s.sys/dir.h> <sys/param. Si select o o es NULL. struct dirent **archivos.CAP´ ITULO 18. int (*select)(const struct dirent *). } getcwd(cadena. Busqueda y ordenamiento de directorios: sys/types. const struct dirent **) Esta funci´n rastrea el directorio dir. e A continuaci´n se tiene una versi´n simple del comando de UNIX ls o o #include #include #include #include <dirent. 104 } 18.1.cadena).i.h. int alphasort(const struct dirent **a. const struct dirent **b). int selecc_arch().h> <unistd. ordenadas usando qsort() con la funci´n de comparaci´n compar(). struct dirent **listanomb.\n". se seleccionan todas las entradas.h Dos funciones utiles (en plataformas BSD y aplicaciones no multi-thread) est´n disponibles: ´ a int scandir(const char *dir.h> #define FALSO 0 #define VERDADERO !FALSO extern int alphasort(). printf("El directorio actual es %s\n". exit(1).h> <stdio.

/* flush buffer */ } int selecc_arch(struct dirent *entry) { if ((strcmp(entry->d_name.CAP´ ITULO 18..") == 0)) return (FALSO). if ((strcmp(entry->d_name.trayectoria). ++i) printf("%s ". } 105 scandir regresa los pseudodirectorios (.") == 0) || (strcmp(entry->d_name. ’.") == 0)) return (FALSO).h MAXPATHLEN esta definida en sys/param. exit(0). /* Probar que tenga un punto */ if ( (ptr != NULL ) && ( (strcmp(ptr. } printf("Directorio de trabajo actual: %s\n". else return (VERDADERO).archivos[i]->d_name).contar). contar = scandir(trayectoria. ". ". o Se puede ir m´s lejos que lo anterior y buscar por archivos particulares. selecc_arch. En este ejemplo se filtran los e pseudodirectorios para que no se muestren haciendo que la funci´n regrese FALSO.. exit(0). o Los prototipos de las funciones scandir y alphasort tienen definiciones en la cabecera dirent.h.")== 0) || (strcmp(entry->d_name. /* Si no se encontraron archivos */ if (contar <= 0) { printf("No hay archivos en este direntorio\n"). } printf("Hay %d archivos. (. ACCESO DE ARCHIVOS Y LLAMADAS AL SISTEMA DE DIRECTORIOS { printf("Error obteniendo la trayectoria actual\n").h y .o. ".) y tambi´n todos los archivos. for (i=0. i<contar.).c") == 0) .\n". alphasort). ".h y la funci´n getwd() en unistd.. &archivos. . o o int selecc_arch(struct dirent *entry) { char *ptr. printf("\n").c. a Reescribiendo la funci´n selecc_arch para que s´lo muestre los archivos con los sufijos . /* Revisar las extensiones de los archivos */ ptr = rindex(entry->d_name. ".’).

CAP´ ITULO 18. que esta definido en #include <unistd. mode_t modo). } 106 La funci´n rindex() es una funci´n para manejo de cadenas que regresa un apuntador a la ultima ocurrencia del o o ´ caracter c en la cadena s. else return(FALSO). o un apuntador NULL si c no ocurre en la cadena.h>. Estado de un archivo Se tienen dos funciones utiles para conocer el estado actual de un archivo.h o Existen varias llamadas al sistema que pueden ser aplicadas directamente a los archivos guardados en un directorio.o") == 0) ) ) return (VERDADERO). ". de acuero con modo.h. Rutinas de manipulaci´n de archivos: unistd. ACCESO DE ARCHIVOS Y LLAMADAS AL SISTEMA DE DIRECTORIOS || (strcmp(ptr.) 18.h. Esta es una variable especial del sistema. etc (ver la definici´n de la estructura abajo). cuando fue creado (st ctime). (index() es una funci´n similar pero o asigna un apuntador a la primera ocurrencia. o -1 en caso de falla y a errno se le asigna un valor adecuado. Por ejemplo se puede sabe que tan ´ grande es un archivo (st size). La funci´n int chmod(const char *trayectoria. ".1. o u F OK –prueba si se permite la comprobaci´n de la existencia del fichero. Esta puede ser manualmente puesto dentro de un programa en C. X OK –prueba el permiso de ejecuci´n o b´squeda. a errno Algunas llamadas al sistema (y algunas funciones de biblioteca) dan un valor al entero errno para indicar que ha habido un error. Permisos de accesos a archivos La funci´n int access(const char *trayectoria.2. de otra forma simplemente retiene el ultimo ´ valor. chmod() devuelve 0 en caso de ´xito y -1 en caso de error adem´s se asigna a la variable errno un valor adecuado e a (revisar las p´ginas de man para ver los tipos de errores) a 18. sys/stat.h") == 0) || (strcmp(ptr. cambia el modo del archivo dado mediante o trayectoria para un modo dado.2.2.2. los cuales pueden ser: R OK –prueba el permiso de lectura.h> . debe ser declarado de la siguiente forma: extern int errno. Para usar errno en un programa de C. sys/types. Las dos o funciones tienen sus prototipos en <sys/stat. o e Ver las p´ginas del man para ver la lista de errores. int modo). W OK –prueba el permiso de escritura. o La funci´n access() regresa: 0 si ha habido ´xito. 18. — determina los permisos de usuario para un o fichero.

-1 si hubo error y errno es actualizado apropiadamente. time_t st_mtime.h> main(int argc. pero todos los directorios o listados en nomb_arch deber´n estar disponibles. en bytes */ n tama~o de bloque para el n sistema de ficheros de E/S */ numero de bloques asignados */ hora ultimo acceso */ hora ultima modificacion */ hora ultimo cambio */ Se muestra un ejemplo que hace uso de la funci´n stat: o #include <stdio. como sigue: struct stat { dev_t ino_t mode_t nlink_t uid_t gid_t dev_t st_dev.h> #include <sys/stat. struct stat *buf) — obtiene informaci´n acerca del archivo apuntado o por nomb_arch. time_t st_atime. char **argv) { struct stat buf. ACCESO DE ARCHIVOS Y LLAMADAS AL SISTEMA DE DIRECTORIOS int stat(const char *nomb arch. if ( stat(argv[0]. printf("%s\n". /* unsigned long st_blksize.h>. st_nlink. st_mode. &buf) == -1 ) { perror(argv[0]). 107 Las funciones stat() y fstat() regresan 0 en caso ´xito. e buf es un apuntador a la estructura stat en la cual la informaci´n es colocada.argv[0]). escritura o ejecuci´n. No se requieren permisos de lectura. }. time_t st_ctime. st_uid. st_rdev. /* unsigned long st_blocks. struct stat *buf) — obtiene la misma informaci´n que el anterior. exit(-1).CAP´ ITULO 18. st_ino. a int fstat(int desarch.h> /* Para la estructura stat */ #include <unistd. /* /* /* /* /* /* /* off_t st_size. st_gid. La estructura stat esta definida o en include <sys/stat. /* /* /* /* dispositivo */ inodo */ proteccion */ numero de enlaces fisicos */ ID del usuario propietario */ ID del grupo propietario */ tipo dispositivo (si es dispositivo inodo) */ tama~o total. } else . pero s´lo el o o archivo abierto apuntado por desarch (tal y como lo devuelve open()) es examinado en lugar de nomb_arch.

Existen dos funciones o convenientes (adem´s de algunas variantes) para la realizaci´n de la tarea mencionada. el fichero es borrado y el ´ u espacio que ocupaba vuelve a estar disponible. Quiz´s la forma m´s com´n es usando las funciones a a u de stdio. const char *nuevo). Dos llamadas al sistema (definidas en unistd. Si dicho nombre era el ultimo enlace a un archivo.3. o ıa 4.h) las cuales son actualmente usadas por las funciones remove() y rename() tambi´n existen.h> o Los programas con frecuencia necesitan crear archivos s´lo durante la vida de un programa. unistd. Ejercicios 1. ACCESO DE ARCHIVOS Y LLAMADAS AL SISTEMA DE DIRECTORIOS { printf("Tama~o del archivo %s %d bytes. parando cada 20 l´ ıneas hasta que una tecla sea presionada (una versi´n simple de la utiler´ more de UNIX).\n".st_size). pero son probablemente m´s d´ e a ıficiles de recordar. Escribir un programa para mostrar las l´ ıneas de un archivo que contenga una palabra dada como argumento al programa. -1 si hubo error y pone la variable errno con alg´n e u valor indicando el tipo de error.argv[0]. etc.buf. El archivo se borrar´ autom´ticamente cuando el programa termine.h: int remove(const char *pathname). El manejo (borrado de a o archivos.CAP´ ITULO 18. o 3.2. — crea un nuevo enlace (tambi´n conocido como e enlace f´ ısico a un archivo existente. Las funciones stat() y fstat() regresan 0 en caso ´xito. Esribir un programa para mostrar una lista de archivos dados como argumentos.2.) es tomado con cuidado por el sistema operativo. 18. a a La funci´n char *tmpnam(char *s). n 2. -1 en caso de error y pone e un valor en errno para indicarlo. La funci´n FILE *tmpfile (void).4. Escribir un programa en C para simular el comando ls -l de UNIX que muestre todos los archivos del directorio actual. Creaci´n de archivos temporales: <stdio. int rename(const char *viejo. int link(const char *oldpath. etc. Regresa 0 en caso de ´xito. Manipulaci´n de archivos: stdio. tama˜o. sus permisos. Escribir un programa que liste todos los archivos del directorio actual y todos los archivos en los subsecuentes directorios.3.h o Existen algunas funciones para borrar y renombrar archivos. crea un nombre unico para un archivo temporal usando el prefijo de trao ´ yectora P_tmpdir definido en <stdio. al menos que se este suficientemente familiarizado con UNIX. n } } 108 18. crea un archivo temporal (en modo de lectura/escritura binaria) y abre el o correspondiente flujo. y ning´n proceso tiene el archivo abierto. — borra un nombre del sistema de archivos. 18.h. una versi´n sencilla de la utilidad grep de UNIX.h>. int unlink(const char *pathname). . es decir. const char *newpath).

*/ Tiempo local medido en minutos oeste de GMT. y entonces llama a asctime() para convertir la estructura tm a una cadena. }. a a El uso m´s com´n de las funciones de tiempo son: a u conocer el tiempo. el valor devuelto tambi´n se guarda en e la zona de memoria a la que apunta t.h> como sigue: struct timeb { time_t time.Cap´ ıtulo 19 Funciones para el tiempo En este cap´ ıtulo se revisar´ como se puede usar el reloj con llamadas al sistema UNIX.*/ No cero si se usa horario de verano */ En caso de ´xito. En caso de error. que esta declarada en <sys/timeb. e e se devuelve ((time_t)-1) y se asigna a la variable errno un valor apropiado.1. short int dstflag. En primer lugar descompone los segundos a una estructura tm llamando a o localtime(). se devuelve el tiempo transcurrido en segundos desde la ´poca. /* /* /* /* Segundos desde epoca. 19. Esta medida se llama el “tiempo de calendario”. igual que ‘time’. a Existen muchas funciones para el tiempo que las que ser´n consideradas aqu´ — usar las p´ginas de man y los a ı a listados de la biblioteca est´ndar de funciones para m´s detalles. 0 minutos. tomar el tiempo a programas y funciones y poner valores a semillas. tiempo universal coordinado (GMT) del 1 de enero de 1970. 109 . medido en segundos desde “la ´poca” 0 e o horas. ftime(struct timeb *pt) — devuelve la hora y la fecha actuales en pt. unsigned short int millitm. En caso de error. se devuelve ((time_t)-1) y se asigna a la variable errno un valor apropiado. char *ctime(time t *timep) — toma un argumento de tipo time_t (long integer) que representa el tiempo de calendadrio y lo convierte a una cadena de 26 caracteres de la forma producida por la funci´n asctime(). Funciones b´sicas para el tiempo a Algunas prototipos de las funciones b´sicas para el tiempo son las siguientes: a time t time(time t *t) — devuelve el tiempo transcurrido. short int timezone. Si t no es nulo. */ millisegundos adicionales. 0 segundos.

FUNCIONES PARA EL TIEMPO char *asctime(const struct tm *timeptr) — convierte un valor de tiempo contenido en una estructura tm a una cadena de 26 caracteres de la forma: ddd mmm dd hh:mm:ss aaaa La funci´n asctime() regresa un apuntador a la cadena.2. j++).h> main() { int i. i<=300. (void) time(&t1).i. j<1000000. en esta ocasi´n se usa la funci´n lrand48() para generar una o o secuencia de n´meros: u /* random. i*i.h> #include <sys/types.j. u a Este es un ejemplo sencillo que ilustra las llamadas a la funci´n tiempo en distintos momentos: o /* timer. ++i) { printf("%d %d %d\n". u Se ha visto un ejemplo similar previamente. time_t t1. b´sicamente existen tres posibles usos de las funciones de tiempo.h> #include <time. } 19.2. for(j=0.CAP´ ITULO 19. Ejemplo 1: Tiempo (en segundos) para hacer alg´n c´lculo. o 110 19. Ejemplos de aplicaciones de funciones del tiempo.c */ #include <stdio.t2. Ejemplo 2: Inicializar la semilla de un n´mero aleatorio.h> #include <time.1. a 19. for (i=1.h> main() { int i.h> #include <sys/types. /* Un peque~o retardo */ n } (void) time(&t2). (int) t2-t1). Como se menciono previamente. printf("\n Tiempo para hacer 300 cuadrados y cubos = %d segundos\n".c */ #include <stdio. . i*i*i ).2.2.

1. srand48((long) t1). o Una funci´n similar es la funci´n drand48 que regresa n´meros de doble precisi´n en el rango [0.(int) t1). Ejercicios 1. o o u o srand48() pone el valor de la semilla para estos generadores de n´meros aleatorios. donde n es cualquier valor flotante. o 2. Escribir un programa en C para producir una serie de n´meros aleatorios de punto flotante en los rangos a) u 0. for (i=0. La semilla deber´ estar puesta para que se garantice a una secuencia unica.0 . lrand48()). ´ .++i) printf("%d ". Es importante tener diferentes u semillas cuando se llame a las funciones de otra forma el mismo conjunto n´meros pseudo-aleatorios sera generados.0 . FUNCIONES PARA EL TIEMPO time_t t1. printf("\n\n").0. u La funci´n time() siempre da una semilla unica (siempre y cuando haya transcurrido por lo menos 1 segundo). /* usar time en segundos para poner la semilla */ printf("5 numeros aleatorios (semilla = %d):\n". o ´ 19. Escribir un programa en C que mida el tiempo de un fragmento de c´digo en milisegundos.0. } 111 La funci´n lrand48() devuelve enteros largos no negativos distribuidos uniformememnte entre 0 y 2 31. (void) time(&t1).1.n.CAP´ ITULO 19.3.i<5.0). b) 0.

La funci´n regresa el status de salida del shell. NOTA: se puede ahorrar bastante tiempo y confusi´n en vez de ejecutar otros programas. int system(char *mandato) — donde mandato puede ser el nombre de una utiler´ de UNIX. wait() y fork() o (las cuales tienen su prototipo en <unistd. Ejecutando comandos de UNIX desde C Se pueden ejecutar comandos desde un programa de C como si se estuviera en la l´ ınea de comandos de UNIX usando la funci´n system(). u ´ El comando ps de UNIX lista todos los procesos que se est´n ejecutando en la m´quina.h>.2. o a La funci´n execl realiza la ejecuci´n (execute) y sale (leave). a a e La funci´n de C int getpid() regresar´ el pid de un proceso que llame a esta funci´n. execl() La funci´n execl tiene otras 5 funciones relacionadas — ver las p´ginas de man. 20.Cap´ ıtulo 20 Control de procesos: <stdlib. Cuando UNIX ejecuta un proceso le asigna a cad proceso un n´mero unico — process ID o pid. 20. o o scripts. Esta definida como: 112 . La funci´n tiene su prototipo en <stdlib. etc. un shell ejecutable ıa o un programa del usuario. system("ls -l").1. Sin embargo despues se ver´ como se puede hacer que un o a programa corra como varios procesos separados comunicados. csh. listando tambi´n el pid. <unistd. para hacer las tareas.h>). un programa iniciado por el usuario (gimp o alguno hecho por el usuario). update. login.h> Un proceso es b´sicamente un unico programa ejecut´ndose. es decir que un proceso ser´ ejecutado y entonces o o a terminado por execl. o a o Un programa usualmente ejecuta un s´lo proceso. etc). Este podr´ ser un programa del ”sistema´´(por a ´ a ıa ejemplo.h> o o Ejemplo: llamada del comando ls desde un programa main() { printf("Archivos en el directorio son:\n"). } La funci´n system es una llamada que esta construida de otras 3 llamadas del sistema: execl().

.. <UNISTD. y no se crea un proceso hijo. printf("Esta linea no es impresa\n"). a ´ El siguiente programa ilustra un ejemplo sencillo del uso de fork. valor_regr). ´ a o . En caso de falla.. El terminador nulo hace lo anterior. donde dos copias son hechas y se ejecutan juntas (multitarea). Como la lista de argumentos sea variable ´ a a se debe indicar de alguna forma a C cuando se termine la lista. pone un valor a errno. Este es un terminador Nulo. printf("Bifurcando el proceso\n").CAP´ ITULO 20."ls"."-l". argn son apuntadores a los argumentos para el comando y el 0 solamente indica el fin de la lista de argumentos variables. En caso de ´xito. el cual ser´ diferente en cada ejecuci´n. 0).h> main() { printf("Los archivos en el directorio son:\n"). getpid().0). printf("El id del proceso es %d y el valor regresado es %d\n".H>."ls". fork() regresa −1 al proceso padre. } La salida podr´ ser como la siguiente: ıa Bifurcando el proceso El id del proceso es 2662 y el valor regresado es 2663 El id del proceso es 2663 y el valor regresado es 0 Los procesos tienen un unico identificador. CONTROL DE PROCESOS: <STDLIB. const char *arg0.. char *argn. execl("/bin/ls". El apuntador camino indica el nombre de un archivo que contiene el comando que ser´ ejecutado.3. 113 El ultimo par´metro deber´ ser siempre 0. Por lo tanto nuestro ejemplo queda de la siguiente forma: #include <unistd. printf("<<< Esto no se ejecuta !!!\n"). arg0 apunta a a una cadena que es el mismo nombre (o al menos su ultimo componente). El proceso hijo tendr´ su propio identificador unico (PID). execl("/bin/ls"."-l". fork() La funci´n int fork() cambia un proceso unico en 2 procesos id´nticos. main() { int valor_regr=0.H> execl (const char *camino.0). . } 20. . .. conocidos como el padre (parent) y el o ´ e hijo (child). fork() regresa 0 al proceso hijo y regresa el identificador del proceso hijo al proceso e padre. ´ arg1. valor_regr=fork().

/* identificador del proceso */ pid = fork(). Se a puede f´cilmente derivar su propia convenci´n.. exit(1).. } if ( pid == 0 ) { /* Proceso hijo */ . exit() La funci´n void exit(int status) termina el proceso que llama a esta funci´n y regresa en la salida el valor o o de status. } else { /* Proceso padre */ . CONTROL DE PROCESOS: <STDLIB. } 20. un estado de 0 significa terminaci´n normal y cualquier otro indica un error o un evento no o o usual. execvp es llamada para ejecutar estos comandos en un proceso hijo generado por fork() ...c . a Se pueden atrapar cualquier error. El estado de la salida del hijo es o regresado en status. Muchas llamadas de la biblioteca est´ndar tienen errores definidos en la cabecera de archivo sys/stat. Tanto UNIX y los programas bifurcados de C pueden leer el valor de status. etc Cada comando y sus correspondientes argumentos son puestos en un arreglo de argumentos.h..CAP´ ITULO 20.5. wait() La funci´n int wait() (int *status) forzar´ a un proceso padre para que espere a un proceso hijo que se o a detenga o termine. por ejemplo: int pid.ejemplo de un programa de bifurcaci´n o El programa pide el ingreso de comandos de UNIX que son dejados en una cadena.4... if ( pid < 0 ) { printf("<< No se pudo duplicar !!\n"). a o Un ejemplo completo de un programa de bifurcaci´n se muestra a continuaci´n: o o /* fork. La cadena es entonces "analizada" encontrando blancos. Por convenci´n. Cuando un proceso es duplicado en 2 procesos o a se puede detectar f´cilmente (en cada proceso) si el proceso es el hijo o el padre ya que fork() regresa 0 para el hijo. <UNISTD.H> 114 Es imposible indicar con antelaci´n cual proceso obtendr´ el CPU.. La funci´n regresa el PID del hijo o −1 en caso de errror..H>.. ya que fork() regresa un −1. 20.

*/ ejecutar(args).h> main() { char buf[1024]. exit(0). } } /* * parse--divide el comando que esta en buf * en argumentos.CAP´ ITULO 20.h> #include <sys/types. if (gets(buf) == NULL) { printf("\n").H> */ #include <stdio.) { /* * Pide y lee un comando. para que * el argumento previo sea terminado 115 . CONTROL DE PROCESOS: <STDLIB. } /* * Dividir la cadena en argumentos. Usar nulos. for (. <UNISTD. char *args[64]. */ parse(char *buf. /* * Ejecutar el comando. */ parse(buf.. char **args) { while (*buf != (char) NULL) { /* * Quitar blancos.h> #include <unistd. */ printf("Comando: "). args).H>.

CAP´ ITULO 20. CONTROL DE PROCESOS: <STDLIB.H>, <UNISTD.H> * automaticamente. */ while ( (*buf == ’ ’) || (*buf == ’\t’) ) *buf++ = (char) NULL; /* * Guardar los argumentos */ *args++ = buf; /* * Brincar sobre los argumentos. */ while ((*buf != (char) NULL) && (*buf != ’ ’) && (*buf != ’\t’)) buf++; } *args = (char) NULL; }

116

/* * ejecutar--genera un proceso hijo y ejecuta * el programa. */ ejecutar(char **args) { int pid, status; /* * Obtener un proceso hijo. */ if ( (pid = fork()) < 0 ) { perror("fork"); exit(1); /* NOTA: perror() genera un mensaje de error breve en la * salida de errores describiendo el ultimo error encontrado * durante una llamada al sistema o funcion de la biblioteca. */ } /* * El proceso hijo ejecuta el codigo dentro del if. */ if (pid == 0)

CAP´ ITULO 20. CONTROL DE PROCESOS: <STDLIB.H>, <UNISTD.H> { execvp(*args, args); perror(*args); exit(1); /* NOTA: las versiones execv() y execvp() de execl() son utiles cuando el numero de argumentos es desconocido previamente. Los argumentos para execv() y execvp() son el nombre del archivo que sera ejecutado y un vector de cadenas que contienen los argumentos. El ultimo argumento de cadema debera ser un apuntador a 0 (NULL) execlp() y execvp() son llamados con los mismos argumentos que execl() y execv(), pero duplican las acciones del shell en la busqueda de un archivo ejecutable en un lista de directorios. La lista de directorios es obtenida del ambiente. */ } /* * El padre ejecuta el wait. */ while (wait(&status) != pid) /* vacio */ ; }

117

20.6.

Ejercicios

1. Crear un programa en C en donde se use la funci´n popen() para entubar la salida del comando rwho de UNIX o en el comando more de UNIX.

Cap´ ıtulo 21

Compilaci´n de Programas con Archivos o M´ ltiples u
En este cap´ ıtulo se revisa los aspectos teoricos y pr´cticos que necesitan ser considerados cuando son escritos a programas grandes. Cuando se escriben programas grandes se deber´ programar en m´dulos. Estos ser´n archivos fuentes separados. a o a La funci´n main() deber´ estar en un archivo, por ejemplo en main.c, y los otros archivos tendr´n otras funciones. o a a Se puede crear una biblioteca propia de funciones escribiendo una suite de subrutinas en uno o m´s m´dulos. De a o hecho los m´dulos pueden ser compartidos entre muchos programas simplemente incluyendo los m´dulos al compilar o o como se ver´ a continuaci´n. a o Se tiene varias ventajas si los programas son escritos de esta forma: los m´dulos de forma natural se dividir´n en grupos comunes de funciones. o a se puede compilar cada m´dulos separadamente y ligarlo con los m´dulos ya compilados. o o las utiler´ tales como make nos ayudan a mantener sistemas grandes. ıas

21.1.

Archivos Cabezera

Si se adopta el modelo modular entonces se querr´ tener para cada m´dulo las definiciones de las variables, los a o prototipos de las funciones, etc. Sin embargo, ¿qu´ sucede si varios m´dulos necesitan compartir tales definiciones? e o En tal caso, lo mejor es centralizar las definiciones en un archivo, y compartir el archivo entre los m´dulos. Tal o archivo es usualmente llamado un archivo cabecera. Por convenci´n estos archivos tienen el sufijo .h o Se han revisado ya algunos archivos cabecera de la biblioteca est´ndar, por ejemplo: a #include <stdio.h> Se pueden definir los propios archivos cabecera y se pueden incluir en el programa como se muestra enseguida: #include "mi_cabecera.h" Los archivos cabecera por lo general s´lo contienen definiciones de tipos de datos, prototipos de funciones y o comandos del preprocesador de C. Considerar el siguiente programa de ejemplo: main.c 118

/* * Llamar a EscribirMiCadena() . COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES 119 /* * main. EstaCadena).h */ #define MI_CADENA "Hola Mundo" void EscribirMiCadena(). printf("Variable Global = %s\n".h" #include <stdio. { printf("%s\n". } EscribirMiCadena. printf("Terminado.c */ extern char *Otra_cadena. void EscribirMiCadena(EstaCadena) char *EstaCadena. Cada m´dulo ser´ compilado separadamente como se ver´ m´s adelante.c */ #include "cabecera. main() { printf("Ejecutando.´ ´ CAP´ ITULO 21.h /* * cabecera. o a a a ...c /* * EscribirMiCadena.h> char *Otra_cadena = "Hola a Todos". } cabecera.definida en otro archivo */ EscribirMiCadena(MI_CADENA).\n"). Otra_cadena).\n").

} La funci´n main() no puede ver a las variables que_alcance o fin_de_alcance.c o El prototipo void de la funci´n EscribirMiCadena esta definida en cabecera.2. es decir. a o Un problema que se tiene al emplear m´dulos son el compartir variables. La funci´n main..c llama a la funci´n e a o o EscribirMiCadena() la cual esta en el m´dulo (archivo) EscribirMiCadena. 21.. En el lenguaje C. Considerar el siguiente c´digo: o main() { ..1. void que_global() { . Si se tienen variables globales declaradas o y son instanciadas en un m´dulo.h" ya que comparten definiciones o comunes. 21. Las variables “externas” est´n definidas fuera de las funciones — se encuentran potencialmente disponibles a todo a el programa (globales) pero NO necesariamente. float fn() { . Algunos como main. Solamente la funci´n fn() puede ver a solitaria. COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES 120 Algunos m´dulos tienen la directiva de preprocesamiento #include "cabecera. arreglos y estructuras muy grandes son dif´ ıciles de guardar localmente — problemas de memoria con el stack. NO se pueden tener declaraciones de funciones anidadas como en PASCAL. ¿c´mo pueden ser pasadas a otros m´dulos para que sean conocidas? o o o Se podr´ pasar los valores como par´metros a las funciones. pero: ıan a puede ser esto laborioso si se pasan los mismos par´metros a muchas funciones o si la lista de argumentos es a muy larga. con la consecuencia de mantener muchos archivos cabecera y tener programas de tama˜o n moderado con uno o dos archivos cabecera (probablemente lo mejor) que compartan m´s definiciones de m´dulos... o Observar que en general se debe decidir entre tener un m´dulo . Variables y Funciones Externas Las variables y argumentos definidos dentro de las funciones son “internas”.2.´ ´ CAP´ ITULO 21. float fin_de_alcance[10]. Alcance de las variables externas Una variable externa (o funci´n) no es siempre totalmente global. } char solitaria. o Esta es tambi´n una de las razones por las que se deben poner los prototipos de las funciones antes del cuerpo e del c´digo. o . todas las definiciones de funciones son externas..h.c tambi´n incluyen archivos cabecera est´ndar. Las variables externas son siempre permanentes.c que tenga acceso solamente a la informaci´n que o o necesita para su trabajo. pero las funciones que_global() o y fn() si pueden. En el lenguaje C se aplica la siguiente regla: o El alcance de una variable externa (o funci´n) inicia en el punto de declaraci´n hasta el fin del archivo (m´dulo) o o o donde fue declarada. locales. } int que_alcance.

´ ´ CAP´ ITULO 21.4. Puede ser usado un estilo orientado a objetos. por ejemplo: o extern int que_global. cada archivo contendr´ una o m´s funciones. Cada archivo define un tipo particular de objeto como un tipo de dato y las operaciones en ese objeto como funciones. 21.c arch. o a Cuando los cambios son hechos a un archivo. Regresando al ejemplo de programaci´n modular. pero fn() si sabe acerca de la funci´n que_global(). a Los archivos pueden contener todas las funciones de un grupo relacionado. por ejemplo: main. extern int arr[]. Cualquier otra funci´n de o o bajo nivel usada en la implemantaci´n puede ser guardada en el mismo archivo. esto es. Estos otros archivos a o a a pueden ser tratados como funciones de una biblioteca. La implementaci´n del objeto puede mantenerse privado al o resto del programa. Estas pueden ser accesadas como una funci´n de una biblioteca.3. La otra raz´n por la cual se usan los prototipos de las funciones es para revisar los par´metros que ser´n pasados o a a a las funciones. Ventajas de Usar Varios Archivos Las ventajas principales de dispersar un programa en varios archivos son: Equipos de programadores pueden trabajar en el programa. pero no son necesarios en las declaraciones n a o externas. La o a funci´n que_global() no sabe nada acerca de la funci´n fn(). o o o ya que esta aparece declarada previamente. se tiene una arreglo de caracteres tipo global Otra_cadena o declarado en main. Con lo anterior se logran programas bien estructurados los cuales son f´ciles de mantener. La variable actual s´lo deber´ estar definida una vez en todo el programa — o a se pueden tener tantas declaraciones externas como se requieran. Un archivo a a incluir´ la funci´n main() mientras los otros contendr´n funciones que ser´n llamados por otros. o Objetos bien implementados o definiciones de funciones pueden ser reusadas en otros programas. La utiler´ make de UNIX es muy util para reconstruir programas con varios archivos. NO o una definici´n. la variable debe ser declarada como una variable externa. Si se requiere hacer referencia a una variable externa antes de que sea declarada o que esta definida en otro m´dulo.c int arr[100]. Como dividir un programa en varios archivos Cuando un programa es separado en varios archivos. Se debe tener cuidado con el especificador de almacenamiento de clase ya que el prefijo es una declaraci´n. ıa ´ 21. cada programador trabaja en un archivo diferente. solamente ese archivo necesita ser recompilado para reconstruir el programa.c y que esta compartido con EscribirMiCadena donde esta declarada como externa. . no se da almacenamiento en la memoria para una variable externa — solamente le dice al o compilador la propiedad de la variable. por lo que los programadores o que llamen a la funci´n principal no se distraer´n por el trabajo de bajo nivel. Los tama˜os de los arreglos deber´n ser dados dentro de la declaraci´n. por ejemplo todas las operaciones con matrices. COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES 121 Por lo que en el ejemplo la funci´n main no conocer´ nada acerca de las funciones que_global() y fn(). con lo que se reduce el tiempo de desarrollo. En programas muy grandes cada funci´n principal puede ocupar un propio archivo.

o funciones que regresasan valores.´ ´ CAP´ ITULO 21. Organizaci´n de los Datos en cada Archivo o Cualquier archivo deber´ tener sus datos organizados en un cierto orden. o Si la funci´n es llamada desde un archivo donde no esta definida.h 21. Las variables globales podr´ estar inicializadas aqu´ o ıan ı. a . La mejor soluci´n a este problema es escribir un archivo cabecera para cada archivo de C. pero terminar´n en .h. ´ o El archivo contiene la definici´n de un objeto. Declaraci´n de variables globales y externas. Las ventajas de lo anterior son: e El objeto puede ser f´cilmente reusado en otros programas. El prototipo puede aparecer entre las variables globales en el inicio del archivo fuente.5. COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES 122 Los programadores usualmente inician dise˜ando un programa dividiendo el problema en secciones m´s f´cilmente n a a manejables. Una o m´s funciones. Los ultimos cambios al objeto requieren solamente la modificaci´n de un archivo. un prototipo deber´ aparecer antes que o a llamada de la funci´n. una declaraci´n completa de la funci´n o o o puede ser colocada delante de cualquier llamada de la funci´n. a El orden anterior es importante ya que cada objeto deber´ estar definido antes de que pueda ser usado. o Una funci´n definida como: o float enc_max(float a. estos tendr´n el mismo o a nombre que el archivo de C. no ser´ posible o e a compilar correctamente. float c). tip´ a ıcamente podr´ ser la siguiente: a Un pre´mbulo consistente de las definiciones de constantes (#define). float c) { . cabeceras de archivos (#include) y los a tipos de datos importantes (typedef). se puede definir la funci´n usando o o o la directiva #include con el archivo apropiado . El archivo cabecera contiene las definiciones de todas las funciones a usadas en el archivo de C. es usual tener todas las funciones o que accesan ´se objeto en el mismo archivo. o a o Cuando se hace una implementaci´n tipo objeto de las estructuras de datos. Cuando una funci´n en otro archivo llame una funci´n de nuestro archivo de C. float b.. al menos que la definici´n de las funciones est´n en el archivo. a Todas las funciones relacionadas estan guardadas juntas. Alternativamente puede ser declarado en el archivo cabecera el cual es le´ usando la directiva #include. Todas las funciones a a de cada secci´n por lo general estar´n en un s´lo archivo. float b. ıdo Es importante recordar que todos los objetos en C deber´n estar declarados antes de ser usados. hay una restricci´n en la llamada o o de estas funciones desde otro archivo. } podr´ tener el siguiente prototipo: a float enc_max(float a. Esta definici´n podr´ ser una de a o ıa las siguientes: El lugar en que la funci´n esta definida y llamada en el mismo archivo.. Las a funciones que regresan valores deber´n estar definidos antes de que sean llamados. Cada una de estas secciones podr´n ser implementaddas como una o m´s funciones.

ıa Observar que make es una herramienta del programador. se teclea en forma incompleta.. basicamente se escribe una secuencia de comandos que describe como nuestro o programa (o sistema de programas) ser´ constru´ a partir de los archivos fuentes. fn.6.6.c .. es decir: gcc -o main main. a Cuando se teclea una secuencia larga de compilaci´n en la l´ o ınea de comandos se cometen errores de tecleo. cap´ ıtulos de texto de un libro que esta siendo tipografiado). fi.. fj. Programando Make La programaci´n de make es directa..o.´ ´ CAP´ ITULO 21..c .. Esta utiler´ fue inicialmente desarrollada para UNIX. Sin embargo.c Sin embargo si se sabe que algunos archivos han sido compilados previamente y sus archivos fuente no han sido cambiados desde entonces. entonces se puede ahorrar tiempo de compilaci´n ligando los c´digos objetos de estos o o archivos. COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES 123 21.. y no es parte del lenguaje C o de alguno otro.. No se requiere proporcionar ninguna liga de alguna biblioteca ya que ser´ resuelta a a en la etapa de ligamiento. o Una regla de dependencia tiene dos partes — un lado izquierdo y un lado derecho separados por : lado izquierdo : lado derecho .o . Existir´n varios de nuestros archivos que ser´n ligados al igual que archivos a a de bibliotecas del sistema.c f1. sin embargo: o Se consume tiempo al compilar un m´dulo . La utiler´ Make ıa La utiler´ Make es un manejador inteligente de programas que mantiene la integridad de una colecci´n de m´dulos ıa o o de un programa. Por o o o ejemplo: gcc -c main.. fn.o) para un m´dulo dado. ıcil Si se usa la utiler´ make todo este control es hecho con cuidado. Se tiene el problema en la compilaci´n del programa de ser muy larga... En general s´lo los m´dulos que sean m´s viejos ıa o o a que los archivos fuente ser´n recompilados. Supongamos el siguiente problema de mantenimiento de una colecci´n grande de archivos fuente: o main. Puede ser dif´ recordar la secuencia correcta.c f1. fn.c Normalmente se compilar´n los archivos de la siguiente manera: a gcc -o main main.c . en la pr´ctica o a puede ser cualquier sistema de archivos (por ejemplo.1..c que crear´ un archivo main.c f1.. pero actualmente esta disponible en muchos sistemas.c — si el m´dulo ha sido compilado antes y no ha sido modificado o o el archivo fuente. a 21. o bien. Se puede solamente ligar los archivos objeto..o ... una colecci´n de programas o un sistema completo — no tienen que ser programas. a ıdo La secuencia de construci´n es descrita en los archivos makefile. los cuales contienen reglas de dependencia y o reglas de construcci´n.c Se puede usar la opci´n -c del compilador de C para crear un c´digo objeto (. no ser´ f´cil recordar cuales archivos han sido actualizados. el programa ejecutable final estar´ incorrecto. Su uso principal ha sido en la asistencia del desarrollo de sistemas de software. por lo que si ligamos un archivo a a objeto no actualizado.. por lo tanto no hay necesidad de recompilarlo.

o..h prog.o -lm . Se pueden usar reglas impl´citas en makefile para generalizar reglas y hacer m´s compacta la escritura..o deber´ ser recompilado. Por lo tanto para un programa t´ ıpico de C cuando un archivo make es ejecutado las siguientes tareas son hechas: 1.o. La utiler´ make lo interpretar´ de la siguiente forma: ıa a 1. y cuales archivos cabecera y fuente necesitan ser compilados para crear cada archivo objeto.o f2.o f1.o f2.. mientras el lado derecho da los nombres de los archivos de los cuales depende el destino (por ejemplo. Se muestra a continuaci´n un ejemplo de un archivo make: o prog: prog. si estos han cambiado prog. Si cualquier fuente o archivo cabecera son m´s recientes que el archivo objeto. . a Observar que los archivos make pueden obedecer cualquier comando que sea tecleado en la l´ ınea de comandos. Creaci´n de un Archivo Make (Makefile) o La creaci´n del archivo es bastante simple.o: .´ ´ CAP´ ITULO 21.o. 21.o f1. ejecutar programas si los archivos de datos han sido cambiados o limpieza de directorios.. las reglas de construcci´n siguiendo o las reglas de dependencia son usadas. Lo mismo sucede con a f1. Si cualquiera de los archivos objeto ha cambiado desde la ultima compilaci´n los archivos deben ser religados.c f2.c f1. 2.. Una vez que todos los archivos objetos han sido revisados. 3. Los ultimos 3 comandos en makefile son llamados reglas expl´citas — ya que los archivos en los comandos son ´ ı listados por nombre.c gcc -c f1. prog depende de tres archivos: prog. prog. entonces los a archivos han sido modificados desde la ultima modificaci´n y por lo tanto los archivos objeto necesitan ser ´ o recompilados.o: cabecera.o y f2. f1. ı a Si se tiene: . archivos fuente. por consiguiente se pueden usar los archivos make para no solamente compilar archivos.h f1. El archivo o u Makefile contiene solamente una lista de dependencias de archivos y comandos que son necesarios para satisfacerlos. el tiempo y la fecha de todos los archivos objeto son revisados contra los archivos ejecutables.o depende de 2 archivos.7. Si el destino esta fuera de fecha con respecto a las partes que le constituyen. sino tambi´n para hacer e respaldos. archivos de datos).. La hora y la fecha de cada archivo objeto son revisados contra el c´digo fuente y los archivos cabecera de los o cuales dependen. Si existe archivos ejecutables viejos ser´n recompilados. prog. El archivo make es le´ El Makefile indica cuales objetos y archivos de biblioteca se requieren para ser ligados ıdo.o: cabecera. se crea un archivo de texto usando alg´n editor de textos..o gcc -o prog prog. ´ o 2. archivos cabecera.o y f2.c gcc -c prog. COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES 124 El lado izquierdo da el nombre del destino (los nombres del programa o archivos del sistema) que ser´ constru´ a ıdo (target).

c = . Se emplean macros internas como $@.c .c se puede generalizar a: .o: f1. nombres de archivos objeto.´ ´ CAP´ ITULO 21.ext_destino: comando donde $< es una forma breve para indicar los archivos que tienen la extensi´n .c -ggdb -C -lm main (FUENTES: .o) en donde (FUENTES: .o: f2. en donde todos los caracteres que siguen a # son ignorados. Uso de macros con Make Se pueden definir macros para que sean usadas por make.c f1.o o o Para referirse o usar una macro con make se debe hacer $(nomb_macro).c o Se pueden insertar comentarios en un Makefile usando el s´ ımbolo #.c f2. Existen varias macros internas a continuaci´n se muestran algunas de ellas: o $* Parte del nombre del archivo de la dependencia actual sin el sufijo.ext_fuente. por ejemplo: $(PROGRAMA) : $(OBJETOS) $(LINK.c=main.c gcc -c f1.c de los fuentes por la extensi´n .c f2.C) -o $@ $(OBJETOS) $(LIBS) En el ejemplo mostrado se observa que: La l´ ınea que contiene $(PROGRAMA) : $(OBJETOS) genera una lista de dependencias y el destino. $@ Nombre completo del destino actual. las cuales son tip´ ıcamente usadas para guardar nombres de archivos fuente.c = .o) cambia la extensi´n . Se definen en una forma simple. COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES f1. 21. por ejemplo: FUENTES CFLAGS LIBS PROGRAMA OBJETOS = = = = = main.8.c del destino Un ejemplo de un makefile para el programa modular discutido previamente se muestra a continuaci´n: o # # Makefile # FUENTES. $ Archivo .c gcc -c f2. opciones del compilador o ligas de bibliotecas.c.o: gcc -c $< 125 Lo cual se lee como .c EscribirMiCadena.

c) -o $@ $(OBJETOS) $(SLIBS) clean: rm -f $(PROGRAMA) $(OBJETOS) Para ver m´s informaci´n de esta utiler´ dentro de Linux usar info make a o ıa 126 21.c=.) .c:. El sistema operativo autom´ticamente busca a un archivo con el nombre Makefile (observar que la primera letra es may´scula y el resto min´sculas). a Se puede anular esta b´squeda de este archivo tecleando make -f makefile. Ejecuci´n de Make o Para usar make solamente se deber´ teclear en la l´ a ınea de comandos.9.KEEP_STATE: debug := CFLAGS=-ggdb all debug: $(PROGRAMA) $(PROGRAMA): $(INCLUDES) $(OBJETOS) $(LINK.´ ´ CAP´ ITULO 21. por lo tanto u u si se tiene un archivo con el nombre Makefile y se teclea make en la l´ ınea de comandos. el archivo Makefile del directorio actual ser´ ejecutado.o) # Destino (target) especial (inicia con . . COMPILACION DE PROGRAMAS CON ARCHIVOS MULTIPLES INCLUDES= CFLAGS= SLIBS= PROGRAMA=main OBJETOS=$(FUENTES. u Existen algunas otras opciones para make las cuales pueden ser consultadas usando man.

1. a o En numerosas aplicaciones hay una necesidad clara para que estos procesos se comuniquen para el intercambio de datos o informaci´n de control. o ”w”para escritura. o u Una tuber´ abierta con popen() deber´ siempre cerrarse con pclose(FILE *flujo). Se ha visto ejemplos de lo anterior en la l´ ınea de comandos de Unix cuando se usa el s´ ımbolo |.1. ıa a Se usa fprintf() y fscanf() para comunicarse con el flujo de la tuber´ ıa. Entubando en un programa de C <stdio.1. 127 . Se ver´ como hacer lo anterior en un programa de C teniendo dos (o m´s) procesos divididos. Lo primero que se a a debe hacer es abrir una tuber´ en Unix permite dos formas de hacerlo: ıa. PIPES A continuaci´n se iniciar´ la revisi´n de como m´ltiples procesos pueden estar siendo ejecutados en una m´quina o a o u a y quiz´s siendo controlados (generados por la funci´n fork()) por uno de nuestros programas. Se pueden considerar los siguientes: o e Tuber´ (Pipes) ıa Se˜ales (Signals) n Colas de Mensajes Sem´foros a Memoria Compartida Sockets En este cap´ ıtulo se estudia el entubamiento de dos procesos.h> El entubamiento es un proceso donde la entrada de un proceso es hecha con la salida de otro. char *tipo) Abre una tuber´ de E/S donde el comando es el proceso que ser´ coıa a nectado al proceso que lo esta llamando de esta forma creando el pipe. Hay unos cuantos m´todos para hacer lo anterior. 22. La funci´n popen() regresa un apuntador a un flujo o NULL para alg´n tipo de error. e 22. en los siguientes cap´ ıtulos se revisan los otros m´todos. El tipo es ”r– para lectura.Cap´ ıtulo 22 Comunicaci´n entre procesos (IPC o Interprocess Communication). popen() Tuber´ formateada ıa FILE *popen(char *orden.

dos (o m´s) procesos coopeo a ıa a rativos ser´n creados por divisi´n y los datos ser´n pasados empleando read() y write(). e El modelo de programaci´n est´ndar es que una vez que la tuber´ ha sido puesta.y4. a El listado del c´digo para grafica. la otra graficara y=sen(1/x) e y=sen x */ #include "externals. PIPES128 22.0) y en el otro las funciones y=sen(x) e y=sen(1/x). float y1. La descripci´n o de las funciones de los m´dulos es la siguiente: o El programa tiene dos m´dulos. * La informacion es entubada a gnuplot. una dibujara la * grafica de y=0.1. descf[0] es para lectura y descf[1] es para escritura.y3.5 e y=aleat(0-1.h> #define DEG_TO_RAD(x) (x*180/M_PI) void salir().2.c es el siguiente: o /* grafica. COMUNICACION ENTRE PROCESOS (IPC INTERPROCESS COMMUNICATION). El programa supone que se tiene instalado el programa de graficaci´n gnuplot en el directorio o /usr/bin.dat". Se llama al paquete "gnuplot" * para graficar desde un programa en C.0.c llama al programa gnuplot.´ CAP´ ITULO 22.y2. que apuntan a un nodo i de una tuber´ y los ıa. *fp3.c. ıas Gnuplot genera en “vivo” la salida de las gr´ficas. Dos tuber´ son creadas cada una con un flujo de datos."w")) == NULL) || . grafica.c que o o contiene las rutinas para enviar los datos al programa gnuplot. main() { float i. A continuaci´n se muestra un ejemplo ıas a o de entubamiento que se utiliza para estar enviando datos al programa gnuplot. se crean 2 tuberias. pipe() Tuber´ de bajo nivel ıa int pipe(int descf[2]) Crea un par de descritores de archivo. pone en el vector de dos elementos apuntado por descf.5 e y= random 0-1. Dos flujos son generados usando el programa grafica. *fp2. a o a Las tuber´ abiertas con pipe() deber´n ser cerradas con close(int fd). y en caso de error se devuelve -1 y se pone un valor apropiado en errno.dat".c que contiene la funci´n main() y graficador. /* abrir archivos en los cuales se guardaran los datos a graficar */ if ( ((fp1 = fopen("plot11. pipe() regresa 0 en caso de ´xito.c * Ejemplo de la tuberias (pipe) de Unix. FILE *fp1. El programa grafica.h" #include <signal. *fp4."w")) == NULL) || ((fp2 = fopen("plot12. empleado para graficar. en uno de ellos se gr´fica la funci´n a o y=0.

i.´ CAP´ ITULO 22.i. fclose(fp3). /* Atrapar la llamada de la funcion de salida ctrl-c */ IniciarGraf().i. fflush(fp2). fflush(fp3).salir). y4 = sin(DEG_TO_RAD(i)). usleep(10000). COMUNICACION ENTRE PROCESOS (IPC INTERPROCESS COMMUNICATION). fclose(fp2). if (i == 0.dat". /* cargar archivos */ fprintf(fp1.i. fprintf(fp4. /* graficar alot graph */ Graf1Vez()."%f %f\n". PararGrafica(). srand48(1).y1). printf("Cerrando los archivos de datos\n"). PIPES129 ((fp3 = fopen("plot21. fprintf(fp2.y2).i+=0. /* dormir por un corto tiempo (250 microsegundos) */ } } void salir() { printf("\nctrl-c atrapada:\n Terminando las tuberias\n"). fprintf(fp3.y4)."%f %f\n". } signal(SIGINT. fclose(fp1).0/i)).01) /* incrementar i siempre */ { /* usar ctrl-c para salir del programa */ y2 = (float) drand48().5.y3). else y3 = sin(DEG_TO_RAD(1."w")) == NULL) || ((fp4 = fopen("plot22.dat".."%f %f\n". .0) y3 = 0. fflush(fp4)."%f %f\n". /* asegurarse que los buffers son copiados al disco */ /* para que gnuplot pueda leer los datos */ fflush(fp1). y1 = 0."w")) == NULL) ) { printf("Error no se puede abrir uno o varios archivos de datos\n"). /* poner semilla */ for (i=0.0. exit(1).

} El m´dulo graficador. *ashell. set_term). *plot2.c es el siguiente: o /**********************************************************************/ /* modulo: graficador. fprintf(plot1. fprintf(plot2.dat plot22. ’plot22. *comando2= "/usr/bin/gnuplot> dump2".´ CAP´ ITULO 22. BorrarDat(). printf("Borrando los archivos de datos\n").dat’ with lines. "w"). En este caso se grafica en dos dimensiones */ /**********************************************************************/ #include "externals. COMUNICACION ENTRE PROCESOS (IPC INTERPROCESS COMMUNICATION). fflush(plot1).dat’ with lines\n". ’plot12.h" static FILE *plot1.1] ’plot11. static char *iniciargraf2 = "plot ’plot21.c */ /* Contiene rutinas para graficar un archivo de datos producido por */ /* programa. exit(0). "w"). "w"). static static static static char char char char *comando1= "/usr/bin/gnuplot> dump1". } .dat’ with lines.dat’ with lines\n".dat plot21. "%s". "%s". static char *iniciargraf1 = "plot [] [0:1.dat plot12. if (plot1 == NULL) exit(2). } void BorrarDat(void) { ashell = popen(borrarchivos.dat". *set_term = "set terminal x11\n". void IniciarGraf(void) { plot1 = popen(comando1. plot2 = popen(comando2. fflush(plot2). set_term). if (plot2 == NULL) exit(2). PIPES130 fclose(fp4). *borrarchivos = "rm plot11.

c= grafica.h grafica. PIPES131 void PararGrafica(void) { pclose(plot1). iniciargraf1). fflush(plot2).c gcc -c graficador. } void Graf1Vez(void) { fprintf(plot1. "%s".c graficador. fflush(plot1). pclose(plot2).c .h> /* prototipos */ Con la finalidad de tener un mejor control en la compilaci´n del programa se luede usar el siguiente archivo o Makefile: FUENTES.h */ #ifndef EXTERNALS #define EXTERNALS #include <stdio.c=.h contiene lo siguiente: /* externals.´ CAP´ ITULO 22.c:. iniciargraf2). "%s".h> #include <math. } El archivo de cabecera externals.c graficador.o: externals.h graficador. fprintf(plot2.o: externals.c gcc -c grafica.c INCLUDES= CFLAGS= SLIBS= -lm PROGRAMA= grafica OBJETOS= $(FUENTES. COMUNICACION ENTRE PROCESOS (IPC INTERPROCESS COMMUNICATION).h> #include <stdlib.o) $(PROGRAMA): $(INCLUDES) $(OBJETOS) gcc -o $(PROGRAMA) $(OBJETOS) $(SLIBS) grafica.

PIPES132 clean: rm -f $(PROGRAMA) $(OBJETOS) . COMUNICACION ENTRE PROCESOS (IPC INTERPROCESS COMMUNICATION).´ CAP´ ITULO 22.

El espacio de direcciones del socket entre los sistemas conectados es llamado el dominio de Internet. Existen cinco tipos de sockets. ımites de grabaci´n. Socket de flujo da un flujo de datos de dos v´ confiable. Los sockets de datagrama operan parecidos a pasar cartas hacia adelante y hacia atr´s en a el correo. o El dominio de UNIX da un espacio de direcciones de socket en un sistema. Los sockets se conectan solamente con sockets en el mismo dominio.h>). La comunicaci´n del dominio de Internet usa la suite del protocolo de Internet TCP/IP. Un socket es un punto a a o final de comunicaci´n al cual se puede asociar un nombre. Un socket de dominio es una representaci´n que da una o o estructura de direccionamiento y un conjunto de protocolos. Este tiene un tipo y uno o m´s procesos asociados. Veintitr´s dominios de sockets son identificados (ver <sys/socket. el cual en el dominio o o de Internet usa TCP (Transmission Control Protocol). Socket de paquete secuencial da una conexi´n de dos v´ secuencial y confiable para datagramas de una lono ıas. Los sockets pueden tambi´n ser usados para comunicar procesos entre diferentes e sistemas. ıa en diferente orden de la secuencia de la cual los mensajes fueron env´ ıados. El tipo del socket es SOCK STREAM. Los sockets son muy o ıas. el cual en el dominio de internet usa UDP (User Datagram Protocol). de los cuales solamente los e dominios de UNIX e Internet son normalmente sockets de Linux usados para comunicarse entre procesos en un s´lo o sistema. pero sus caracter´ ısticas exactas dependen de la interfaz dada por el protocolo. No hay protocolo implementado para este tipo a de cualquier familia de protocolos. o a Los sockets existen en los dominios de comunicaci´n. o Los tipos de socket definen las propiedades de comunicaci´n visibles para la aplicaci´n. o Los sockets son usualmente datagramas orientados. Los sockets de dominio de UNIX son nombrados con las rutas de UNIX. El tipo de socket es SOCK SEQPACKET. y sin duplicados sin l´ ıas. como otras formas de comunicaci´n entre procesos. raw socket da acceso a los protocolos de comunicaci´n subyacente. Los procesos se comunican o o solamente entre los sockets del mismo tipo. Socket de datagrama soporta un flujo de mensajes de dos v´ En un socket de datagrama podr´ recibir mensajes ıas. vers´tiles y son un componente b´sico de comunicaci´n entre interprocesos e intersistemas.Cap´ ıtulo 23 Sockets Los sockets proporcionan una comunicaci´n de dos v´ punto a punto entre dos procesos. Los l´ ımites de grabaci´n en los datos o son preservados. El tipo de socket es SOCK DGRAM. El flujo opera o en forma parecida a una conversaci´n telef´nica. gitud fija m´xima. 133 .

socklen t longdir) para enlazar un o camino o una direcci´n de Interne a un conector (socket). bind (sd. int backlog).h> . dependiendo o del dominio del socket. usar: a #include <sys/un. (struct sockaddr_un *) &direccion. int protocolo). int tipo. struct sockaddr *nomb serv. En el dominio de Internet. Un cliente inicia la conexi´n al socket del servidor o a o mediante una llamada a la funci´n int connect(inst s. .CAP´ ITULO 23. Para sockets del dominio de Internet usar: #include <net/netinet. Hay tres formas diferentes de llamar a bind(). ´ Se llama a la funci´n int bind(int sockfd. . una conexi´n esta compuesta usualmente o e o de uno o dos nombres de rutas.. 23. Si la trayectoria de un socket del dominio de UNIX requieres m´s caracteres. connect ( sd. (struct sockaddr *) &direccion.. Conectando sockets de flujo La conexi´n de sockets es usualmente asim´trica. connect ( sd. para crear un extremo de una o comunicaci´n (socket) en el dominio especificado y del tipo indicado. (struc sockaddr_un *)&server.. Los procesos de o comunicaci´n se conectan a trav´s de direcciones..h> . las conexiones deben ser unicas. longitud). (struct sockaddr_in *) &direccion. o menos caracteres se puede usar: #include <sys/socket....1..h> . Un proceso remoto no tiene forma de identificar un socket hasta que una direcci´n se ligada a este. Un proceso usualmente act´a como un servidor y el otro o e u proceso es el cliente. bind (sd. En el dominio de UNIX. Creaci´n y nombrado de sockets o Se llama a la funci´n int socket(int dominio.2. El manejador del socket (un descriptor) es devuelto. y entonces bloquea o el socket. (struc sockaddr_un *)&server. SOCKETS 134 23. int longdirec). el enlazado de nombres crea un socket con nombre en el sistema de archivos.. Para un conetor SOCK STREAM. Si un protocolo no es especificado. longitud). En muchos dominios. bind (sd. El servidor enlaza su socket a un camino o direcci´n previamente acordada. para o indicar cuantas peticiones de conexi´n ser´n puestas en la cola. mientras en el dominio de Internet la llamada podr´ ser: ıa struct sockaddr_in server. long). . una conexi´n esta compuesta de direcciones locales y o remotas y partes locales y remotas.. el sistema o usa uno predefinido que soporte el tipo de socket especificado. Para los sockets del dominio de UNIX con rutas conteniendo 14. Usar unlink() or rm() para eliminar el socket. Una o llamada en el dominio de UNIX es como la siguiente: struct sockaddr_un server. struct sockaddr *nomb. long). En el dominio de UNIX. longitud). el servidor llama a la funci´n int listen(int s.

Un servidor puede tener m´ltiples conexiones SOCK STREAM a o u activas al mismo tiempo. 23.CAP´ ITULO 23. y as´ esta opci´n ı. el servidor llama a la funci´n accept() o para completar la conexi´n. Para un socket SOCK STREAM. o si los pr´ximos datos que se van a recibir son de un tipo diferente del que se ha o o devuelto. void *buf.isfaga la petici´n completamente. struct sockaddr *addr. int flags). la llamada puede aun devolver menos datos de los pedidos si se captura una se˜al. o MSG ERRQUEUE Esta opci´n indica que los errores encolados deben recibirse desde la cola de errores de coo nectores. int *longdirec) regresa un conector nuevo el cual o es v´lido solamente para la conexi´n particular. Estas son write(). size t lon. o La funci´n int accept(int s. SOCKETS 135 Si el socket del cliente no esta enlazado al momento de hacer la llamada para la conexi´n. size t lon. read(). ı. const void *msg.1. int send(int s. El argumento flags de una llamada a recv se forma aplicando el operador de bits O-l´gico a uno o m´s de los o a valores siguientes: o MSG OOB Pide la recepci´n de datos fuera-de-banda que no se recibiran en el flujo de datos normal. el sistema autom´ticao a mente selecciona y liga un nombre al socket. Las funciones send() y recv() son parecidas a read() y write(). El usuario debe proporciona un buffer de tama˜o suficiente. o o a MSG WAITALL Esta opci´n hace que la operaci´n se bloquee hasta que se sat. y int recv(int s. El error se pasa en un mensaje auxiliar con un tipo dependiente del protocolo (para IPv4 ste es IP RECVERR). int flags). o . MSG PEEK Hace que la operacin de recepci´n devuelva datos del principio de la cola de recepci´n sin quitarlos o o de all´ As´ una pr´xima llamada de recepci´n devolver´ los mismos datos. Vea cmsg(3) para obtener ms n informaci´n sobre mensajes auxiliares. Transferencia de datos en un flujo y cerrado Se emplean varias funciones para enviar y recibir datos de un socket SOCK STREAM. o no puede emplearse con tales protocolos. o o o Sin embargo.2. si ocurre un n error o una desconexi´n. pero tienen algunas banderas operacionales adicionales. ı. MSG NOSIGNAL Esta opcin desactiva el que se produzca una se˜al SIGPIPE sobre los conectores orientados a n conexi´n cuando el otro extremo desaparece. Algunos protocolos ponen datos despachados con prontitud en la cabeza de la cola de datos normales.

Sign up to vote on this title
UsefulNot useful